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