git @ Cat's Eye Technologies GraNoLa-M / 3814543
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
10 changed file(s) with 274 addition(s) and 257 deletion(s). Raw diff Collapse all Expand all
0 *.beam
0 syntax: glob
1
2 *.beam
+0
-25
BLURB less more
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 #!/bin/sh
1 ERL_LIBS=`dirname $0`/../../ erl -noshell -run granolam shell -run init stop
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
-111
granolam.erl less more
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
-121
granolam.txt less more
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().