diff --git a/README.md b/README.md index 033992c..f3ca1ea 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ cc src/modal.c -o bin/modal bin/modal examples/hello.modal -v Print version -q Quiet mode, no step printing + -p Print summary with refs count -n Infinite mode, no rewrites limit ``` diff --git a/examples/fib.modal b/examples/fib.modal new file mode 100644 index 0000000..e422a49 --- /dev/null +++ b/examples/fib.modal @@ -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) \ No newline at end of file diff --git a/examples/fizzbuzz-alu.modal b/examples/fizzbuzz-alu.modal new file mode 100644 index 0000000..3371eda --- /dev/null +++ b/examples/fizzbuzz-alu.modal @@ -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 \ No newline at end of file diff --git a/examples/postcard.modal b/examples/postcard.modal index 478b784..ffa521d 100644 --- a/examples/postcard.modal +++ b/examples/postcard.modal @@ -1,4 +1,3 @@ - <> (what is ?x) (is ?x a (programming language)) <> (is ?x a ?y) (or, is ?x (a virtual machine)) <> (or, is ?x ?y) (?x is ?y <> ?y (a meta-language)) @@ -6,4 +5,3 @@ <> (?x is ?x) (?(?: ?:) ?x) what is modal - diff --git a/examples/tests.modal b/examples/tests.modal index cc8a36b..2c9d38e 100644 --- a/examples/tests.modal +++ b/examples/tests.modal @@ -8,8 +8,8 @@ ?(?-) (Formatter) -?((?x ?y) one) aaa(bbb) = one (formatter 1) test -?((?x ?y) one) (bbb)aaa = one (formatter 2) test +?((?x ?y) two) aaa(bbb) = two (formatter 1) 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 4) 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 +?(?-) (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) <> ((?x -> ?y)) (<> ?x ?y) @@ -93,6 +106,36 @@ reverse (modal) = ladom (reverse 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) -<> (?x = ?y ?n test) (?(?: ?:) (#fail ?n found: ?x expect: ?y\n)) \ No newline at end of file +<> (?x = ?y ?n test) (?(?: ?:) (#fail ?n found: ?x expect: ?y\n)) + diff --git a/makefile b/makefile index 24e2457..563ad91 100644 --- a/makefile +++ b/makefile @@ -1,5 +1,5 @@ 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 diff --git a/old/modal.py b/old/modal.py deleted file mode 100644 index e3afa9b..0000000 --- a/old/modal.py +++ /dev/null @@ -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, "") - -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() diff --git a/src/modal.c b/src/modal.c index 0c1a83f..d2986ae 100644 --- a/src/modal.c +++ b/src/modal.c @@ -1,15 +1,16 @@ #include typedef struct { - unsigned int id, refs, ptr; - char *a, *b, reg[0x10]; + unsigned int id, refs; + char *a, *b; } Rule; -static int flip, quiet, cycles = 0x10000; +static int flip, quiet, debug, cycles = 0x10000; static Rule rules[0x1000], *rules_ = rules, lambda; static char dict[0x8000], *dict_ = dict, empty; static char bank_a[0x4000], *src_ = bank_a; static char bank_b[0x4000], *dst_ = bank_b; +static char *regs[0x100], stack[0x10], *stack_ = stack; #define spacer(c) (c <= ' ' || c == '(' || c == ')') @@ -29,32 +30,76 @@ walk(char *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 write_reg(char r, char *reg) { char c, *cap = walk(reg); switch(r) { - case ':': /* op: output */ - if(*reg == '(') reg++, --cap; - 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 ':': device_write(reg); return; + case '~': device_read(); return; case '^': /* op: join */ if(*reg == '(') reg++, --cap; while(reg < cap && (c = *reg++)) @@ -87,7 +132,7 @@ write_reg(char r, char *reg) } static int -write_rule(Rule *r, char *s, int create) +write_tail(char *s) { while((*dst_++ = *s++)) ; @@ -96,34 +141,27 @@ write_rule(Rule *r, char *s, int create) src_ = bank_b, dst_ = bank_a; else 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; } static int apply_rule(Rule *r, char *s) { - unsigned int i, id; - char c, *a = r->a, *b = r->b, *origin = dst_, *reg, *regs[0x08]; - /* phase: clean registers */ - for(i = 0; i < r->ptr; i++) - regs[i] = NULL; + unsigned char rid; + char c, *a = r->a, *b = r->b, *origin = dst_, *reg; + /* phase: clean regs */ + while(stack_ != stack) regs[(int)*(--stack_)] = 0; /* phase: match rule */ while((c = *a++)) { if(c == '?') { char *pcap = walk(s); - id = *a++ - '0'; - if((reg = regs[id])) { /* reg cmp */ + rid = *a++; + if((reg = regs[rid])) { /* reg cmp */ char *rcap = walk(reg), *pp = s; while(reg < rcap || pp < pcap) if(*reg++ != *pp++) return 0; } else /* reg set */ - regs[id] = s; + regs[rid] = s, *stack_++ = rid; s = pcap; } else if(c != *s++) return 0; @@ -133,9 +171,9 @@ apply_rule(Rule *r, char *s) /* phase: write rule */ while((c = *b++)) { if(c == '?') { - id = *b - '0'; - if(id < 9 && (reg = regs[id])) - b++, write_reg(r->reg[id], reg); + rid = *b; + if((reg = regs[rid])) + b++, write_reg(rid, reg); else *dst_++ = c; } else @@ -145,55 +183,42 @@ apply_rule(Rule *r, char *s) while(*s == ' ') s++; if(*s == ')' && *(dst_ - 1) == ' ') dst_--; } - return write_rule(r, s, 0); -} - -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; + if(!quiet) fprintf(stderr, "%02d %s\n", r->id, src_), ++r->refs; + return write_tail(s); } static char * -compile_rule(Rule *r, int id, char *src) +parse_frag(char **side, char *src) { + int wrapped; 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++; - if(c == ')' || (c == '<' && src[1] == '>')) return src; - r->a = dict_, cap = walk(src), wrapped = c == '('; - if(wrapped) src++, cap--; - 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; - } + if(c == ')' || (c == '<' && src[1] == '>')) { + *side = ∅ + return src; } - src += wrapped, *dict_++ = 0; - /* phase: compile right */ - while((c = *src) && c == ' ') src++; - if(c == ')' || (c == '<' && src[1] == '>')) return src; - r->b = dict_, cap = walk(src), wrapped = c == '('; + *side = dict_, cap = walk(src), wrapped = c == '('; if(wrapped) src++, cap--; - while(src < cap) { - c = *src, *dict_++ = *src++; - if(c == '?') { - c = *src++, reg = find_register(r, c); - *dict_++ = reg != -1 ? '0' + reg : c; - } - } + while(src < cap) c = *src, *dict_++ = *src++; src += wrapped, *dict_++ = 0; 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 rewrite(void) { @@ -202,23 +227,47 @@ rewrite(void) while((c = *s)) { if(c == '(' || spacer(last)) { Rule *r = NULL; - /* phase: rule */ - if(c == '<' && s[1] == '>') { - r = rules_++; - s = compile_rule(r, rules_ - rules - 1, s + 2); + /* phase: undefine */ + if(c == '>' && s[1] == '<') { + s += 2; 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 */ 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++; - 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; } /* phase: match */ 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; s++; @@ -231,19 +280,20 @@ int main(int argc, char **argv) { FILE *f; - int i, pl = 0, pr = 0; + int i, pl = 0, pr = 0, rw = 0; char c, last = 0, *w = bank_a; if(argc < 2) return !printf("usage: modal [-vqn] source.modal\n"); for(i = 1; i < argc && *argv[i] == '-'; i++) { 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 'p': /* debug */ debug = 1; break; case 'n': /* infinite */ cycles = 0xffffffff; break; } } 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)) { c = c <= 0x20 ? 0x20 : c; if(c == ' ' && last == '(') continue; @@ -258,11 +308,20 @@ main(int argc, char **argv) while(*(--w) <= ' ') *w = 0; fclose(f); if(pr != pl) - return !fprintf(stdout, "Modal program imbalanced.\n"); - while(rewrite()) - if(!cycles--) return !fprintf(stdout, "Modal rewrites exceeded.\n"); - while(rules_-- > rules && !quiet) - if(!rules_->refs) printf("-- Unused rule: %d <> (%s) (%s)\n", rules_->refs, rules_->a, rules_->b); - if(!quiet) printf(".. %s\n", src_); + return !fprintf(stderr, "Modal program imbalanced.\n"); + while(rewrite() && ++rw) + if(!cycles--) return !fprintf(stderr, "Modal rewrites exceeded.\n"); + if(!quiet) { + while(rules_-- > rules) { + 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; -} \ No newline at end of file +}