Don't ship with (old) .beams, Markdownify, subdirs, add script.
--HG--
rename : BLURB => README.markdown
rename : granolam.txt => doc/GraNoLa-M.markdown
rename : granolam.erl => src/granolam.erl
catseye
10 years ago
0 | *.beam |
0 | granolam.erl - GraNoLa/M interpreter in Erlang | |
1 | ---------------------------------------------- | |
2 | ||
3 | GraNoLa/M is a programming language in which the directed graph is the | |
4 | only data type. See granolam.txt for a more complete description of the | |
5 | GraNoLa/M language. granolam.erl is an interpreter for this language. | |
6 | ||
7 | You need an Erlang compiler at least at language version 4.4 to compile | |
8 | granola.erl. This program was developed with OTP/R8B, so that is the | |
9 | recommended platform for using it. R8B can be obtained from: | |
10 | ||
11 | http://www.erlang.org/download.html | |
12 | ||
13 | Usage: | |
14 | ||
15 | erlc granolam.erl | |
16 | erl -noshell -run granolam shell | |
17 | ||
18 | Or, to run the built-in tests: | |
19 | ||
20 | erl | |
21 | granolam:test(N). (where N is 1-7) | |
22 | ||
23 | The contents of the files in this archive BLURB, granolam.txt and | |
24 | granolam.erl are parts of the public domain. |
0 | GraNoLa/M | |
1 | ========= | |
2 | ||
3 | This is the reference distribution for the esoteric programming language | |
4 | _GraNoLa/M_. | |
5 | ||
6 | GraNoLa/M is a programming language in which the directed graph is the | |
7 | only data type. See the file `GraNoLa-M.markdown` in the `doc` directory | |
8 | for a more complete description of the GraNoLa/M language. | |
9 | ||
10 | This distribution also contains an interpreter for GraNoLa/M written in | |
11 | Erlang, as `granolam.erl` in the `src` directory. | |
12 | ||
13 | You need an Erlang compiler at least at language version 4.4 to compile | |
14 | granola.erl. This program was developed with OTP/R8B, so that is the | |
15 | recommended platform for using it, although more recent versions should | |
16 | work as well. | |
17 | ||
18 | To build the `granolam` module, run the script `make.sh`. | |
19 | ||
20 | After the module is built, run the script `granolam` in the `bin` directory | |
21 | to start a GraNoLa/M shell. | |
22 | ||
23 | To run the built-in test cases, start an Erlang shell and run | |
24 | ||
25 | granolam:test(N). | |
26 | ||
27 | where N is an integer from 1 to 7. |
0 | GraNoLa/M - Graph Node Language Mark "M" | |
1 | ======================================== | |
2 | ||
3 | Introduction | |
4 | ------------ | |
5 | ||
6 | GraNoLa/M is a programming language that owes much of its heritage to | |
7 | Tamerlane and Q-BAL, but hints of BASIC, LISP, FORTH, SETL, Muriel, and | |
8 | Aardappel can be detected in faint outline. It widely believed to be a | |
9 | subset of a much larger, PL/I-like language called 'GraNoLa/88800'. | |
10 | ||
11 | Data Types | |
12 | ---------- | |
13 | ||
14 | The basic data type in GraNoLa/M is the directed graph. Each vertex of | |
15 | the graph (called a node) can be labelled and can contain a datum. This | |
16 | datum is itself a graph, and thus graphs (and the namespaces which their | |
17 | labels make up) can be nested. A graph is not the same thing as a drum. | |
18 | ||
19 | Each node is defined by its name, which must not be used elsewhere in | |
20 | this graph, and by a list of edges, connecting to either further new | |
21 | node definitions, or backreferences to previous node definitions. Note | |
22 | that each node has an (ordered) list of edges and not just a set of | |
23 | edges; we make no pretense of these being 'proper' graphs. | |
24 | ||
25 | Actually, that different nodes have unique names and that backreferences | |
26 | must be to existing nodes are mere convention, as well. However, we | |
27 | define here that a graph in which two nodes share the same label will | |
28 | produce undefined behaviour. A single backreference to a non-existent | |
29 | node is a legal graph though, and this special case (called a nub) is | |
30 | regarded differently in certain roles, usually something akin to an | |
31 | 'atom' in certain other languages. | |
32 | ||
33 | Syntax | |
34 | ------ | |
35 | ||
36 | The following EBNF exemplifies the simplicity of the grammar for this | |
37 | data type: | |
38 | ||
39 | Graph ::= "^" ExtantName | NewName ["=" Graph] "(" {Graph} ")". | |
40 | ||
41 | That is, the syntactic representation of a graph starts with either a | |
42 | caret followed by an existing label in the graph, or it starts with a | |
43 | new label, optionally followed by an equals sign and a graph (to be | |
44 | embedded,) followed by an open paren, any number of graphs (to connect | |
45 | to), and a close paren. | |
46 | ||
47 | So, some example GraNoLa/M graphs are: | |
48 | ||
49 | a(b(^a)c(^a)d(^a)e(^a)) | |
50 | a(b(c(d(e(^a))))) | |
51 | a=a()(b=a(b())(^a)) | |
52 | a=b=c=d=e()()()()(^a) | |
53 | ^potrzebie | |
54 | a=^#potrzebie(b=^uwaming(^a)) | |
55 | ||
56 | Semantics | |
57 | --------- | |
58 | ||
59 | All GraNoLa/M operations work on graphs. Actually they work on an | |
60 | internal stack of graphs - actually the stack is nothing more than a | |
61 | graph, but to avoid (and cause) confusion, we will call it a stack, | |
62 | because mainly we are concerned with putting things into it and getting | |
63 | things off of it. | |
64 | ||
65 | In truth, there is a cursor which tells us where, in graph, we should be | |
66 | pushing and popping things. | |
67 | ||
68 | Pushing a graph onto the stack entails that we add the graph, as a node, | |
69 | to a new edge in the current node in the stack, named by the cursor. | |
70 | ||
71 | Popping a graph from the stack entails that we remove the last edge | |
72 | (remember, it's an ordered list) from the current node named by the | |
73 | cursor. | |
74 | ||
75 | Execution | |
76 | --------- | |
77 | ||
78 | A GraNoLa/M program is a graph. For this reason the syntax of a legal | |
79 | GraNoLa/M program is the same as the syntax for a graph, already given. | |
80 | ||
81 | Embedded graphs within a program graph can be thought of as subprograms | |
82 | or data, depending on whether they are executed or not. | |
83 | ||
84 | Execution of a graph begins at the outermost (first defined) node. (That | |
85 | would be node `a` in most of the examples given above.) | |
86 | ||
87 | At each node, if there is an embedded graph, it is executed (in its own | |
88 | context - it uses the same stack but it has it's own set of labels.) | |
89 | ||
90 | When a nub is embedded in a node, it specifies an operation to perform | |
91 | when executed, as in the case of the example `b=^uwaming(...)` above. | |
92 | ||
93 | Execution then passes to another node. An edge is picked by the | |
94 | traversal method (random, first, or last) and the node at the other end | |
95 | of the edge becomes the new current node. The process repeats until a | |
96 | degenerate node (with no outgoing edges) is encountered - this halts | |
97 | execution of this (sub)program, returning to the parent program (if | |
98 | there is one.) | |
99 | ||
100 | Operations | |
101 | ---------- | |
102 | ||
103 | * #label - push a nub onto the stack | |
104 | * 0label - push an empty graph (node) onto the stack | |
105 | * 1label - copy node with label from program onto stack | |
106 | * @label - set the cursor to label | |
107 | * whebong - push current (sub)program onto stack | |
108 | * duronilt - pop graph and replace current (sub)program with it | |
109 | * chehy - pop graph off of stack and use it as new stack | |
110 | * taug - push stack onto stack embedded into a new node | |
111 | * soduv - pop a graph, set execution order to first if it is empty, last if not | |
112 | * rehohur - pop and discard graph | |
113 | * bimodang - pop label and jump to it as subroutine in current program graph | |
114 | * ubewic - return from current subroutine (jump back to last bimodang) | |
115 | * chuwakagathaz - switch to nondeterministic execution order (default) | |
116 | * sajalom - deterministic execution order - use first edge | |
117 | * grangnum - deterministic execution order - use last edge | |
118 | * uwaming - pop a graph off the stack and print it | |
119 | * bejadoz - input a graph (in GraNoLa/M syntax) and push it on the stack | |
120 |
0 | -module(granolam). | |
1 | -vsn('2002.0314'). % This work is a part of the public domain. | |
2 | ||
3 | -export([parse/1, interpret/1, run/1, test/1, shell/0]). | |
4 | ||
5 | -define(g0(X),X++S)->" "++X++" "++sub(S). | |
6 | -define(g1(X),X|R0])->{Q,R1}=). | |
7 | -define(g2(X),{S0,X}=pop(C,S)). | |
8 | -define(g3(X),S0=push(C,S,X)). | |
9 | -define(g9,++K->S0=push(C,S). | |
10 | -define(y,S,C,CS). | |
11 | -define(t,C,CS,M). | |
12 | -define(t0,L,P,S0). | |
13 | -define(l(X),list_to_atom(X)). | |
14 | ||
15 | %% Parser -------------------------------------------------------------- | |
16 | ||
17 | sub(?g0("("));sub(?g0(")"));sub(?g0("^"));sub(?g0("=")); | |
18 | sub([C|S])->[C]++sub(S);sub([])->[]. | |
19 | lta([])->[];lta([H|T])->[?l(H)|lta(T)]. | |
20 | parse(S)->{P,R}=graph(lta(string:tokens(sub(S)," "))),P. | |
21 | graph(['^',N|R0])->{N,R0}; | |
22 | graph([N,?g1('=')graph(R0),['('|R2]=R1,{Q2,R3}= | |
23 | graph2(R2,[]),{{N,Q,Q2},R3}; | |
24 | graph([N,?g1('(')graph2(R0,[]),{{N,nil,Q},R1}. | |
25 | graph2([')'|R0],A)->{A,R0}; | |
26 | graph2(R,A)->{Q,R0}=graph(R),graph2(R0,A++[Q]). | |
27 | ||
28 | %% Interpreter --------------------------------------------------------- | |
29 | ||
30 | interpret(P)->interpret(first(P),P,{stack,nil,[]},stack,[],random). | |
31 | interpret(nil, P,?y,M)->{?y}; | |
32 | interpret(stop, P,?y,M)->{?y}; | |
33 | interpret(N, P,?y,M)-> | |
34 | case find(N,P) of | |
35 | {N,nil,L} -> | |
36 | interpret(pick(L,M),P,?y,M); | |
37 | {N,V,L} when atom(V)-> | |
38 | {N0,P0,S0,C0,CS0,M0} = do(N,V,P,?y,M), | |
39 | {N1,V1,L1}=find(N0,P0), | |
40 | case N0 of | |
41 | N->interpret(pick(L1,M0),P0,S0,C0,CS0,M0); | |
42 | _->interpret(N0,P0,S0,C0,CS0,M0) | |
43 | end; | |
44 | {N,V,L} -> | |
45 | {S0,C0,CS0}=interpret(first(V),V,?y,M), | |
46 | interpret(pick(L,M),P,S0,C0,CS0,M); | |
47 | _ -> | |
48 | {?y} | |
49 | end. | |
50 | ||
51 | do(L,V,P,?y,M) -> | |
52 | case V of | |
53 | uwaming->?g2(Q),io:fwrite("~s ",[format(Q)]),{?t0,?t}; | |
54 | bejadoz->?g3(input()),{?t0,?t}; | |
55 | duronilt->?g2(Q),{L,Q,S0,?t}; | |
56 | whebong->?g3(P),{?t0,?t}; | |
57 | taug->?g3({embed,S,[]}),{?t0,?t}; | |
58 | chehy->?g2(Q),{L,P,Q,?t}; | |
59 | bimodang->?g2(Q),case Q of | |
60 | skip->{?t0,?t}; | |
61 | _->{Q,P,S0,C,[L|CS],M}end; | |
62 | ubewic->?g3(skip),[H|T]=CS,{H,P,S0,C,T,M}; | |
63 | rehohur->?g2(Q),{?t0,?t}; | |
64 | soduv->?g2(Q),case Q of | |
65 | {_,_,[]} ->{?t0,C,CS,first}; | |
66 | {_,_,[_|_]}->{?t0,C,CS,last}; | |
67 | _->{?t0,?t}end; | |
68 | chuwakagathaz->{L,P,?y,random}; | |
69 | sajalom->{L,P,?y,first}; | |
70 | grangnum->{L,P,?y,last}; | |
71 | _ -> | |
72 | case atom_to_list(V) of | |
73 | "#"?g9,?l(K)),{?t0,?t}; | |
74 | "0"?g9,{?l(K),nil,[]}),{?t0,?t}; | |
75 | "1"?g9,find(?l(K),P)),{?t0,?t}; | |
76 | "@"++C0->{L,P,S,?l(C0),CS,M}; | |
77 | _->{L,P,?y,M} | |
78 | end | |
79 | end. | |
80 | ||
81 | %% Utilities ------------------------------------------------------------ | |
82 | ||
83 | first({N,S,L})->N;first(A)->A. pick([],_)->stop;pick([H|_],first)->first(H); | |
84 | pick(L,last)->first(lists:nth(length(L),L));pick(L,random)->first(lists:nth( | |
85 | random:uniform(length(L)),L)). find(N,[])->false;find(N,[H|T])->case find(N,H) | |
86 | of false->find(N,T);V->V end;find(N,{N,S,L}=P)->P;find(N,{O,S,L})->find(N,L); | |
87 | find(N,_)->false. replace(N,[],G)->[];replace(N,[H|T],G)->[replace(N,H,G)| | |
88 | replace(N,T,G)];replace(N,{N,S,L}=P,G)->G;replace(N,{O,S,L},G)->replace(N,L,G); | |
89 | replace(N,V,G)->V. push(C,S,G)->{N,D,L}=find(C,S),replace(C,S,{N,D,[G|L]}). pop | |
90 | (C, S)->case find(C,S) of{N,D,[]}->{S,nil};{N,D,[H|T]}->{replace(C,S,{N,D,T}), | |
91 | H}end. format([])->"";format(A) when atom(A)->"^"++atom_to_list(A);format([H|T]) | |
92 | ->format(H)++format(T);format({N,nil,L})->io_lib:format("~w(~s)",[N,format(L)]) | |
93 | ;format({N,V,L})->io_lib:format("~w=~s(~s)",[N,format(V),format(L)]);format(_) | |
94 | ||
95 | ->"?". | |
96 | ||
97 | %% User Interface ------------------------------------------------------ | |
98 | ||
99 | input()->parse(lib:nonl(io:get_line('GraNoLa/M> '))). | |
100 | run(S)->interpret(parse(S)). | |
101 | test(1)->run("a=^#cthulhu(b=^uwaming(^a))"); | |
102 | test(2)->run("a=^whebong(b=^uwaming(^a))"); | |
103 | test(4)->run("a=^0hello(b=^@hello(c=^taug(d=^uwaming(^a))))"); | |
104 | test(5)->run("a=^1hello(b=^uwaming(end=() hello=(world())))"); | |
105 | test(6)->run("a=^sajalom(b=^#d(c=^bimodang(^a))" | |
106 | "d(e=^#sakura(f=^uwaming(g=^ubewic()))))"); | |
107 | test(7)->run("a=^sajalom(b=^bejadoz(c=^soduv(^a d())))"); | |
108 | test(_)->unknown_test. | |
109 | shell()->{?y}=interpret(input()),io:fwrite("~s@~w~n",[format(S),C]), | |
110 | shell(). |
0 | GraNoLa/M - Graph Node Language Mark "M" | |
1 | ---------------------------------------- | |
2 | ||
3 | Introduction | |
4 | ------------ | |
5 | ||
6 | GraNoLa/M is a programming language that owes much of its heritage to | |
7 | Tamerlane and Q-BAL, but hints of BASIC, LISP, FORTH, SETL, Muriel, and | |
8 | Aardappel can be detected in faint outline. It widely believed to be a | |
9 | subset of a much larger, PL/I-like language called 'GraNoLa/88800'. | |
10 | ||
11 | Data Types | |
12 | ---------- | |
13 | ||
14 | The basic data type in GraNoLa/M is the directed graph. Each vertex of | |
15 | the graph (called a node) can be labelled and can contain a datum. This | |
16 | datum is itself a graph, and thus graphs (and the namespaces which their | |
17 | labels make up) can be nested. A graph is not the same thing as a drum. | |
18 | ||
19 | Each node is defined by its name, which must not be used elsewhere in | |
20 | this graph, and by a list of edges, connecting to either further new | |
21 | node definitions, or backreferences to previous node definitions. Note | |
22 | that each node has an (ordered) list of edges and not just a set of | |
23 | edges; we make no pretense of these being 'proper' graphs. | |
24 | ||
25 | Actually, that different nodes have unique names and that backreferences | |
26 | must be to existing nodes are mere convention, as well. However, we | |
27 | define here that a graph in which two nodes share the same label will | |
28 | produce undefined behaviour. A single backreference to a non-existent | |
29 | node is a legal graph though, and this special case (called a nub) is | |
30 | regarded differently in certain roles, usually something akin to an | |
31 | 'atom' in certain other languages. | |
32 | ||
33 | Syntax | |
34 | ------ | |
35 | ||
36 | The following EBNF exemplifies the simplicity of the grammar for this | |
37 | data type: | |
38 | ||
39 | Graph ::= "^" ExtantName | NewName ["=" Graph] "(" {Graph} ")". | |
40 | ||
41 | That is, the syntactic representation of a graph starts with either a | |
42 | caret followed by an existing label in the graph, or it starts with a | |
43 | new label, optionally followed by an equals sign and a graph (to be | |
44 | embedded,) followed by an open paren, any number of graphs (to connect | |
45 | to), and a close paren. | |
46 | ||
47 | So, some example GraNoLa/M graphs are: | |
48 | ||
49 | a(b(^a)c(^a)d(^a)e(^a)) | |
50 | a(b(c(d(e(^a))))) | |
51 | a=a()(b=a(b())(^a)) | |
52 | a=b=c=d=e()()()()(^a) | |
53 | ^potrzebie | |
54 | a=^#potrzebie(b=^uwaming(^a)) | |
55 | ||
56 | Semantics | |
57 | --------- | |
58 | ||
59 | All GraNoLa/M operations work on graphs. Actually they work on an | |
60 | internal stack of graphs - actually the stack is nothing more than a | |
61 | graph, but to avoid (and cause) confusion, we will call it a stack, | |
62 | because mainly we are concerned with putting things into it and getting | |
63 | things off of it. | |
64 | ||
65 | In truth, there is a cursor which tells us where, in graph, we should be | |
66 | pushing and popping things. | |
67 | ||
68 | Pushing a graph onto the stack entails that we add the graph, as a node, | |
69 | to a new edge in the current node in the stack, named by the cursor. | |
70 | ||
71 | Popping a graph from the stack entails that we remove the last edge | |
72 | (remember, it's an ordered list) from the current node named by the | |
73 | cursor. | |
74 | ||
75 | Execution | |
76 | --------- | |
77 | ||
78 | A GraNoLa/M program is a graph. For this reason the syntax of a legal | |
79 | GraNoLa/M program is the same as the syntax for a graph, already given. | |
80 | ||
81 | Embedded graphs within a program graph can be thought of as subprograms | |
82 | or data, depending on whether they are executed or not. | |
83 | ||
84 | Execution of a graph begins at the outermost (first defined) node. (That | |
85 | would be node 'a' in most of the examples given above.) | |
86 | ||
87 | At each node, if there is an embedded graph, it is executed (in its own | |
88 | context - it uses the same stack but it has it's own set of labels.) | |
89 | ||
90 | When a nub is embedded in a node, it specifies an operation to perform | |
91 | when executed, as in the case of the example "b=^uwaming(...)" above. | |
92 | ||
93 | Execution then passes to another node. An edge is picked by the | |
94 | traversal method (random, first, or last) and the node at the other end | |
95 | of the edge becomes the new current node. The process repeats until a | |
96 | degenerate node (with no outgoing edges) is encountered - this halts | |
97 | execution of this (sub)program, returning to the parent program (if | |
98 | there is one.) | |
99 | ||
100 | Operations | |
101 | ---------- | |
102 | ||
103 | * #label - push a nub onto the stack | |
104 | * 0label - push an empty graph (node) onto the stack | |
105 | * 1label - copy node with label from program onto stack | |
106 | * @label - set the cursor to label | |
107 | * whebong - push current (sub)program onto stack | |
108 | * duronilt - pop graph and replace current (sub)program with it | |
109 | * chehy - pop graph off of stack and use it as new stack | |
110 | * taug - push stack onto stack embedded into a new node | |
111 | * soduv - pop a graph, set execution order to first if it is empty, last if not | |
112 | * rehohur - pop and discard graph | |
113 | * bimodang - pop label and jump to it as subroutine in current program graph | |
114 | * ubewic - return from current subroutine (jump back to last bimodang) | |
115 | * chuwakagathaz - switch to nondeterministic execution order (default) | |
116 | * sajalom - deterministic execution order - use first edge | |
117 | * grangnum - deterministic execution order - use last edge | |
118 | * uwaming - pop a graph off the stack and print it | |
119 | * bejadoz - input a graph (in GraNoLa/M syntax) and push it on the stack | |
120 |
0 | #!/bin/sh | |
1 | ||
2 | if [ ! -d ebin ]; then | |
3 | mkdir ebin | |
4 | fi | |
5 | for FILE in src/*.erl; do | |
6 | erlc -o ebin $FILE | |
7 | done |
0 | -module(granolam). | |
1 | -vsn('2002.0314'). % This work is a part of the public domain. | |
2 | ||
3 | -export([parse/1, interpret/1, run/1, test/1, shell/0]). | |
4 | ||
5 | -define(g0(X),X++S)->" "++X++" "++sub(S). | |
6 | -define(g1(X),X|R0])->{Q,R1}=). | |
7 | -define(g2(X),{S0,X}=pop(C,S)). | |
8 | -define(g3(X),S0=push(C,S,X)). | |
9 | -define(g9,++K->S0=push(C,S). | |
10 | -define(y,S,C,CS). | |
11 | -define(t,C,CS,M). | |
12 | -define(t0,L,P,S0). | |
13 | -define(l(X),list_to_atom(X)). | |
14 | ||
15 | %% Parser -------------------------------------------------------------- | |
16 | ||
17 | sub(?g0("("));sub(?g0(")"));sub(?g0("^"));sub(?g0("=")); | |
18 | sub([C|S])->[C]++sub(S);sub([])->[]. | |
19 | lta([])->[];lta([H|T])->[?l(H)|lta(T)]. | |
20 | parse(S)->{P,R}=graph(lta(string:tokens(sub(S)," "))),P. | |
21 | graph(['^',N|R0])->{N,R0}; | |
22 | graph([N,?g1('=')graph(R0),['('|R2]=R1,{Q2,R3}= | |
23 | graph2(R2,[]),{{N,Q,Q2},R3}; | |
24 | graph([N,?g1('(')graph2(R0,[]),{{N,nil,Q},R1}. | |
25 | graph2([')'|R0],A)->{A,R0}; | |
26 | graph2(R,A)->{Q,R0}=graph(R),graph2(R0,A++[Q]). | |
27 | ||
28 | %% Interpreter --------------------------------------------------------- | |
29 | ||
30 | interpret(P)->interpret(first(P),P,{stack,nil,[]},stack,[],random). | |
31 | interpret(nil, P,?y,M)->{?y}; | |
32 | interpret(stop, P,?y,M)->{?y}; | |
33 | interpret(N, P,?y,M)-> | |
34 | case find(N,P) of | |
35 | {N,nil,L} -> | |
36 | interpret(pick(L,M),P,?y,M); | |
37 | {N,V,L} when atom(V)-> | |
38 | {N0,P0,S0,C0,CS0,M0} = do(N,V,P,?y,M), | |
39 | {N1,V1,L1}=find(N0,P0), | |
40 | case N0 of | |
41 | N->interpret(pick(L1,M0),P0,S0,C0,CS0,M0); | |
42 | _->interpret(N0,P0,S0,C0,CS0,M0) | |
43 | end; | |
44 | {N,V,L} -> | |
45 | {S0,C0,CS0}=interpret(first(V),V,?y,M), | |
46 | interpret(pick(L,M),P,S0,C0,CS0,M); | |
47 | _ -> | |
48 | {?y} | |
49 | end. | |
50 | ||
51 | do(L,V,P,?y,M) -> | |
52 | case V of | |
53 | uwaming->?g2(Q),io:fwrite("~s ",[format(Q)]),{?t0,?t}; | |
54 | bejadoz->?g3(input()),{?t0,?t}; | |
55 | duronilt->?g2(Q),{L,Q,S0,?t}; | |
56 | whebong->?g3(P),{?t0,?t}; | |
57 | taug->?g3({embed,S,[]}),{?t0,?t}; | |
58 | chehy->?g2(Q),{L,P,Q,?t}; | |
59 | bimodang->?g2(Q),case Q of | |
60 | skip->{?t0,?t}; | |
61 | _->{Q,P,S0,C,[L|CS],M}end; | |
62 | ubewic->?g3(skip),[H|T]=CS,{H,P,S0,C,T,M}; | |
63 | rehohur->?g2(Q),{?t0,?t}; | |
64 | soduv->?g2(Q),case Q of | |
65 | {_,_,[]} ->{?t0,C,CS,first}; | |
66 | {_,_,[_|_]}->{?t0,C,CS,last}; | |
67 | _->{?t0,?t}end; | |
68 | chuwakagathaz->{L,P,?y,random}; | |
69 | sajalom->{L,P,?y,first}; | |
70 | grangnum->{L,P,?y,last}; | |
71 | _ -> | |
72 | case atom_to_list(V) of | |
73 | "#"?g9,?l(K)),{?t0,?t}; | |
74 | "0"?g9,{?l(K),nil,[]}),{?t0,?t}; | |
75 | "1"?g9,find(?l(K),P)),{?t0,?t}; | |
76 | "@"++C0->{L,P,S,?l(C0),CS,M}; | |
77 | _->{L,P,?y,M} | |
78 | end | |
79 | end. | |
80 | ||
81 | %% Utilities ------------------------------------------------------------ | |
82 | ||
83 | first({N,S,L})->N;first(A)->A. pick([],_)->stop;pick([H|_],first)->first(H); | |
84 | pick(L,last)->first(lists:nth(length(L),L));pick(L,random)->first(lists:nth( | |
85 | random:uniform(length(L)),L)). find(N,[])->false;find(N,[H|T])->case find(N,H) | |
86 | of false->find(N,T);V->V end;find(N,{N,S,L}=P)->P;find(N,{O,S,L})->find(N,L); | |
87 | find(N,_)->false. replace(N,[],G)->[];replace(N,[H|T],G)->[replace(N,H,G)| | |
88 | replace(N,T,G)];replace(N,{N,S,L}=P,G)->G;replace(N,{O,S,L},G)->replace(N,L,G); | |
89 | replace(N,V,G)->V. push(C,S,G)->{N,D,L}=find(C,S),replace(C,S,{N,D,[G|L]}). pop | |
90 | (C, S)->case find(C,S) of{N,D,[]}->{S,nil};{N,D,[H|T]}->{replace(C,S,{N,D,T}), | |
91 | H}end. format([])->"";format(A) when atom(A)->"^"++atom_to_list(A);format([H|T]) | |
92 | ->format(H)++format(T);format({N,nil,L})->io_lib:format("~w(~s)",[N,format(L)]) | |
93 | ;format({N,V,L})->io_lib:format("~w=~s(~s)",[N,format(V),format(L)]);format(_) | |
94 | ||
95 | ->"?". | |
96 | ||
97 | %% User Interface ------------------------------------------------------ | |
98 | ||
99 | input()->parse(lib:nonl(io:get_line('GraNoLa/M> '))). | |
100 | run(S)->interpret(parse(S)). | |
101 | test(1)->run("a=^#cthulhu(b=^uwaming(^a))"); | |
102 | test(2)->run("a=^whebong(b=^uwaming(^a))"); | |
103 | test(4)->run("a=^0hello(b=^@hello(c=^taug(d=^uwaming(^a))))"); | |
104 | test(5)->run("a=^1hello(b=^uwaming(end=() hello=(world())))"); | |
105 | test(6)->run("a=^sajalom(b=^#d(c=^bimodang(^a))" | |
106 | "d(e=^#sakura(f=^uwaming(g=^ubewic()))))"); | |
107 | test(7)->run("a=^sajalom(b=^bejadoz(c=^soduv(^a d())))"); | |
108 | test(_)->unknown_test. | |
109 | shell()->{?y}=interpret(input()),io:fwrite("~s@~w~n",[format(S),C]), | |
110 | shell(). |