Merge remote-tracking branch 'upstream/master' into d6/binary

This commit is contained in:
~d6 2024-04-26 23:08:48 -04:00
commit 52ae7406b5
8 changed files with 280 additions and 208 deletions

View File

@ -14,6 +14,9 @@ cc src/modal.c -o bin/modal
``` ```
bin/modal examples/hello.modal bin/modal examples/hello.modal
-v Print version
-q Quiet mode, no step printing
-n Infinite mode, no rewrites limit
``` ```
## Credits ## Credits

View File

@ -1,11 +0,0 @@
?(?-) (This example demonstrates how to keep the runtime active between prompts.)
<> ((You said: quit\n) send) ((You quit.) print ')
<> (?: print ') (?:)
<> (?: send) (?: wait stdin)
<> (wait ?~) ((You said: ?~\n) send)
<> (' ?x) (?x ')
(Say something, or type "quit": \n) print '
wait stdin

9
examples/postcard.modal Normal file
View File

@ -0,0 +1,9 @@
<> (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))
<> ((a meta-language)) modal
<> (?x is ?x) (?(?: ?:) ?x)
what is modal

8
examples/repl.modal Normal file
View File

@ -0,0 +1,8 @@
?(?-) (This is a fully functional Modal REPL.)
<> quit EOF
<> wait?~ (?~ wait!)
<> (?: print) ?:
<> (EOF wait!) (Bye.\n print)
(Say something, or type "quit":\n) print wait!

21
examples/sierpinski.modal Normal file
View File

@ -0,0 +1,21 @@
?(?-) (Rules)
<> (* (. > (. ?x))) (* (. (. > ?x)))
<> (. (. > (* ?x))) (* (. (* > ?x)))
?(?-) (Physics)
<> (Tri > (?x ?y)) (Tri (?x > ?y))
<> (Tri (?x > (?y ?z))) (Tri (?x (?y > ?z)))
<> (?x (?y > (?z ?n))) (. (?y (?z > ?n)))
<> ((?x > ())) (< ())
<> (Tri < (* ?^)) (?(?: ?:) (*?^ \n))
<> ((?x < ?y)) (< (?x ?y))
?(?-) (Print)
<> (Tri.join ?x ?:) (Tri > ?x ?:)
<> (Tri.dup ?x ?^) (Tri.join ?x ?^)
<> (Tri < ?x) (Tri.dup (. ?x) (?x \n))
?(?* (Tri < (?*))) ...............*...............

View File

@ -1,74 +1,82 @@
?(?-) (This example tests various aspects of the implementation.) ?(?-) (This example tests various aspects of the implementation.)
?(?-) (Inline rules) ?(?-) (Early Test Primitives)
<> ((?x -> ?y)) (<> ?x ?y) <> (?x = ?x ?n test) (?(?: ?:) (#pass ?n\n))
(nap -> (tap =))
nap tap test <> (ghost) ()
?(?-) (Formatter)
?((?x ?y) one) aaa(bbb) = one (formatter 1) test
?((?x ?y) one) (bbb)aaa = one (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
(a b c ( a b c ) ) = (a b c (a b c)) (formatter 6) test
?(?-) (Empty replacements) ?(?-) (Empty replacements)
<> (?x pop-plain) (ab cd () ghost) = (ab cd ()) (empty 1) test
<> (?x pop) () (ab cd ghost ()) = (ab cd ()) (empty 2) test
<> (ghost) () (ab ghost cd ()) = (ab cd ()) (empty 3) test
(ghost ab cd ()) = (ab cd ()) (empty 4) test
(ghost) = () (empty 5) test
abc def pop-plain = abc test (q ?((?x ?y ?z) (?x ?y)) a b c) = (q a b) (empty 6) test
abc def pop = abc test (q ?((?x ?y ?z) (?x ?z)) a b c) = (q a c) (empty 7) test
(ghost) = () test (q ?((?x ?y ?z) (?y ?z)) a b c) = (q b c) (empty 8) test
?(?-) (Basic replacements) <> (prefix/pop ?x) ()
<> (?x suffix/pop) ()
<> (replace-name (foo)) ((bar) =) (ab prefix/pop cd ef) = (ab ef) (empty 9) test
(ab cd suffix/pop ef) = (ab ef) (empty 10) test
replace-name (foo) (bar) test
?(?-) (Basic register setups) ?(?-) (Basic register setups)
<> (dup (?x)) ((?x ?x) =) <> (dup (?x)) (?x ?x)
<> (swap (?x ?y)) ((?y ?x) =) <> (swap (?x ?y)) (?y ?x)
<> (compare (?x ?x ?x)) ((#t) =) <> (compare (?x ?x ?x)) (#t)
<> (rotate (?x (?y (?z)))) (?y (?z (?x)))
<> (unused ?x) ?y
dup (abc) (abc abc) test (dup (abc)) = (abc abc) (basic 1) test
swap (abc def) (def abc) test (swap (abc def)) = (def abc) (basic 2) test
compare (abc abc abc) (#t) test (compare (abc abc abc)) = (#t) (basic 3) test
(rotate (abc (def (ghi)))) = (def (ghi (abc))) (basic 4) test
(unused hey) = (?y) (basic 5) test
?(?-) (Empty register replacement) ?(?-) (Substring registers)
<> (replace-empty ?x) (?y) <> (connect ?x ?y ?z) (?x-?y?z)
<> (prefix-?x) (?x-suffix)
replace-empty abc = ?y test connect foo bar baz = foo-barbaz (substring 1) test
prefix-anything = anything-suffix (substring 2) test
?(?-) (Guards setups) ?(foo QQQ) foobar = foobar (substring 3) test
<> (join (String ?x) (String ?y)) ((?x ?y) =)
join (String abc) (String def) (abc def) test
?(?-) (Lambdas) ?(?-) (Lambdas)
?((?x) ((?x ?x) =)) abc (abc abc) test (?(?x (?x ?x)) abc) = (abc abc) (lambda 1) test
abc ?(?x) def = abc test abc ?(?x) def = abc (lambda 2) test
?(?-) (Explode) ?(?-) (op: explode)
<> (explode ?*) ((?*) =) (?(?* ?*) cow) = (c (o (w ()))) (explode word) test
(?(?* ?*) (12 34 45)) = (12 (34 (45 ()))) (explode tuple) test
(?(?* ?*) ()) = () (explode empty) test
explode cow (c (o (w ()))) test ?(?-) (op: join)
explode (12 34 45) (12 (34 (45 ()))) test
?(?-) (Implode) ?(?^ ?^) (b (a (t ()))) = bat (join 1) test
?(?^ ?^) (12 (34 (56 ()))) = 123456 (join 2) test
(?(?^ ?^) ()) = () (join empty) test
<> (implode ?^) (?^ =) ?(?-) (op: unwrap)
implode (b (a (t ()))) bat test ?(?. ?.) (abcd) = abcd (unwrap 1) test
implode (12 (34 (56 ()))) 123456 test (?(?. ?.) ()) = () (unwrap empty) test
?(?-) (Test Primitives)
<> (?: print) (?:)
<> (?x = ?x test) (#ok)
<> (?x = ?y test) (#fail)
?(?-) (List reversal) ?(?-) (List reversal)
@ -76,5 +84,15 @@ implode (12 (34 (56 ()))) 123456 test
<> (reverse (?*)) (reverse List (?*) ()) <> (reverse (?*)) (reverse List (?*) ())
<> (reverse List (?x ?y) ?z) (reverse List ?y (?x ?z)) <> (reverse List (?x ?y) ?z) (reverse List ?y (?x ?z))
reverse (modal) = ladom test reverse (modal) = ladom (reverse 1) test
?(?-) (Inline rules)
<> ((?x -> ?y)) (<> ?x ?y)
(nap -> (tap =))
nap tap (inline 1) test
?(?-) (Late Test Primitives)
<> (?x = ?y ?n test) (?(?: ?:) (#fail ?n found: ?x expect: ?y\n))

View File

@ -8,12 +8,14 @@ all: dest
dest: dest:
@ mkdir -p bin @ mkdir -p bin
run: all bin/modal run: all bin/modal
@ bin/modal examples/hello.modal 2> /dev/null @ bin/modal -q examples/hello.modal
debug: bin/modal-debug debug: all bin/modal-debug
@ bin/modal-debug examples/hello.modal @ bin/modal-debug examples/hello.modal
test: bin/modal-debug bin/modal test: all bin/modal-debug bin/modal
@ bin/modal -v @ bin/modal -v
@ bin/modal-debug examples/tests.modal @ bin/modal-debug -q examples/fizzbuzz.modal
@ bin/modal-debug -q examples/sierpinski.modal
@ bin/modal-debug -q examples/tests.modal
install: bin/modal install: bin/modal
cp bin/modal ~/bin/ cp bin/modal ~/bin/
uninstall: uninstall:

View File

@ -1,16 +1,15 @@
#include <stdio.h> #include <stdio.h>
typedef struct { typedef struct {
int id; unsigned int id, refs, ptr;
char *a, *b; char *a, *b, reg[0x10];
} Rule; } Rule;
static int flip, rmin = 0xff, rmax = 0x00, cycles = 0x10000; static int flip, quiet, cycles = 0x10000;
static Rule rules[0x1000], lambda, *rules_ = rules; static Rule rules[0x1000], *rules_ = rules, lambda;
static char dict[0x8000], *dict_ = dict; 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];
#define spacer(c) (c <= ' ' || c == '(' || c == ')') #define spacer(c) (c <= ' ' || c == '(' || c == ')')
@ -30,62 +29,17 @@ walk(char *s)
return s; return s;
} }
static int
set_reg(int r, char *b)
{
if(regs[r]) {
char *a = regs[r], *aa = walk(a), *bb = walk(b);
while(a < aa && b < bb)
if(*a++ != *b++) return 0;
} else {
regs[r] = b;
if(r < rmin) rmin = r;
if(r > rmax) rmax = r;
}
return 1;
}
static void static void
put_reg(char r) write_reg(char r, char *reg)
{ {
char c, *s = regs[(int)r], *ss; char c, *cap = walk(reg);
if(!s) { switch(r) {
*dst_++ = '?', *dst_++ = r; case ':': /* op: output */
return; if(*reg == '(') reg++, --cap;
} while(reg < cap) {
ss = walk(s); c = *reg++;
if(r == '*') {
int i, depth = 0;
if(*s == '(') { /* special explode tuple */
s++;
while(s < ss) {
while((c = *s) && !spacer(c))
*dst_++ = c, s++;
*dst_++ = ' ';
*dst_++ = '(', s++, depth++;
}
} else { /* special explode token */
while((c = *s++) && !spacer(c))
*dst_++ = c, *dst_++ = ' ', *dst_++ = '(', depth++;
}
for(i = 0; i < depth; i++)
*dst_++ = ')';
} else if(r == '.') { /* special unpack */
if(*s == '(') s++, --ss;
while(s < ss) *dst_++ = *s++;
} else if(r == '^') { /* special join */
if(*s == '(') s++, --ss;
while(s < ss && (c = *s++))
if(!spacer(c)) *dst_++ = c;
} else if(r == '~') { /* special stdin */
while(fread(&c, 1, 1, stdin) && c >= ' ')
*dst_++ = c;
} else if(r == ':') { /* special stdout */
if(*s == '(') s++, --ss;
while(s < ss) {
c = *s++;
if(c == '\\') { if(c == '\\') {
switch(*s++) { switch(*reg++) {
case 't': putc(0x09, stdout); break; case 't': putc(0x09, stdout); break;
case 'n': putc(0x0a, stdout); break; case 'n': putc(0x0a, stdout); break;
case 's': putc(0x20, stdout); break; case 's': putc(0x20, stdout); break;
@ -93,117 +47,178 @@ put_reg(char r)
} else } else
putc(c, stdout); putc(c, stdout);
} }
} else return;
while(s < ss) *dst_++ = *s++; case '~': { /* op: input */
} while(fread(&c, 1, 1, stdin) && c >= ' ')
*dst_++ = c;
static char * if(feof(stdin))
match_rule(Rule *r, char *p) *dst_++ = 'E', *dst_++ = 'O', *dst_++ = 'F';
{ return;
int i;
char c, last = 0, *a = r->a, *b = p;
if(rmax) {
for(i = rmin; i <= rmax; i++)
regs[i] = 0;
rmin = 0xff, rmax = 0x00;
} }
while((c = *a)) { case '^': /* op: join */
if(spacer(last) && c == '?') { if(*reg == '(') reg++, --cap;
if(!set_reg(*(++a), b)) return NULL; while(reg < cap && (c = *reg++))
a++, b = walk(b); if(!spacer(c)) *dst_++ = c;
continue; return;
} case '.': /* op: unwrap */
if(c != *b) return NULL; if(*reg == '(') reg++, --cap;
a++, b++, last = c; while(reg < cap) *dst_++ = *reg++;
return;
case '*': { /* op: explode */
int i, depth = 0;
if(*reg == '(' && reg[1] != ')') { /* tuple */
reg++;
while(reg < cap) {
while((c = *reg) && !spacer(c))
*dst_++ = c, reg++;
*dst_++ = ' ';
*dst_++ = '(', reg++, depth++;
}
} else /* token */
while((c = *reg++) && !spacer(c))
*dst_++ = c, *dst_++ = ' ', *dst_++ = '(', depth++;
for(i = 0; i < depth; i++) *dst_++ = ')';
return;
}
default:
while(reg < cap) *dst_++ = *reg++;
return;
} }
c = *b;
return spacer(c) ? b : NULL;
} }
static int static int
commit_rule(Rule *r, char *s, int create) write_rule(Rule *r, char *s, int create)
{ {
while((*dst_++ = *s++)) while((*dst_++ = *s++))
; ;
*dst_++ = 0; *dst_ = 0;
if((flip = !flip)) if((flip = !flip))
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(create) if(!quiet) {
fprintf(stderr, "<> (%s) (%s)\n", r->a, r->b); if(create)
else fprintf(stderr, "<> (%s) (%s)\n", r->a, r->b);
fprintf(stderr, "%02d %s\n", r->id, src_); else
fprintf(stderr, "%02d %s\n", r->id, src_), ++r->refs;
}
return 1; return 1;
} }
static int static int
write_rule(Rule *r, char last, char *res) apply_rule(Rule *r, char *s)
{ {
char c, *b = r->b, *origin = dst_; unsigned int i, id;
while((c = *b++)) char c, *a = r->a, *b = r->b, *origin = dst_, *reg, *regs[0x08];
if(spacer(last) && c == '?') /* phase: clean registers */
put_reg(*b++); for(i = 0; i < r->ptr; i++)
else regs[i] = NULL;
*dst_++ = c, last = c; /* phase: match rule */
if(dst_ == origin) { while((c = *a++)) {
while(*res == ' ') res++; if(c == '?') {
if(*res == ')' && *(dst_ - 1) == ' ') dst_--; char *pcap = walk(s);
id = *a++ - '0';
if((reg = regs[id])) { /* reg cmp */
char *rcap = walk(reg), *pp = s;
while(reg < rcap || pp < pcap)
if(*reg++ != *pp++) return 0;
} else /* reg set */
regs[id] = s;
s = pcap;
} else if(c != *s++)
return 0;
} }
return commit_rule(r, res, 0); c = *s;
} if(!spacer(c)) return 0;
/* phase: write rule */
static char * while((c = *b++)) {
parse_frag(char *s) if(c == '?') {
{ id = *b - '0';
char c, *ss; if(id < 9 && (reg = regs[id]))
while((c = *s) && c <= ' ') s++; b++, write_reg(r->reg[id], reg);
if(*s != ')' && !(*s == '<' && s[1] == '>')) { else
ss = walk(s); *dst_++ = c;
if(*s == '(') {
s++;
while(s < ss - 1) *dict_++ = *s++;
s++;
} else } else
while(s < ss) *dict_++ = *s++; *dst_++ = c;
} }
*dict_++ = 0; if(dst_ == origin) {
return 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;
} }
static char * static char *
create_rule(Rule *r, int id, char *s) compile_rule(Rule *r, int id, char *src)
{ {
r->id = id, s += 2; char c, *cap;
r->a = dict_, s = parse_frag(s); int wrapped, reg;
r->b = dict_, s = parse_frag(s); r->id = id, r->ptr = 0, r->a = &empty, r->b = &empty;
return s; /* 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;
}
}
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 == '(';
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;
}
}
src += wrapped, *dict_++ = 0;
return src;
} }
static int static int
rewrite(void) rewrite(void)
{ {
char c, last = 0, *cap, *s = src_, *res; char c, last = 0, *cap, *s = src_;
while((c = *s) && c <= ' ') s++; while(*s == ' ') s++;
while((c = *s)) { while((c = *s)) {
if(spacer(last)) { if(c == '(' || spacer(last)) {
Rule *r; Rule *r = NULL;
if(*s == '<' && s[1] == '>') { /* phase: rule */
if(c == '<' && s[1] == '>') {
r = rules_++; r = rules_++;
s = create_rule(r, rules_ - rules - 1, s); s = compile_rule(r, rules_ - rules - 1, s + 2);
while((c = *s) && c <= ' ') s++; while(*s == ' ') s++;
return commit_rule(r, s, 1); return write_rule(r, s, 1);
} }
if(*s == '?' && s[1] == '(') { /* phase: lambda */
r = &lambda, cap = walk(s + 1); if(c == '?' && s[1] == '(') {
create_rule(&lambda, -1, s), s = cap; cap = walk(s + 1), compile_rule(&lambda, -1, s + 2), s = cap;
while((c = *s) && c <= ' ') s++; while(*s == ' ') s++;
if((res = match_rule(&lambda, s)) != NULL) if(!apply_rule(&lambda, s)) write_rule(&lambda, s, 0);
return write_rule(&lambda, last, res); return 1;
} }
/* phase: match */
for(r = rules; r < rules_; r++) for(r = rules; r < rules_; r++)
if((res = match_rule(r, s)) != NULL) if(apply_rule(r, s)) return 1;
return write_rule(r, last, res);
} }
*dst_++ = last = c; *dst_++ = last = c;
s++; s++;
@ -216,31 +231,38 @@ int
main(int argc, char **argv) main(int argc, char **argv)
{ {
FILE *f; FILE *f;
int i; int i, pl = 0, pr = 0;
char c, *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, 18 Apr 2024.\n"); case 'v': /* version */ return !printf("Modal Interpreter, 26 Apr 2024.\n");
case 'q': /* quiet */ fclose(stderr); break; case 'q': /* quiet */ quiet = 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, "Invalid Modal file: %s.\n", argv[i]); return !fprintf(stdout, "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(w > bank_a) { if(c == ' ' && last == '(') continue;
if(c == ' ' && *(w - 1) == '(') continue; if(c == ')' && last == ' ') w--;
if(c == ')' && *(w - 1) == ' ') w--; if(c == ' ' && last == ' ') w--;
if(c == ' ' && *(w - 1) == ' ') w--; if(c == '(') pl++;
} if(c == ')') pr++;
*w++ = c; if(c == '(' && last != '?' && !spacer(last)) *w++ = ' ';
if(last == ')' && !spacer(c)) *w++ = ' ';
*w++ = last = c;
} }
while(*(--w) <= ' ') *w = 0; while(*(--w) <= ' ') *w = 0;
fclose(f); fclose(f);
if(pr != pl)
return !fprintf(stdout, "Modal program imbalanced.\n");
while(rewrite()) while(rewrite())
if(!cycles--) return !fprintf(stdout, "Modal rewrites exceeded.\n"); 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 0; return 0;
} }