Updated python source

This commit is contained in:
Devine Lu Linvega 2024-04-01 17:17:53 -07:00
parent 77655f3b7f
commit 8b79c3007b
3 changed files with 208 additions and 193 deletions

11
combinators.modal Normal file
View File

@ -0,0 +1,11 @@
define (M ?x) (?x ?x)
define (KI ?x ?y) (?y)
define (T ?x ?y) (?y ?y)
define (W ?x ?y) (?x ?y ?y)
define (K ?x ?y) (?x)
define (C ?x ?y ?z) (?x ?z ?y)
define (B ?x ?y ?z) (?x (?y ?z))
define (I ?x) (?x)
define (S ?x ?y ?z) (?x ?z (?y ?z))

386
modal.py
View File

@ -7,344 +7,341 @@
|is based on the idea of cyclic delimited string rewriting | |is based on the idea of cyclic delimited string rewriting |
|using a central queue and a dictionary of variable bindings.| |using a central queue and a dictionary of variable bindings.|
| | | |
|© 2019 imode, Immediate Mode Technologies | |© 2019-2024 wryl, Paradigital |
*------------------------------------------------------------* *------------------------------------------------------------*
""" """
# `sys` for I/O and arguments. # `sys` for I/O and arguments.
# `time` for measuring the runtime of the interpreter. # `time` for measuring the runtime of the interpreter.
import sys, time; import sys, time
# A decorator intended for measuring the runtime of a given function. # A decorator intended for measuring the runtime of a given function.
def measure(function): def measure(function):
def measurement(*arguments): def measurement(*arguments):
start = time.time(); start = time.time()
result = function(*arguments); result = function(*arguments)
end = time.time(); end = time.time()
milliseconds = (end - start) * 1000.0; milliseconds = (end - start) * 1000.0
print("\nTook {:.3f}ms".format(milliseconds)) print("\nTook {:.3f}ms".format(milliseconds))
return result; return result
return measurement; return measurement
# Enqueue an item by appending it to the queue. # Enqueue an item by appending it to the queue.
def enqueue(queue, item): def enqueue(queue, item):
return queue + item; return queue + item
# Dequeue an item by slicing the queue. The last item is the head. # Dequeue an item by slicing the queue. The last item is the head.
def dequeue(queue, length=1): def dequeue(queue, length=1):
if length > len(queue): if length > len(queue):
return queue; return queue
return queue[length:]; return queue[length:]
# Get the item(s) at the head of the queue. # Get the item(s) at the head of the queue.
def peek(queue, length=1): def peek(queue, length=1):
if length > len(queue): if length > len(queue):
return None; return None
if length == 1: if length == 1:
return queue[0]; return queue[0]
return queue[:length]; return queue[:length]
# Roll/cycle the queue by a certain amount by slicing. # Roll/cycle the queue by a certain amount by slicing.
# This dequeues and enqueues a number of items, "cycling" the queue. # This dequeues and enqueues a number of items, "cycling" the queue.
def roll(queue, length=1): def roll(queue, length=1):
if length > len(queue): if length > len(queue):
return queue; return queue
return queue[length:] + queue[:length]; return queue[length:] + queue[:length]
# Seek to a certain position in the queue by repeatedly rolling it. # Seek to a certain position in the queue by repeatedly rolling it.
def seek(queue, pattern): def seek(queue, pattern):
if pattern not in queue: if pattern not in queue:
return queue; return queue
while peek(queue, len(pattern)) != pattern: while peek(queue, len(pattern)) != pattern:
queue = roll(queue); queue = roll(queue)
return queue; return queue
# Extract a delimited fragment (subtree) from the queue. # Extract a delimited fragment (subtree) from the queue.
def extract(queue): def extract(queue):
results = []; results = []
depth = 0; depth = 0
for element in queue: for element in queue:
if element[0] == "SRT": if element[0] == "SRT":
return []; return []
if element[0] == "INC": if element[0] == "INC":
depth = depth + 1; depth = depth + 1
if element[0] == "DEC": if element[0] == "DEC":
if depth == 0: if depth == 0:
return results; return results
depth = depth - 1; depth = depth - 1
results.append(element); results.append(element)
if depth == 0: if depth == 0:
return results; return results
return results; return results
# Generate a list of variable bindings from the current queue and a pattern. # Generate a list of variable bindings from the current queue and a pattern.
def match(pattern, queue, context=None): def match(pattern, queue, context=None):
if context == None: if context == None:
context = {}; context = {}
if peek(queue) == None: if peek(queue) == None:
return context; return context
for element in pattern: for element in pattern:
if element[0] == "VAR": if element[0] == "VAR":
variable = element[1]; variable = element[1]
value = extract(queue); value = extract(queue)
if variable in context: if variable in context:
if context[variable] != value: if context[variable] != value:
return None; return None
queue = dequeue(queue, len(context[variable])); queue = dequeue(queue, len(context[variable]))
else: else:
if len(value) == 0: if len(value) == 0:
return None; return None
context[variable] = value; context[variable] = value
queue = dequeue(queue, len(context[variable])); queue = dequeue(queue, len(context[variable]))
elif element != peek(queue): elif element != peek(queue):
return None; return None
else: else:
queue = dequeue(queue); queue = dequeue(queue)
return context; return context
# Fill in a pattern with variables in it using a list of variable bindings. # Fill in a pattern with variables in it using a list of variable bindings.
def construct(pattern, context): def construct(pattern, context):
results = []; results = []
for element in pattern: for element in pattern:
if element[0] == "VAR": if element[0] == "VAR":
if element[1] in context: if element[1] in context:
for element in context[element[1]]: for element in context[element[1]]:
results.append(element); results.append(element)
else: else:
results.append(element); results.append(element)
else: else:
results.append(element); results.append(element)
return results; return results
# Apply a pattern/replacement rule to the queue. # Apply a pattern/replacement rule to the queue.
def apply(queue, rules, pattern, replacement): def apply(queue, rules, pattern, replacement):
context = match(pattern, queue); context = match(pattern, queue)
if context == None: if context == None:
return (False, roll(queue)); return (False, roll(queue))
pattern = construct(pattern, context); pattern = construct(pattern, context)
if not pattern: if not pattern:
return (False, roll(queue)); return (False, roll(queue))
replacement = construct(replacement, context); replacement = construct(replacement, context)
if len(replacement) == 0:
return (True, dequeue(queue, len(pattern)))
if not replacement: if not replacement:
return (True, roll(queue)); return (False, roll(queue))
return (True, enqueue(dequeue(queue, len(pattern)), replacement)); return (True, enqueue(dequeue(queue, len(pattern)), replacement))
def define(queue, rules, pattern): def define(queue, rules, pattern):
context = match(pattern, queue); context = match(pattern, queue)
left = context["left"]; left = context["left"]
right = context["right"]; right = context["right"]
if right and left: if right and left:
if len(left) > 1: if len(left) > 1:
left = left[1:][:-1]; left = left[1:][:-1]
if len(right) > 1: if len(right) > 1:
right = right[1:][:-1]; right = right[1:][:-1]
rules.append((left, apply, [right])); rules.append((left, apply, [right]))
return (True, dequeue(queue, len(construct(pattern, context)))); return (True, dequeue(queue, len(construct(pattern, context))))
return (False, roll(queue)); return (False, roll(queue))
def undefine(queue, rules, pattern): def undefine(queue, rules, pattern):
context = match(pattern, queue); context = match(pattern, queue)
left = context["left"]; left = context["left"]
if left: if left:
if len(left) > 1: if len(left) > 1:
left = left[1:][:-1]; left = left[1:][:-1]
for rule, index in zip(rules, range(0, len(rules))): for rule, index in zip(rules, range(0, len(rules))):
candidate , _, _ = rule; candidate , _, _ = rule
if candidate == left: if candidate == left:
del rules[index]; del rules[index]
return (True, dequeue(queue, len(construct(pattern, context)))); return (True, dequeue(queue, len(construct(pattern, context))))
return (False, roll(queue)); return (False, roll(queue))
def add(queue, rules, pattern): def add(queue, rules, pattern):
context = match(pattern, queue); context = match(pattern, queue)
left = context["left"]; left = context["left"]
right = context["right"]; right = context["right"]
if left and right: if left and right:
if left[0][0] == "NUM" and right[0][0] == "NUM": if left[0][0] == "NUM" and right[0][0] == "NUM":
queue = dequeue(queue, len(construct(pattern, context))); queue = dequeue(queue, len(construct(pattern, context)))
queue = enqueue(queue, parse(str(left[0][1] + right[0][1]))); queue = enqueue(queue, parse(str(left[0][1] + right[0][1])))
return (True, queue); return (True, queue)
return (False, roll(queue)); return (False, roll(queue))
def subtract(queue, rules, pattern): def subtract(queue, rules, pattern):
context = match(pattern, queue); context = match(pattern, queue)
left = context["left"]; left = context["left"]
right = context["right"]; right = context["right"]
if left and right: if left and right:
if left[0][0] == "NUM" and right[0][0] == "NUM": if left[0][0] == "NUM" and right[0][0] == "NUM":
queue = dequeue(queue, len(construct(pattern, context))); queue = dequeue(queue, len(construct(pattern, context)))
queue = enqueue(queue, parse(str(left[0][1] - right[0][1]))); queue = enqueue(queue, parse(str(left[0][1] - right[0][1])))
return (True, queue); return (True, queue)
return (False, roll(queue)); return (False, roll(queue))
def multiply(queue, rules, pattern): def multiply(queue, rules, pattern):
context = match(pattern, queue); context = match(pattern, queue)
left = context["left"]; left = context["left"]
right = context["right"]; right = context["right"]
if left and right: if left and right:
if left[0][0] == "NUM" and right[0][0] == "NUM": if left[0][0] == "NUM" and right[0][0] == "NUM":
queue = dequeue(queue, len(construct(pattern, context))); queue = dequeue(queue, len(construct(pattern, context)))
queue = enqueue(queue, parse(str(left[0][1] * right[0][1]))); queue = enqueue(queue, parse(str(left[0][1] * right[0][1])))
return (True, queue); return (True, queue)
return (False, roll(queue)); return (False, roll(queue))
def divide(queue, rules, pattern): def divide(queue, rules, pattern):
context = match(pattern, queue); context = match(pattern, queue)
left = context["left"]; left = context["left"]
right = context["right"]; right = context["right"]
if left and right: if left and right:
if left[0][0] == "NUM" and right[0][0] == "NUM": if left[0][0] == "NUM" and right[0][0] == "NUM":
queue = dequeue(queue, len(construct(pattern, context))); queue = dequeue(queue, len(construct(pattern, context)))
queue = enqueue(queue, parse(str(left[0][1] // right[0][1]))); queue = enqueue(queue, parse(str(left[0][1] // right[0][1])))
return (True, queue); return (True, queue)
return (False, roll(queue)); return (False, roll(queue))
def modulo(queue, rules, pattern): def modulo(queue, rules, pattern):
context = match(pattern, queue); context = match(pattern, queue)
left = context["left"]; left = context["left"]
right = context["right"]; right = context["right"]
if left and right: if left and right:
if left[0][0] == "NUM" and right[0][0] == "NUM": if left[0][0] == "NUM" and right[0][0] == "NUM":
queue = dequeue(queue, len(construct(pattern, context))); queue = dequeue(queue, len(construct(pattern, context)))
queue = enqueue(queue, parse(str(left[0][1] % right[0][1]))); queue = enqueue(queue, parse(str(left[0][1] % right[0][1])))
return (True, queue); return (True, queue)
return (False, roll(queue)); return (False, roll(queue))
def display(queue, rules, pattern): def display(queue, rules, pattern):
context = match(pattern, queue); context = match(pattern, queue)
left = context["left"]; left = context["left"]
if left: if left:
if left[0][0] == "LIT" or left[0][0] == "NUM": if left[0][0] == "LIT" or left[0][0] == "NUM":
if left[0][1] == "space": if left[0][1] == "space":
print(' ', end=""); print(' ', end="")
elif left[0][1] == "newline": elif left[0][1] == "newline":
print('\n', end=""); print('\n', end="")
elif left[0][1] == "tab": elif left[0][1] == "tab":
print('\t', end=""); print('\t', end="")
else: else:
print(left[0][1], end=""); print(left[0][1], end="")
return (True, dequeue(queue, len(construct(pattern, context)))); return (True, dequeue(queue, len(construct(pattern, context))))
return (False, roll(queue)); return (False, roll(queue))
def applicable(rules, queue): def applicable(rules, queue):
results = []; results = []
for pattern, operation, parameters in rules: for pattern, operation, parameters in rules:
if match(pattern, queue) != None: if match(pattern, queue) != None:
results.append((pattern, operation, parameters)); results.append((pattern, operation, parameters))
return results; return results
def pick(list): def pick(list):
if len(list) == 0: if len(list) == 0:
return None; return None
return list[0]; return list[0]
def reconstruct(pattern): def reconstruct(pattern):
for element in pattern: for element in pattern:
if element[0] == "INC": if element[0] == "INC":
yield '('; yield '('
elif element[0] == "DEC": elif element[0] == "DEC":
yield ')'; yield ')'
elif element[0] == "NUM": elif element[0] == "NUM":
yield str(element[1]); yield str(element[1])
elif element[0] == "LIT": elif element[0] == "LIT":
yield element[1]; yield element[1]
elif element[0] == "VAR": elif element[0] == "VAR":
yield '?' + element[1]; yield '?' + element[1]
yield ' '; yield ' '
def number(string): def number(string):
try: try:
result = int(string); result = int(string)
return True; return True
except: except:
return False; return False
def literal(string, index=0): def literal(string, index=0):
token = ""; token = ""
while index != len(string): while index != len(string):
character = string[index]; character = string[index]
if character in ['(', ')', '{', '}', '[', ']', '?', ' ', '\t', '\n', '\r']: if character in ['(', ')', '{', '}', '[', ']', '?', ' ', '\t', '\n', '\r']:
break; break
else: else:
token = token + string[index]; token = token + string[index]
index = index + 1; index = index + 1
return (token, index); return (token, index)
def parse(string, index=0): def parse(string, index=0):
results = []; results = []
while index != len(string): while index != len(string):
character = string[index]; character = string[index]
if character in ['(', ')', '{', '}', '[', ']', '?', ' ', '\t', '\n', '\r']: if character in ['(', ')', '{', '}', '[', ']', '?', ' ', '\t', '\n', '\r']:
index = index + 1; index = index + 1
if character in ['(', '{', '[']: if character in ['(', '{', '[']:
results.append(["INC"]); results.append(["INC"])
elif character in [')', '}', ']']: elif character in [')', '}', ']']:
results.append(["DEC"]); results.append(["DEC"])
elif character not in [' ', '\t', '\n', '\r']: elif character not in [' ', '\t', '\n', '\r']:
token, index = literal(string, index); token, index = literal(string, index)
if character == '?': if character == '?':
results.append(["VAR", token]); results.append(["VAR", token])
elif number(token): elif number(token):
results.append(["NUM", int(token)]); results.append(["NUM", int(token)])
else: else:
results.append(["LIT", token]); results.append(["LIT", token])
return results; return results
@measure @measure
def run(rules, queue, limit=pow(2, 32)): def run(rules, queue, limit=pow(2, 32)):
steps = 0; steps = 0
failures = 0; failures = 0
queue = [["SRT"]] + queue; queue = [["SRT"]] + queue
while failures != len(queue) and steps != limit: while failures != len(queue) and steps != limit:
rule = pick(applicable(rules, queue)); rule = pick(applicable(rules, queue))
if rule == None: if rule == None:
queue = roll(queue); queue = roll(queue)
failures = failures + 1; failures = failures + 1
print("<>: ", inspect(queue))
else: else:
pattern, operation, parameters = rule; pattern, operation, parameters = rule
result, queue = operation(queue, rules, pattern, *parameters); result, queue = operation(queue, rules, pattern, *parameters)
if result == True: if result == True:
failures = 0; failures = 0
#print("<>: ", inspect(seek(queue, ["SRT"])), ":::", inspect(pattern)) #print("<>: ", inspect(seek(queue, ["SRT"])))
print("<>: ", inspect(queue), ":::", inspect(pattern)) #print("<>: ", inspect(queue))
#input() #input()
steps = steps + 1; steps = steps + 1
if steps == limit: if steps == limit:
print("Execution limit reached."); print("Execution limit reached.")
return queue; return queue
def read(file): def read(file):
try: try:
with open(file) as file: with open(file) as file:
content = file.read(); content = file.read()
return content; return content
except EnvironmentError: except EnvironmentError:
return None; return None
def inspect(pattern): def inspect(pattern):
return "".join(reconstruct(pattern)); return "".join(reconstruct(pattern))
def usage(name): def usage(name):
print(name + ':', "a programming language based on rewriting."); print(name + ':', "a programming language based on rewriting.")
print("Usage:"); print("Usage:")
print((' ' * 4) + name, "<file>"); print((' ' * 4) + name, "<file>")
def prompt(prompt): def prompt(prompt):
try: try:
return input(prompt); return input(prompt)
except: except:
return None; return None
def help(): def help():
prefix = ' ' * 4; prefix = ' ' * 4
print("Commands:"); print("Commands:")
print(prefix + "rules : Display the current ruleset."); print(prefix + "rules : Display the current ruleset.")
print(prefix + "clear : Clear the current ruleset."); print(prefix + "clear : Clear the current ruleset.")
print(prefix + "help : This message."); print(prefix + "help : This message.")
print(prefix + "quit : Quit."); print(prefix + "quit : Quit.")
def main(): def main():
defaults = [ defaults = [
@ -385,37 +382,40 @@ def main():
(parse("?left / ?right"), divide, []), (parse("?left / ?right"), divide, []),
(parse("?left % ?right"), modulo, []), (parse("?left % ?right"), modulo, []),
(parse("display ?left"), display, []), (parse("display ?left"), display, []),
]; ]
rules = defaults; rules = defaults.copy()
if len(sys.argv) >= 2: if len(sys.argv) >= 2:
content = read(sys.argv[1]); content = read(sys.argv[1])
if content == None: if content == None:
print("No such file."); print("No such file.")
return; return
print("Initializating..."); print("Initializating...")
run(rules, parse(content)); run(rules, parse(content))
print("Modal v0.02"); else:
help(); usage(sys.argv[0])
return
print("Modal v0.02")
help()
while True: while True:
input = prompt("::> "); input = prompt("::> ")
if input == None: if input == None:
break; break
elif parse(input) == parse("rules"): elif parse(input) == parse("rules"):
print("Rules:"); print("Rules:")
prefix = ' ' * 4; prefix = ' ' * 4
for pattern, operation, parameters in rules: for pattern, operation, parameters in rules:
if operation == apply: if operation == apply:
print(prefix + inspect(pattern) + "-> " + inspect(parameters[0])); print(prefix + inspect(pattern) + "-> " + inspect(parameters[0]))
elif parse(input) == parse("clear"): elif parse(input) == parse("clear"):
rules = defaults; rules = defaults
elif parse(input) == parse("help"): elif parse(input) == parse("help"):
help(); help()
elif parse(input) == parse("quit") or parse(input) == parse("exit"): elif parse(input) == parse("quit") or parse(input) == parse("exit"):
break; break
else: else:
print("Reducing..."); print("Reducing...")
print(inspect(seek(run(rules, parse(input)), ["SRT"]))); print(inspect(seek(run(rules, parse(input)), ["SRT"])))
return; return
if __name__ == "__main__": if __name__ == "__main__":
main(); main()

4
test.modal Normal file
View File

@ -0,0 +1,4 @@
define nil ((0))
define (cons ?x (?y)) ((?x ?y))
define (car (?x)) ?x