From f2688e311fd6808aa4f97a7f74d34bdbae807b02 Mon Sep 17 00:00:00 2001 From: sforman Date: Tue, 25 Jul 2023 08:51:27 -0700 Subject: [PATCH] Messing around with GNU Prolog. I have it broken up into three stages: a parser that reads a string from stdin and emits (Prolog) AST to stdout; an interpreter of sorts that reads AST from stdin, evaluates it, and then emits the AST of the stack on stdout; and a printer that reads AST on stdin and prints Joy-ish code to stdout. I say Joy-ish because currently math is not evaluated and results of math appear as expressions, not values. This is because GNU Prolog doesn't have unbounded integers (it's numbers are machine integers) so literals that are larger than the machine word are converted into atoms! To keep things simple, I made all ints into atoms, but then you can't evaluate them: '1'+'2' is not '3' (it might be '12' though.) So I print them out at expressions: $ echo "1 2 3 4 [+ sub /] i" | ./joy_to_ast | ./thun | ./printer (1 div (2-(4+3))) You could almost feed that to, say, Python to evaluate, eh? Or dc with proper formatting? (man dc; "Desk Calculator".) Anyway, it's a start. The Prolog interpreter is more for things like type checking and inference, optimizing, compiling, etc. Symbolic stuff that's a PITA to express in other languages. (The old type inference code in Python was pages long, in Prolog it's just the thun/3 & thun/4 predicates themselves. At least so far. There are things we will want to do eventually that might be a PITA to express in Prolog, eh? --- implementations/GNUProlog/Makefile | 12 +++++ implementations/GNUProlog/joy_to_ast.prolog | 18 +++++-- implementations/GNUProlog/printer.prolog | 25 ++++++++++ implementations/GNUProlog/thun.prolog | 73 +++++++++++++++++++++++++++++ 4 files changed, 124 insertions(+), 4 deletions(-) create mode 100644 implementations/GNUProlog/Makefile create mode 100644 implementations/GNUProlog/printer.prolog create mode 100644 implementations/GNUProlog/thun.prolog diff --git a/implementations/GNUProlog/Makefile b/implementations/GNUProlog/Makefile new file mode 100644 index 0000000..2448c3c --- /dev/null +++ b/implementations/GNUProlog/Makefile @@ -0,0 +1,12 @@ + +all: joy_to_ast thun printer + +joy_to_ast: joy_to_ast.prolog + gplc --no-top-level joy_to_ast.prolog + +thun: thun.prolog + gplc --no-top-level thun.prolog + +printer: printer.prolog + gplc --no-top-level printer.prolog + diff --git a/implementations/GNUProlog/joy_to_ast.prolog b/implementations/GNUProlog/joy_to_ast.prolog index 7219cc4..1a0fde3 100644 --- a/implementations/GNUProlog/joy_to_ast.prolog +++ b/implementations/GNUProlog/joy_to_ast.prolog @@ -1,5 +1,4 @@ - stdin_to_codes(Codes) :- % Pass in and discard atom 'code' to prime stdin_to_codes/2. stdin_to_codes(code, [code|Codes]). @@ -9,6 +8,7 @@ stdin_to_codes(Code, [Code|Codes]) :- get_code(NextCode), stdin_to_codes(NextCode, Codes). + % %joy(InputString, StackIn, StackOut) :- % text_to_expression(InputString, Expression), @@ -34,7 +34,7 @@ joy_parse([]) --> []. joy_term(list(J)) --> [lbracket], !, joy_parse(J), [rbracket]. joy_term(Token) --> [tok(Codes)], {joy_token(Token, Codes)}. -joy_token(int(I), Codes) :- catch(number_codes(I, Codes), _Err, fail), !. +joy_token(int(I), Codes) :- numeric(Codes), atom_codes(I, Codes), !. joy_token(bool(true), "true") :- !. joy_token(bool(false), "false") :- !. joy_token(symbol(S), Codes) :- atom_codes(S, Codes). @@ -51,6 +51,16 @@ chars([Ch]) --> char(Ch). char(Ch) --> \+ blank, [Ch], { Ch \== 0'[, Ch \== 0'] }. +numeric(Codes) :- digits(Codes, []), !. +numeric([45,FirstDigit|Codes]) :- digit(FirstDigit), digits(Codes, []), !. +% ASCII 45 is '-'. + +digits --> digit, digits. +digits --> []. + +digit --> [Code], { digit(Code) }. + +digit(Code) :- between(0'0, 0'9, Code). blank --> [9]. blank --> [10]. @@ -82,5 +92,5 @@ blank --> [227, 128, 128]. :- initialization(( stdin_to_codes(Codes), text_to_expression(Codes, Expr), - print(Expr), print('\n') - )). \ No newline at end of file + write_term(Expr, [quoted(true)]), print('\n') + )). diff --git a/implementations/GNUProlog/printer.prolog b/implementations/GNUProlog/printer.prolog new file mode 100644 index 0000000..c873ad0 --- /dev/null +++ b/implementations/GNUProlog/printer.prolog @@ -0,0 +1,25 @@ + +format_joy_expression( int(I)) --> { integer(I), !, number_codes(I, Codes) }, Codes. +format_joy_expression( int(I)) --> { atom(I), !, atom_codes(I, Codes) }, Codes. +format_joy_expression( int(I)) --> { write_to_codes(Codes, I) }, [40], Codes, [41]. +format_joy_expression( bool(B)) --> { atom_codes(B, Codes) }, Codes. +format_joy_expression(symbol(S)) --> { atom_codes(S, Codes) }, Codes. +format_joy_expression(symbol(S)) --> { atom_codes(S, Codes) }, Codes. +format_joy_expression( list(J)) --> "[", format_joy_terms(J), "]". + +format_joy_terms( []) --> []. +format_joy_terms( [T]) --> format_joy_expression(T), !. +format_joy_terms([T|Ts]) --> format_joy_expression(T), " ", format_joy_terms(Ts). + +codes_to_stream([Code|Codes], Stream) :- + put_code(Stream, Code), !, + codes_to_stream(Codes, Stream). +codes_to_stream([], _). + +:- initialization(( + read_term(AST, [end_of_term(eof)]), + format_joy_terms(AST, Codes, []), + codes_to_stream(Codes, user_output), print('\n') + )). + + diff --git a/implementations/GNUProlog/thun.prolog b/implementations/GNUProlog/thun.prolog new file mode 100644 index 0000000..2f1dc17 --- /dev/null +++ b/implementations/GNUProlog/thun.prolog @@ -0,0 +1,73 @@ + + +thun([], S, S). +thun([Term|E], Si, So) :- thun(Term, E, Si, So). + +thun(A, [], S, [A|S]) :- var(A), !. +thun(A, [T|E], S, So) :- var(A), !, thun(T, E, [A|S], So). + +thun(int(A), [], B, [int(A)|B]). +thun(int(C), [A|B], D, E) :- thun(A, B, [int(C)|D], E). + +thun(bool(A), [], B, [bool(A)|B]). +thun(bool(C), [A|B], D, E) :- thun(A, B, [bool(C)|D], E). + +thun(list(A), [], B, [list(A)|B]). +thun(list(C), [A|B], D, E) :- thun(A, B, [list(C)|D], E). + +thun(symbol(A), [], B, C) :- func(A, B, C). +thun(symbol(A), [C|D], B, F) :- func(A, B, E), thun(C, D, E, F). + +thun(symbol(Combo), E, Si, So) :- combo(Combo, Si, S, E, Eo), thun(Eo, S, So). + + +func(swap, [A, B|S], [B, A|S]). +func(dup, [A|S], [A, A|S]). +func(pop, [_|S], S ). + +func(cons, [list(A), B |S], [list([B|A])|S]). + +func(swaack, [list(R)|S], [list(S)|R]). +func(stack, S , [list(S)|S]). +func(clear, _ , []). +func(first, [list([X|_])|S], [ X |S]). +func(rest, [list([_|X])|S], [list(X)|S]). + +func(bool, [ int(0)|S], [bool(false)|S]). +func(bool, [ list([])|S], [bool(false)|S]). +func(bool, [bool(false)|S], [bool(false)|S]). + +func(bool, [ int(N)|S], [bool(true)|S]) :- N #\= 0. +func(bool, [list([_|_])|S], [bool(true)|S]). +func(bool, [ bool(true)|S], [bool(true)|S]). + +func( + , [int(A), int(B)|S], [int(A + B)|S]). +func( - , [int(A), int(B)|S], [int(B - A)|S]). +func( * , [int(A), int(B)|S], [int(A * B)|S]). +func( / , [int(A), int(B)|S], [int(B div A)|S]). +func('%', [int(A), int(B)|S], [int(B mod A)|S]). + +func( add , [int(A), int(B)|S], [int(A + B)|S]). +func( sub , [int(A), int(B)|S], [int(B - A)|S]). +func( mul , [int(A), int(B)|S], [int(A * B)|S]). +func( div , [int(A), int(B)|S], [int(B div A)|S]). +func( mod, [int(A), int(B)|S], [int(B mod A)|S]). + + +combo(i, [list(P)|S], S, Ei, Eo) :- append(P, Ei, Eo). +combo(dip, [list(P), X|S], S, Ei, Eo) :- append(P, [X|Ei], Eo). + +combo(branch, [list(T), list(_), bool(true)|S], S, Ei, Eo) :- append(T, Ei, Eo). +combo(branch, [list(_), list(F), bool(false)|S], S, Ei, Eo) :- append(F, Ei, Eo). + +combo(loop, [list(_), bool(false)|S], S, E, E ). +combo(loop, [list(B), bool(true)|S], S, Ei, Eo) :- append(B, [list(B), symbol(loop)|Ei], Eo). + + + +:- initialization(( + read_term(AST, [end_of_term(eof)]), + thun(AST, [], Stack), + write_term(Stack, [quoted(true)]), print('\n') + )). + -- 2.11.0