Merge remote-tracking branch 'upstream/master' into d6/binary
This commit is contained in:
commit
e073d7c884
|
@ -16,6 +16,7 @@ cc src/modal.c -o bin/modal
|
||||||
bin/modal examples/hello.modal
|
bin/modal examples/hello.modal
|
||||||
-v Print version
|
-v Print version
|
||||||
-q Quiet mode, no step printing
|
-q Quiet mode, no step printing
|
||||||
|
-p Print summary with refs count
|
||||||
-n Infinite mode, no rewrites limit
|
-n Infinite mode, no rewrites limit
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
<> (`?: ?0 ?1) ((Int ?:))
|
||||||
|
<> (fib ?n) (?n (Int 0) (Int 1) Fib)
|
||||||
|
<> ((Int 0) (Int ?0) (Int ?1) Fib) ?0
|
||||||
|
<> ((Int ?x) (Int ?y) (Int ?z) Fib) (`- ?x 1 (Int ?z) `+ ?z ?y Fib)
|
||||||
|
|
||||||
|
fib (Int 11)
|
|
@ -0,0 +1,9 @@
|
||||||
|
<> (fizzbuzz ?x ?x) (done.)
|
||||||
|
<> (?x Int 0 Int 0 test) (?(?: ?:) (Fizz Buzz\n))
|
||||||
|
<> (?x Int 0 Int ?z test) (?(?: ?:) Fizz\n)
|
||||||
|
<> (?x Int ?y Int 0 test) (?(?: ?:) Buzz\n)
|
||||||
|
<> (?x Int ?y Int ?z test) (?(?: ?:) (?x \n))
|
||||||
|
<> (?x eval) (?x ?((?0 ?1 ?:) (Int ?:)) ?x 3 % ?((?0 ?1 ?:) (Int ?:)) ?x 5 % test)
|
||||||
|
<> (fizzbuzz ?x ?y) (?x eval fizzbuzz ?((?0 ?1 ?:) ?:) ?x 1 + ?y)
|
||||||
|
|
||||||
|
fizzbuzz 1 30
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
<> (what is ?x) (is ?x a (programming language))
|
<> (what is ?x) (is ?x a (programming language))
|
||||||
<> (is ?x a ?y) (or, is ?x (a virtual machine))
|
<> (is ?x a ?y) (or, is ?x (a virtual machine))
|
||||||
<> (or, is ?x ?y) (?x is ?y <> ?y (a meta-language))
|
<> (or, is ?x ?y) (?x is ?y <> ?y (a meta-language))
|
||||||
|
@ -6,4 +5,3 @@
|
||||||
<> (?x is ?x) (?(?: ?:) ?x)
|
<> (?x is ?x) (?(?: ?:) ?x)
|
||||||
|
|
||||||
what is modal
|
what is modal
|
||||||
|
|
||||||
|
|
|
@ -8,8 +8,8 @@
|
||||||
|
|
||||||
?(?-) (Formatter)
|
?(?-) (Formatter)
|
||||||
|
|
||||||
?((?x ?y) one) aaa(bbb) = one (formatter 1) test
|
?((?x ?y) two) aaa(bbb) = two (formatter 1) test
|
||||||
?((?x ?y) one) (bbb)aaa = one (formatter 2) test
|
?((?x ?y) two) (bbb)aaa = two (formatter 2) test
|
||||||
(a b c ) = (a b c) (formatter 3) test
|
(a b c ) = (a b c) (formatter 3) test
|
||||||
( a b c) = (a b c) (formatter 4) test
|
( a b c) = (a b c) (formatter 4) test
|
||||||
( a b c ) = (a b c) (formatter 5) test
|
( a b c ) = (a b c) (formatter 5) test
|
||||||
|
@ -86,6 +86,19 @@ abc ?(?x) def = abc (lambda 2) test
|
||||||
|
|
||||||
reverse (modal) = ladom (reverse 1) test
|
reverse (modal) = ladom (reverse 1) test
|
||||||
|
|
||||||
|
?(?-) (Incomplete definitions)
|
||||||
|
|
||||||
|
<> <>
|
||||||
|
<> () ()
|
||||||
|
<> (incomplete-basic)
|
||||||
|
<> (incomplete-reg ?x)
|
||||||
|
<> (waste-rule) *
|
||||||
|
|
||||||
|
(incomplete-basic) = () (incomplete 1) test
|
||||||
|
(incomplete-reg abcdef) = () (incomplete 2) test
|
||||||
|
(?(?x) incomplete-lambda) = () (incomplete 3) test
|
||||||
|
(?() abc) = (abc) (incomplete 4) test
|
||||||
|
|
||||||
?(?-) (Inline rules)
|
?(?-) (Inline rules)
|
||||||
|
|
||||||
<> ((?x -> ?y)) (<> ?x ?y)
|
<> ((?x -> ?y)) (<> ?x ?y)
|
||||||
|
@ -93,6 +106,36 @@ reverse (modal) = ladom (reverse 1) test
|
||||||
|
|
||||||
nap tap (inline 1) test
|
nap tap (inline 1) test
|
||||||
|
|
||||||
|
?(?-) (Undefinition)
|
||||||
|
|
||||||
|
<> (undefine-me) (abc)
|
||||||
|
<> (undefine-me) (def)
|
||||||
|
<> (undefine-me) (ghi)
|
||||||
|
>< (undefine-me)
|
||||||
|
|
||||||
|
(undefine-me) = (def) (undefinition 1) test
|
||||||
|
|
||||||
|
>< (undefine-me)
|
||||||
|
|
||||||
|
(undefine-me) = (ghi) (undefinition 2) test
|
||||||
|
|
||||||
|
>< (undefine-unknown)
|
||||||
|
|
||||||
|
?(* (>< (undefine-me))) *
|
||||||
|
|
||||||
|
(undefine-me) = (undefine-me) (undefinition 3) test
|
||||||
|
|
||||||
|
?(?-) (Arithmetic)
|
||||||
|
|
||||||
|
?((?: ?0 ?1 ?2) ?:) + 1 2 3 = 6 (Arithmetic 1) test
|
||||||
|
?((?0 ?: ?1) ?:) 16 - 8 = 8 (Arithmetic 2) test
|
||||||
|
?((?0 ?1 ?:) ?:) 12 10 * = 120 (Arithmetic 3) test
|
||||||
|
|
||||||
|
<> (?0 ?1 `?:) (?:)
|
||||||
|
|
||||||
|
(12 45 `+ -2 `+) = (55) (Arithmetic 4) test
|
||||||
|
|
||||||
?(?-) (Late Test Primitives)
|
?(?-) (Late Test Primitives)
|
||||||
|
|
||||||
<> (?x = ?y ?n test) (?(?: ?:) (#fail ?n found: ?x expect: ?y\n))
|
<> (?x = ?y ?n test) (?(?: ?:) (#fail ?n found: ?x expect: ?y\n))
|
||||||
|
|
||||||
|
|
2
makefile
2
makefile
|
@ -1,5 +1,5 @@
|
||||||
RELEASE_flags=-DNDEBUG -O2 -g0 -s
|
RELEASE_flags=-DNDEBUG -O2 -g0 -s
|
||||||
DEBUG_flags=-std=c89 -D_POSIX_C_SOURCE=199309L -DDEBUG -Wall -Wno-unknown-pragmas -Wpedantic -Wshadow -Wextra -Werror=implicit-int -Werror=incompatible-pointer-types -Werror=int-conversion -Wvla -g -Og -fsanitize=address -fsanitize=undefined
|
DEBUG_flags=-std=c99 -D_POSIX_C_SOURCE=199309L -DDEBUG -Wall -Wno-unknown-pragmas -Wpedantic -Wshadow -Wextra -Werror=implicit-int -Werror=incompatible-pointer-types -Werror=int-conversion -Wvla -g -Og -fsanitize=address -fsanitize=undefined
|
||||||
|
|
||||||
.PHONY: all debug dest run debug test install uninstall format clean archive
|
.PHONY: all debug dest run debug test install uninstall format clean archive
|
||||||
|
|
||||||
|
|
419
old/modal.py
419
old/modal.py
|
@ -1,419 +0,0 @@
|
||||||
"""
|
|
||||||
*------------------------------------------------------------*
|
|
||||||
|modal.py : An Implementation of Modal in Python |
|
|
||||||
| |
|
|
||||||
|This is an implementation of Modal, a programming language |
|
|
||||||
|based on term rewriting. This particular implementation |
|
|
||||||
|is based on the idea of cyclic delimited string rewriting |
|
|
||||||
|using a central queue and a dictionary of variable bindings.|
|
|
||||||
| |
|
|
||||||
|© 2019-2024 wryl, Paradigital |
|
|
||||||
*------------------------------------------------------------*
|
|
||||||
"""
|
|
||||||
|
|
||||||
# `sys` for I/O and arguments.
|
|
||||||
# `time` for measuring the runtime of the interpreter.
|
|
||||||
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
|
|
||||||
print("\nTook {:.3f}ms".format(milliseconds))
|
|
||||||
return result
|
|
||||||
return measurement
|
|
||||||
|
|
||||||
# Enqueue an item by appending it to the queue.
|
|
||||||
def enqueue(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:]
|
|
||||||
|
|
||||||
# Get the item(s) at the head of the queue.
|
|
||||||
def peek(queue, length=1):
|
|
||||||
if length > len(queue):
|
|
||||||
return None
|
|
||||||
if length == 1:
|
|
||||||
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]
|
|
||||||
|
|
||||||
# Seek to a certain position in the queue by repeatedly rolling it.
|
|
||||||
def seek(queue, pattern):
|
|
||||||
if pattern not in queue:
|
|
||||||
return queue
|
|
||||||
while peek(queue, len(pattern)) != pattern:
|
|
||||||
queue = roll(queue)
|
|
||||||
return queue
|
|
||||||
|
|
||||||
# Extract a delimited fragment (subtree) from the queue.
|
|
||||||
def extract(queue):
|
|
||||||
results = []
|
|
||||||
depth = 0
|
|
||||||
for element in queue:
|
|
||||||
if element[0] == "SRT":
|
|
||||||
return []
|
|
||||||
if element[0] == "INC":
|
|
||||||
depth = depth + 1
|
|
||||||
if element[0] == "DEC":
|
|
||||||
if depth == 0:
|
|
||||||
return results
|
|
||||||
depth = depth - 1
|
|
||||||
results.append(element)
|
|
||||||
if depth == 0:
|
|
||||||
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 = {}
|
|
||||||
if peek(queue) == None:
|
|
||||||
return context
|
|
||||||
for element in pattern:
|
|
||||||
if element[0] == "VAR":
|
|
||||||
variable = element[1]
|
|
||||||
value = extract(queue)
|
|
||||||
if variable in context:
|
|
||||||
if context[variable] != value:
|
|
||||||
return None
|
|
||||||
queue = dequeue(queue, len(context[variable]))
|
|
||||||
else:
|
|
||||||
if len(value) == 0:
|
|
||||||
return None
|
|
||||||
context[variable] = value
|
|
||||||
queue = dequeue(queue, len(context[variable]))
|
|
||||||
elif element != peek(queue):
|
|
||||||
return None
|
|
||||||
else:
|
|
||||||
queue = dequeue(queue)
|
|
||||||
return context
|
|
||||||
|
|
||||||
# Fill in a pattern with variables in it using a list of variable bindings.
|
|
||||||
def construct(pattern, context):
|
|
||||||
results = []
|
|
||||||
for element in pattern:
|
|
||||||
if element[0] == "VAR":
|
|
||||||
if element[1] in context:
|
|
||||||
for element in context[element[1]]:
|
|
||||||
results.append(element)
|
|
||||||
else:
|
|
||||||
results.append(element)
|
|
||||||
else:
|
|
||||||
results.append(element)
|
|
||||||
return results
|
|
||||||
|
|
||||||
# Apply a pattern/replacement rule to the queue.
|
|
||||||
def apply(queue, rules, pattern, replacement):
|
|
||||||
context = match(pattern, queue)
|
|
||||||
if context == None:
|
|
||||||
return (False, roll(queue))
|
|
||||||
pattern = construct(pattern, context)
|
|
||||||
if not pattern:
|
|
||||||
return (False, roll(queue))
|
|
||||||
replacement = construct(replacement, context)
|
|
||||||
return (True, enqueue(dequeue(queue, len(pattern)), replacement))
|
|
||||||
|
|
||||||
def define(queue, rules, pattern):
|
|
||||||
context = match(pattern, queue)
|
|
||||||
left = context["left"]
|
|
||||||
right = context["right"]
|
|
||||||
if right and left:
|
|
||||||
if len(left) > 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))
|
|
||||||
|
|
||||||
def undefine(queue, rules, pattern):
|
|
||||||
context = match(pattern, queue)
|
|
||||||
left = context["left"]
|
|
||||||
if left:
|
|
||||||
if len(left) > 1:
|
|
||||||
left = left[1:][:-1]
|
|
||||||
for rule, index in zip(rules, range(0, len(rules))):
|
|
||||||
candidate , _, _ = rule
|
|
||||||
if candidate == left:
|
|
||||||
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"]
|
|
||||||
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))
|
|
||||||
|
|
||||||
def subtract(queue, rules, pattern):
|
|
||||||
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))
|
|
||||||
|
|
||||||
def multiply(queue, rules, pattern):
|
|
||||||
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))
|
|
||||||
|
|
||||||
def divide(queue, rules, pattern):
|
|
||||||
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))
|
|
||||||
|
|
||||||
def modulo(queue, rules, pattern):
|
|
||||||
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))
|
|
||||||
|
|
||||||
def display(queue, rules, pattern):
|
|
||||||
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="")
|
|
||||||
elif left[0][1] == "newline":
|
|
||||||
print('\n', end="")
|
|
||||||
elif left[0][1] == "tab":
|
|
||||||
print('\t', end="")
|
|
||||||
else:
|
|
||||||
print(left[0][1], end="")
|
|
||||||
return (True, dequeue(queue, len(construct(pattern, context))))
|
|
||||||
return (False, roll(queue))
|
|
||||||
|
|
||||||
def applicable(rules, queue):
|
|
||||||
results = []
|
|
||||||
for pattern, operation, parameters in rules:
|
|
||||||
if match(pattern, queue) != None:
|
|
||||||
results.append((pattern, operation, parameters))
|
|
||||||
return results
|
|
||||||
|
|
||||||
def pick(list):
|
|
||||||
if len(list) == 0:
|
|
||||||
return None
|
|
||||||
return list[0]
|
|
||||||
|
|
||||||
def reconstruct(pattern):
|
|
||||||
for element in pattern:
|
|
||||||
if element[0] == "INC":
|
|
||||||
yield '('
|
|
||||||
elif element[0] == "DEC":
|
|
||||||
yield ')'
|
|
||||||
elif element[0] == "NUM":
|
|
||||||
yield str(element[1])
|
|
||||||
elif element[0] == "LIT":
|
|
||||||
yield element[1]
|
|
||||||
elif element[0] == "VAR":
|
|
||||||
yield '?' + element[1]
|
|
||||||
yield ' '
|
|
||||||
|
|
||||||
def number(string):
|
|
||||||
try:
|
|
||||||
result = int(string)
|
|
||||||
return True
|
|
||||||
except:
|
|
||||||
return False
|
|
||||||
|
|
||||||
def literal(string, index=0):
|
|
||||||
token = ""
|
|
||||||
while index != len(string):
|
|
||||||
character = string[index]
|
|
||||||
if character in ['(', ')', '{', '}', '[', ']', '?', ' ', '\t', '\n', '\r']:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
token = token + string[index]
|
|
||||||
index = index + 1
|
|
||||||
return (token, index)
|
|
||||||
|
|
||||||
def parse(string, index=0):
|
|
||||||
results = []
|
|
||||||
while index != len(string):
|
|
||||||
character = string[index]
|
|
||||||
if character in ['(', ')', '{', '}', '[', ']', '?', ' ', '\t', '\n', '\r']:
|
|
||||||
index = index + 1
|
|
||||||
if character in ['(', '{', '[']:
|
|
||||||
results.append(["INC"])
|
|
||||||
elif character in [')', '}', ']']:
|
|
||||||
results.append(["DEC"])
|
|
||||||
elif character not in [' ', '\t', '\n', '\r']:
|
|
||||||
token, index = literal(string, index)
|
|
||||||
if character == '?':
|
|
||||||
results.append(["VAR", token])
|
|
||||||
elif number(token):
|
|
||||||
results.append(["NUM", int(token)])
|
|
||||||
else:
|
|
||||||
results.append(["LIT", token])
|
|
||||||
return results
|
|
||||||
|
|
||||||
@measure
|
|
||||||
def run(rules, queue, limit=pow(2, 32)):
|
|
||||||
steps = 0
|
|
||||||
failures = 0
|
|
||||||
queue = [["SRT"]] + queue
|
|
||||||
while failures != len(queue) and steps != limit:
|
|
||||||
rule = pick(applicable(rules, queue))
|
|
||||||
if rule == None:
|
|
||||||
queue = roll(queue)
|
|
||||||
failures = failures + 1
|
|
||||||
else:
|
|
||||||
pattern, operation, parameters = rule
|
|
||||||
result, queue = operation(queue, rules, pattern, *parameters)
|
|
||||||
if result == True:
|
|
||||||
failures = 0
|
|
||||||
print("<>: ", inspect(seek(queue, ["SRT"])))
|
|
||||||
#print("<>: ", inspect(queue))
|
|
||||||
#input()
|
|
||||||
steps = steps + 1
|
|
||||||
if steps == limit:
|
|
||||||
print("Execution limit reached.")
|
|
||||||
return queue
|
|
||||||
|
|
||||||
def read(file):
|
|
||||||
try:
|
|
||||||
with open(file) as file:
|
|
||||||
content = file.read()
|
|
||||||
return content
|
|
||||||
except EnvironmentError:
|
|
||||||
return None
|
|
||||||
|
|
||||||
def inspect(pattern):
|
|
||||||
return "".join(reconstruct(pattern))
|
|
||||||
|
|
||||||
def usage(name):
|
|
||||||
print(name + ':', "a programming language based on rewriting.")
|
|
||||||
print("Usage:")
|
|
||||||
print((' ' * 4) + name, "<file>")
|
|
||||||
|
|
||||||
def prompt(prompt):
|
|
||||||
try:
|
|
||||||
return input(prompt)
|
|
||||||
except:
|
|
||||||
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.")
|
|
||||||
|
|
||||||
def main():
|
|
||||||
defaults = [
|
|
||||||
(parse("define ?left ?right"), define, []),
|
|
||||||
(parse("undefine ?left"), undefine, []),
|
|
||||||
(parse("add (?left) (?right)"), add, []),
|
|
||||||
(parse("add (?left) ?right"), add, []),
|
|
||||||
(parse("add ?left (?right)"), add, []),
|
|
||||||
(parse("add ?left ?right"), add, []),
|
|
||||||
(parse("subtract (?left) (?right)"), subtract, []),
|
|
||||||
(parse("subtract (?left) ?right"), subtract, []),
|
|
||||||
(parse("subtract ?left (?right)"), subtract, []),
|
|
||||||
(parse("subtract ?left ?right"), subtract, []),
|
|
||||||
(parse("multiply (?left) (?right)"), multiply, []),
|
|
||||||
(parse("multiply (?left) ?right"), multiply, []),
|
|
||||||
(parse("multiply ?left (?right)"), multiply, []),
|
|
||||||
(parse("multiply ?left ?right"), multiply, []),
|
|
||||||
(parse("divide (?left) (?right)"), divide, []),
|
|
||||||
(parse("divide (?left) ?right"), divide, []),
|
|
||||||
(parse("divide ?left (?right)"), divide, []),
|
|
||||||
(parse("divide ?left ?right"), divide, []),
|
|
||||||
(parse("modulo (?left) (?right)"), modulo, []),
|
|
||||||
(parse("modulo (?left) ?right"), modulo, []),
|
|
||||||
(parse("modulo ?left (?right)"), modulo, []),
|
|
||||||
(parse("modulo ?left ?right"), modulo, []),
|
|
||||||
(parse("(?left) + (?right)"), add, []),
|
|
||||||
(parse("(?left) + ?right"), add, []),
|
|
||||||
(parse("?left + (?right)"), add, []),
|
|
||||||
(parse("?left + ?right"), add, []),
|
|
||||||
(parse("(?left) - (?right)"), subtract, []),
|
|
||||||
(parse("(?left) - ?right"), subtract, []),
|
|
||||||
(parse("?left - (?right)"), subtract, []),
|
|
||||||
(parse("?left - ?right"), subtract, []),
|
|
||||||
(parse("(?left) * (?right)"), multiply, []),
|
|
||||||
(parse("(?left) * ?right"), multiply, []),
|
|
||||||
(parse("?left * (?right)"), multiply, []),
|
|
||||||
(parse("?left * ?right"), multiply, []),
|
|
||||||
(parse("?left / ?right"), divide, []),
|
|
||||||
(parse("?left % ?right"), modulo, []),
|
|
||||||
(parse("display ?left"), display, []),
|
|
||||||
]
|
|
||||||
rules = defaults.copy()
|
|
||||||
if len(sys.argv) >= 2:
|
|
||||||
content = read(sys.argv[1])
|
|
||||||
if content == None:
|
|
||||||
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("::> ")
|
|
||||||
if input == None:
|
|
||||||
break
|
|
||||||
elif parse(input) == parse("rules"):
|
|
||||||
print("Rules:")
|
|
||||||
prefix = ' ' * 4
|
|
||||||
for pattern, operation, parameters in rules:
|
|
||||||
if operation == apply:
|
|
||||||
print(prefix + inspect(pattern) + "-> " + inspect(parameters[0]))
|
|
||||||
elif parse(input) == parse("clear"):
|
|
||||||
rules = defaults
|
|
||||||
elif parse(input) == parse("help"):
|
|
||||||
help()
|
|
||||||
elif parse(input) == parse("quit") or parse(input) == parse("exit"):
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
print("Reducing...")
|
|
||||||
print(inspect(seek(run(rules, parse(input)), ["SRT"])))
|
|
||||||
return
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
253
src/modal.c
253
src/modal.c
|
@ -1,15 +1,16 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
unsigned int id, refs, ptr;
|
unsigned int id, refs;
|
||||||
char *a, *b, reg[0x10];
|
char *a, *b;
|
||||||
} Rule;
|
} Rule;
|
||||||
|
|
||||||
static int flip, quiet, cycles = 0x10000;
|
static int flip, quiet, debug, cycles = 0x10000;
|
||||||
static Rule rules[0x1000], *rules_ = rules, lambda;
|
static Rule rules[0x1000], *rules_ = rules, lambda;
|
||||||
static char dict[0x8000], *dict_ = dict, empty;
|
static char dict[0x8000], *dict_ = dict, empty;
|
||||||
static char bank_a[0x4000], *src_ = bank_a;
|
static char bank_a[0x4000], *src_ = bank_a;
|
||||||
static char bank_b[0x4000], *dst_ = bank_b;
|
static char bank_b[0x4000], *dst_ = bank_b;
|
||||||
|
static char *regs[0x100], stack[0x10], *stack_ = stack;
|
||||||
|
|
||||||
#define spacer(c) (c <= ' ' || c == '(' || c == ')')
|
#define spacer(c) (c <= ' ' || c == '(' || c == ')')
|
||||||
|
|
||||||
|
@ -29,32 +30,76 @@ walk(char *s)
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
sint(char *s)
|
||||||
|
{
|
||||||
|
char c;
|
||||||
|
int r = 0, n = 1;
|
||||||
|
if(*s == '-') { n = -1, s++; }
|
||||||
|
while((c = *s++) && !spacer(c)) r = r * 10 + c - '0';
|
||||||
|
return r * n;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
device_write(char *s)
|
||||||
|
{
|
||||||
|
char c = *s, *cap = walk(s), **reg = regs + '0';
|
||||||
|
/* phase: ALU */
|
||||||
|
if(*reg) {
|
||||||
|
int acc = sint(*reg++);
|
||||||
|
/* clang-format off */
|
||||||
|
switch(c) {
|
||||||
|
case '+': while(*reg) acc += sint(*reg++); break;
|
||||||
|
case '-': while(*reg) acc -= sint(*reg++); break;
|
||||||
|
case '*': while(*reg) acc *= sint(*reg++); break;
|
||||||
|
case '/': while(*reg) acc /= sint(*reg++); break;
|
||||||
|
case '%': while(*reg) acc %= sint(*reg++); break;
|
||||||
|
case '&': while(*reg) acc &= sint(*reg++); break;
|
||||||
|
case '^': while(*reg) acc ^= sint(*reg++); break;
|
||||||
|
case '|': while(*reg) acc |= sint(*reg++); break;
|
||||||
|
case '=': while(*reg) acc = acc == sint(*reg++); break;
|
||||||
|
case '!': while(*reg) acc = acc != sint(*reg++); break;
|
||||||
|
case '>': while(*reg) acc = acc > sint(*reg++); break;
|
||||||
|
case '<': while(*reg) acc = acc < sint(*reg++); break;
|
||||||
|
}
|
||||||
|
/* clang-format on */
|
||||||
|
dst_ += snprintf(dst_, 0x10, "%d", acc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* phase: string */
|
||||||
|
if(*s == '(') s++, --cap;
|
||||||
|
while(s < cap) {
|
||||||
|
c = *s++;
|
||||||
|
if(c == '\\') {
|
||||||
|
switch(*s++) {
|
||||||
|
case 't': putc(0x09, stdout); break;
|
||||||
|
case 'n': putc(0x0a, stdout); break;
|
||||||
|
case 's': putc(0x20, stdout); break;
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
putc(c, stdout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
device_read(void)
|
||||||
|
{
|
||||||
|
char c, *origin = dst_;
|
||||||
|
while(fread(&c, 1, 1, stdin) && c >= ' ')
|
||||||
|
*dst_++ = c;
|
||||||
|
if(feof(stdin))
|
||||||
|
*dst_++ = 'E', *dst_++ = 'O', *dst_++ = 'F';
|
||||||
|
if(dst_ - origin == 0)
|
||||||
|
dst_--;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
write_reg(char r, char *reg)
|
write_reg(char r, char *reg)
|
||||||
{
|
{
|
||||||
char c, *cap = walk(reg);
|
char c, *cap = walk(reg);
|
||||||
switch(r) {
|
switch(r) {
|
||||||
case ':': /* op: output */
|
case ':': device_write(reg); return;
|
||||||
if(*reg == '(') reg++, --cap;
|
case '~': device_read(); return;
|
||||||
while(reg < cap) {
|
|
||||||
c = *reg++;
|
|
||||||
if(c == '\\') {
|
|
||||||
switch(*reg++) {
|
|
||||||
case 't': putc(0x09, stdout); break;
|
|
||||||
case 'n': putc(0x0a, stdout); break;
|
|
||||||
case 's': putc(0x20, stdout); break;
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
putc(c, stdout);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
case '~': { /* op: input */
|
|
||||||
while(fread(&c, 1, 1, stdin) && c >= ' ')
|
|
||||||
*dst_++ = c;
|
|
||||||
if(feof(stdin))
|
|
||||||
*dst_++ = 'E', *dst_++ = 'O', *dst_++ = 'F';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
case '^': /* op: join */
|
case '^': /* op: join */
|
||||||
if(*reg == '(') reg++, --cap;
|
if(*reg == '(') reg++, --cap;
|
||||||
while(reg < cap && (c = *reg++))
|
while(reg < cap && (c = *reg++))
|
||||||
|
@ -87,7 +132,7 @@ write_reg(char r, char *reg)
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
write_rule(Rule *r, char *s, int create)
|
write_tail(char *s)
|
||||||
{
|
{
|
||||||
while((*dst_++ = *s++))
|
while((*dst_++ = *s++))
|
||||||
;
|
;
|
||||||
|
@ -96,34 +141,27 @@ write_rule(Rule *r, char *s, int create)
|
||||||
src_ = bank_b, dst_ = bank_a;
|
src_ = bank_b, dst_ = bank_a;
|
||||||
else
|
else
|
||||||
src_ = bank_a, dst_ = bank_b;
|
src_ = bank_a, dst_ = bank_b;
|
||||||
if(!quiet) {
|
|
||||||
if(create)
|
|
||||||
fprintf(stderr, "<> (%s) (%s)\n", r->a, r->b);
|
|
||||||
else
|
|
||||||
fprintf(stderr, "%02d %s\n", r->id, src_), ++r->refs;
|
|
||||||
}
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
apply_rule(Rule *r, char *s)
|
apply_rule(Rule *r, char *s)
|
||||||
{
|
{
|
||||||
unsigned int i, id;
|
unsigned char rid;
|
||||||
char c, *a = r->a, *b = r->b, *origin = dst_, *reg, *regs[0x08];
|
char c, *a = r->a, *b = r->b, *origin = dst_, *reg;
|
||||||
/* phase: clean registers */
|
/* phase: clean regs */
|
||||||
for(i = 0; i < r->ptr; i++)
|
while(stack_ != stack) regs[(int)*(--stack_)] = 0;
|
||||||
regs[i] = NULL;
|
|
||||||
/* phase: match rule */
|
/* phase: match rule */
|
||||||
while((c = *a++)) {
|
while((c = *a++)) {
|
||||||
if(c == '?') {
|
if(c == '?') {
|
||||||
char *pcap = walk(s);
|
char *pcap = walk(s);
|
||||||
id = *a++ - '0';
|
rid = *a++;
|
||||||
if((reg = regs[id])) { /* reg cmp */
|
if((reg = regs[rid])) { /* reg cmp */
|
||||||
char *rcap = walk(reg), *pp = s;
|
char *rcap = walk(reg), *pp = s;
|
||||||
while(reg < rcap || pp < pcap)
|
while(reg < rcap || pp < pcap)
|
||||||
if(*reg++ != *pp++) return 0;
|
if(*reg++ != *pp++) return 0;
|
||||||
} else /* reg set */
|
} else /* reg set */
|
||||||
regs[id] = s;
|
regs[rid] = s, *stack_++ = rid;
|
||||||
s = pcap;
|
s = pcap;
|
||||||
} else if(c != *s++)
|
} else if(c != *s++)
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -133,9 +171,9 @@ apply_rule(Rule *r, char *s)
|
||||||
/* phase: write rule */
|
/* phase: write rule */
|
||||||
while((c = *b++)) {
|
while((c = *b++)) {
|
||||||
if(c == '?') {
|
if(c == '?') {
|
||||||
id = *b - '0';
|
rid = *b;
|
||||||
if(id < 9 && (reg = regs[id]))
|
if((reg = regs[rid]))
|
||||||
b++, write_reg(r->reg[id], reg);
|
b++, write_reg(rid, reg);
|
||||||
else
|
else
|
||||||
*dst_++ = c;
|
*dst_++ = c;
|
||||||
} else
|
} else
|
||||||
|
@ -145,55 +183,42 @@ apply_rule(Rule *r, char *s)
|
||||||
while(*s == ' ') s++;
|
while(*s == ' ') s++;
|
||||||
if(*s == ')' && *(dst_ - 1) == ' ') dst_--;
|
if(*s == ')' && *(dst_ - 1) == ' ') dst_--;
|
||||||
}
|
}
|
||||||
return write_rule(r, s, 0);
|
if(!quiet) fprintf(stderr, "%02d %s\n", r->id, src_), ++r->refs;
|
||||||
}
|
return write_tail(s);
|
||||||
|
|
||||||
static int
|
|
||||||
find_register(Rule *r, char reg)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
for(i = 0; i < (int)r->ptr; i++)
|
|
||||||
if(r->reg[i] == reg) return i;
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *
|
static char *
|
||||||
compile_rule(Rule *r, int id, char *src)
|
parse_frag(char **side, char *src)
|
||||||
{
|
{
|
||||||
|
int wrapped;
|
||||||
char c, *cap;
|
char c, *cap;
|
||||||
int wrapped, reg;
|
|
||||||
r->id = id, r->ptr = 0, r->a = &empty, r->b = ∅
|
|
||||||
/* phase: compile left */
|
|
||||||
while((c = *src) && c == ' ') src++;
|
while((c = *src) && c == ' ') src++;
|
||||||
if(c == ')' || (c == '<' && src[1] == '>')) return src;
|
if(c == ')' || (c == '<' && src[1] == '>')) {
|
||||||
r->a = dict_, cap = walk(src), wrapped = c == '(';
|
*side = ∅
|
||||||
if(wrapped) src++, cap--;
|
return src;
|
||||||
while(src < cap) {
|
|
||||||
c = *src, *dict_++ = *src++;
|
|
||||||
if(c == '?') {
|
|
||||||
c = *src++, reg = find_register(r, c);
|
|
||||||
if(reg == -1 && c != '(')
|
|
||||||
r->reg[r->ptr] = c, reg = r->ptr++;
|
|
||||||
*dict_++ = '0' + reg;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
src += wrapped, *dict_++ = 0;
|
*side = dict_, cap = walk(src), wrapped = c == '(';
|
||||||
/* phase: compile right */
|
|
||||||
while((c = *src) && c == ' ') src++;
|
|
||||||
if(c == ')' || (c == '<' && src[1] == '>')) return src;
|
|
||||||
r->b = dict_, cap = walk(src), wrapped = c == '(';
|
|
||||||
if(wrapped) src++, cap--;
|
if(wrapped) src++, cap--;
|
||||||
while(src < cap) {
|
while(src < cap) c = *src, *dict_++ = *src++;
|
||||||
c = *src, *dict_++ = *src++;
|
|
||||||
if(c == '?') {
|
|
||||||
c = *src++, reg = find_register(r, c);
|
|
||||||
*dict_++ = reg != -1 ? '0' + reg : c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
src += wrapped, *dict_++ = 0;
|
src += wrapped, *dict_++ = 0;
|
||||||
return src;
|
return src;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Rule *
|
||||||
|
find_rule(char *s, char *cap)
|
||||||
|
{
|
||||||
|
Rule *r = rules;
|
||||||
|
if(*s == '(') s++, cap--;
|
||||||
|
while(r < rules_) {
|
||||||
|
char *ss = s, *a = r->a;
|
||||||
|
if(a)
|
||||||
|
while(*ss++ == *a++)
|
||||||
|
if(!*a && ss == cap) return r;
|
||||||
|
r++;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
rewrite(void)
|
rewrite(void)
|
||||||
{
|
{
|
||||||
|
@ -202,23 +227,47 @@ rewrite(void)
|
||||||
while((c = *s)) {
|
while((c = *s)) {
|
||||||
if(c == '(' || spacer(last)) {
|
if(c == '(' || spacer(last)) {
|
||||||
Rule *r = NULL;
|
Rule *r = NULL;
|
||||||
/* phase: rule */
|
/* phase: undefine */
|
||||||
if(c == '<' && s[1] == '>') {
|
if(c == '>' && s[1] == '<') {
|
||||||
r = rules_++;
|
s += 2;
|
||||||
s = compile_rule(r, rules_ - rules - 1, s + 2);
|
|
||||||
while(*s == ' ') s++;
|
while(*s == ' ') s++;
|
||||||
return write_rule(r, s, 1);
|
cap = walk(s), r = find_rule(s, cap);
|
||||||
|
if(r != NULL) {
|
||||||
|
if(!quiet) fprintf(stderr, ">< (%s) (%s)\n", r->a, r->b);
|
||||||
|
r->a = 0;
|
||||||
|
}
|
||||||
|
while(*cap == ' ') cap++;
|
||||||
|
return write_tail(cap);
|
||||||
|
}
|
||||||
|
/* phase: define */
|
||||||
|
if(c == '<' && s[1] == '>') {
|
||||||
|
r = rules_, r->id = rules_ - rules;
|
||||||
|
s = parse_frag(&r->b, parse_frag(&r->a, s + 2));
|
||||||
|
if(*r->a) {
|
||||||
|
if(!quiet) fprintf(stderr, "<> (%s) (%s)\n", r->a, r->b);
|
||||||
|
rules_++;
|
||||||
|
}
|
||||||
|
while(*s == ' ') s++;
|
||||||
|
return write_tail(s);
|
||||||
}
|
}
|
||||||
/* phase: lambda */
|
/* phase: lambda */
|
||||||
if(c == '?' && s[1] == '(') {
|
if(c == '?' && s[1] == '(') {
|
||||||
cap = walk(s + 1), compile_rule(&lambda, -1, s + 2), s = cap;
|
char *d_ = dict_;
|
||||||
|
cap = walk(s + 1);
|
||||||
|
r = &lambda, r->id = -1;
|
||||||
|
parse_frag(&r->b, parse_frag(&r->a, s + 2));
|
||||||
|
s = cap;
|
||||||
while(*s == ' ') s++;
|
while(*s == ' ') s++;
|
||||||
if(!apply_rule(&lambda, s)) write_rule(&lambda, s, 0);
|
if(!apply_rule(&lambda, s)) {
|
||||||
|
if(!quiet) fprintf(stderr, "%02d %s\n", r->id, src_), ++r->refs;
|
||||||
|
write_tail(s);
|
||||||
|
}
|
||||||
|
dict_ = d_;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
/* phase: match */
|
/* phase: match */
|
||||||
for(r = rules; r < rules_; r++)
|
for(r = rules; r < rules_; r++)
|
||||||
if(apply_rule(r, s)) return 1;
|
if(r->a && apply_rule(r, s)) return 1;
|
||||||
}
|
}
|
||||||
*dst_++ = last = c;
|
*dst_++ = last = c;
|
||||||
s++;
|
s++;
|
||||||
|
@ -231,19 +280,20 @@ int
|
||||||
main(int argc, char **argv)
|
main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
FILE *f;
|
FILE *f;
|
||||||
int i, pl = 0, pr = 0;
|
int i, pl = 0, pr = 0, rw = 0;
|
||||||
char c, last = 0, *w = bank_a;
|
char c, last = 0, *w = bank_a;
|
||||||
if(argc < 2)
|
if(argc < 2)
|
||||||
return !printf("usage: modal [-vqn] source.modal\n");
|
return !printf("usage: modal [-vqn] source.modal\n");
|
||||||
for(i = 1; i < argc && *argv[i] == '-'; i++) {
|
for(i = 1; i < argc && *argv[i] == '-'; i++) {
|
||||||
switch(argv[i][1]) {
|
switch(argv[i][1]) {
|
||||||
case 'v': /* version */ return !printf("Modal Interpreter, 26 Apr 2024.\n");
|
case 'v': /* version */ return !printf("Modal Interpreter, 4 May 2024.\n");
|
||||||
case 'q': /* quiet */ quiet = 1; break;
|
case 'q': /* quiet */ quiet = 1; break;
|
||||||
|
case 'p': /* debug */ debug = 1; break;
|
||||||
case 'n': /* infinite */ cycles = 0xffffffff; break;
|
case 'n': /* infinite */ cycles = 0xffffffff; break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(!(f = fopen(argv[i], "r")))
|
if(!(f = fopen(argv[i], "r")))
|
||||||
return !fprintf(stdout, "Modal file invalid: %s.\n", argv[i]);
|
return !printf("Modal file invalid: %s.\n", argv[i]);
|
||||||
while(fread(&c, 1, 1, f)) {
|
while(fread(&c, 1, 1, f)) {
|
||||||
c = c <= 0x20 ? 0x20 : c;
|
c = c <= 0x20 ? 0x20 : c;
|
||||||
if(c == ' ' && last == '(') continue;
|
if(c == ' ' && last == '(') continue;
|
||||||
|
@ -258,11 +308,20 @@ main(int argc, char **argv)
|
||||||
while(*(--w) <= ' ') *w = 0;
|
while(*(--w) <= ' ') *w = 0;
|
||||||
fclose(f);
|
fclose(f);
|
||||||
if(pr != pl)
|
if(pr != pl)
|
||||||
return !fprintf(stdout, "Modal program imbalanced.\n");
|
return !fprintf(stderr, "Modal program imbalanced.\n");
|
||||||
while(rewrite())
|
while(rewrite() && ++rw)
|
||||||
if(!cycles--) return !fprintf(stdout, "Modal rewrites exceeded.\n");
|
if(!cycles--) return !fprintf(stderr, "Modal rewrites exceeded.\n");
|
||||||
while(rules_-- > rules && !quiet)
|
if(!quiet) {
|
||||||
if(!rules_->refs) printf("-- Unused rule: %d <> (%s) (%s)\n", rules_->refs, rules_->a, rules_->b);
|
while(rules_-- > rules) {
|
||||||
if(!quiet) printf(".. %s\n", src_);
|
if(rules_->a) {
|
||||||
|
if(!rules_->refs)
|
||||||
|
fprintf(stderr, "-- Unused rule: %d <> (%s) (%s)\n", rules_->id, rules_->a, rules_->b);
|
||||||
|
if(debug)
|
||||||
|
fprintf(stderr, " (%s) (%s), %d times.\n", rules_->a, rules_->b, rules_->refs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(rw)
|
||||||
|
fprintf(stderr, ".. %s\nCompleted in %d rewrites.\n", src_, rw);
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue