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
-v Print version
-q Quiet mode, no step printing
-n Infinite mode, no rewrites limit
```
## 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.)
?(?-) (Inline rules)
?(?-) (Early Test Primitives)
<> ((?x -> ?y)) (<> ?x ?y)
(nap -> (tap =))
<> (?x = ?x ?n test) (?(?: ?:) (#pass ?n\n))
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)
<> (?x pop-plain)
<> (?x pop) ()
<> (ghost) ()
(ab cd () ghost) = (ab cd ()) (empty 1) test
(ab cd ghost ()) = (ab cd ()) (empty 2) test
(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
abc def pop = abc test
(ghost) = () test
(q ?((?x ?y ?z) (?x ?y)) a b c) = (q a b) (empty 6) test
(q ?((?x ?y ?z) (?x ?z)) a b c) = (q a c) (empty 7) 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) =)
replace-name (foo) (bar) test
(ab prefix/pop cd ef) = (ab ef) (empty 9) test
(ab cd suffix/pop ef) = (ab ef) (empty 10) test
?(?-) (Basic register setups)
<> (dup (?x)) ((?x ?x) =)
<> (swap (?x ?y)) ((?y ?x) =)
<> (compare (?x ?x ?x)) ((#t) =)
<> (dup (?x)) (?x ?x)
<> (swap (?x ?y)) (?y ?x)
<> (compare (?x ?x ?x)) (#t)
<> (rotate (?x (?y (?z)))) (?y (?z (?x)))
<> (unused ?x) ?y
dup (abc) (abc abc) test
swap (abc def) (def abc) test
compare (abc abc abc) (#t) test
(dup (abc)) = (abc abc) (basic 1) test
(swap (abc def)) = (def abc) (basic 2) 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
?(?-) (Guards setups)
<> (join (String ?x) (String ?y)) ((?x ?y) =)
join (String abc) (String def) (abc def) test
connect foo bar baz = foo-barbaz (substring 1) test
prefix-anything = anything-suffix (substring 2) test
?(foo QQQ) foobar = foobar (substring 3) test
?(?-) (Lambdas)
?((?x) ((?x ?x) =)) abc (abc abc) test
abc ?(?x) def = abc test
(?(?x (?x ?x)) abc) = (abc abc) (lambda 1) 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
explode (12 34 45) (12 (34 (45 ()))) test
?(?-) (op: join)
?(?-) (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
implode (12 (34 (56 ()))) 123456 test
?(?-) (Test Primitives)
<> (?: print) (?:)
<> (?x = ?x test) (#ok)
<> (?x = ?y test) (#fail)
?(?. ?.) (abcd) = abcd (unwrap 1) test
(?(?. ?.) ()) = () (unwrap empty) test
?(?-) (List reversal)
@ -76,5 +84,15 @@ implode (12 (34 (56 ()))) 123456 test
<> (reverse (?*)) (reverse List (?*) ())
<> (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:
@ mkdir -p bin
run: all bin/modal
@ bin/modal examples/hello.modal 2> /dev/null
debug: bin/modal-debug
@ bin/modal -q examples/hello.modal
debug: all bin/modal-debug
@ bin/modal-debug examples/hello.modal
test: bin/modal-debug bin/modal
test: all bin/modal-debug bin/modal
@ 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
cp bin/modal ~/bin/
uninstall:

View File

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