15 | Below you can find all functions included with Manglang. They are either built-in or implemented in the
16 | standard library.
17 |
18 |
19 |
Time Complexity
20 |
21 | For all container functions we specify how the time complexity depends on the number of elements N in the container. So an operation that is marked as O(1) takes the same time for small and large containers. An operation that is marked as O(N) takes twice as long for a container that is twice as big.
22 |
23 |
24 |
Container Interface
25 | Manglang has containers like: stacks, strings, tables. They share an interface of basic operations:
26 |
27 |
take
take!container returns a single item from the container. O(1).
28 |
drop
drop!container returns the container with a single item dropped from it. O(1).
29 |
put
put!(item container) returns a new container with item added to the old container. For tables we have that put!((key value) table) sets the value corresponding to the key in the table and returns the table. For stacks and strings the original container is not mutated, but it is mutated for tables so that other references to it are effected as well! O(1).
30 |
clear
clear!container returns an empty container of the same type as the input. O(1).
31 |
indexing
container!index can be used to get the item at the specified index. The container is interpreted as a function which takes an index as input and outputs an item. O(N).
32 |
33 | We check if a container is not empty by if container then ... else ... O(1).
34 |
35 |
36 | These operations can be used as a base for building more container functions. The standard library does that and adds all the functions below.
37 |
38 |
39 |
Generic Container Functions
40 | These functions work on both stacks, strings, tables, and the type of the output container is the same as the input.
41 | We use the word container to represent either a stack, string or table.
42 |
43 |
fold
fold!(binary_operation container init) is mainly used as an algorithmic building block to implement the other container functions. O(N).
44 |
reverse
reverse!container returns a container with the items in the reverse order for stacks and string. For tables we just get a copy, since a table is always sorted by its keys. O(N).
45 |
46 |
47 |
take
take!container returns a single item from the container. O(1).
48 |
take_many
take_many!(n container) returns a new containers with n items taken from the input container. O(n).
49 |
take_while
take_while!(predicate container) returns a new containers with items taken from the input container as long as the predicate says yes. O(N).
50 |
take_until_item
take_until_item!(item container) returns a new containers with items taken from the input container until the query item is found. O(N).
51 |
52 |
53 |
drop
drop!container returns the container with a single item dropped from it. O(1).
54 |
drop_many
drop_many!(n container) returns the container with n items dropped from it. O(n).
55 |
drop_while
drop_while!(predicate container) returns the container with items dropped from it as long as the predicate says yes. O(N).
56 |
drop_until_item
drop_until_item!(item container) returns a container with items dropped until the query item is found. O(N).
57 |
58 |
59 |
clear
clear!container returns an empty container of the same type as the input. O(1).
60 |
clear_item
clear_item!(item container) returns a container with all occurances of the specified item removed. O(N).
61 |
clear_if
clear_if!(predicate container) returns a container without the items for which the predicate says yes. O(N).
62 |
63 |
64 |
replace
replace!(new_item container) returns a container where each item is replaced with new_item. O(N).
65 |
replace_item
replace_item!(old_item new_item container) returns a container with occurances of old_item replaced with new_item. O(N).
66 |
replace_if
replace_if!(predicate new_item container) returns a container where each item is replaced if the predicate says yes. O(N).
67 |
68 |
69 |
count
count!container returns the number of items in the container. O(N).
70 |
count_item
count!(item container) counts the number of occurances of an item in the container. O(N).
71 |
count_if
count_if!(predicate container) counts the number of items for which the predicate says yes. O(N).
72 |
73 |
74 |
Generic Container Functions - Accessing Items
75 | You can access the items of a container via take!container and drop!container and container!index, or using the following functions:
76 |
77 |
get0
get0!container returns the item with index 0 in the container. O(1).
78 |
get1
get1!container returns the item with index 1 in the container. O(1).
79 |
get2
get2!container returns the item with index 2 in the container. O(1).
80 |
get3
get3!container returns the item with index 3 in the container. O(1).
81 |
get4
get4!container returns the item with index 4 in the container. O(1).
82 |
get5
get5!container returns the item with index 5 in the container. O(1).
83 |
get6
get6!container returns the item with index 6 in the container. O(1).
84 |
get7
get7!container returns the item with index 7 in the container. O(1).
85 |
get8
get8!container returns the item with index 8 in the container. O(1).
86 |
get9
get9!container returns the item with index 9 in the container. O(1).
87 |
88 | These fixed getters are useful in two situations. Firstly, for passing to a higher order function like xs=map!(get0 points). Secondly, when using multiple indices to index containers of containers like scalar=get3!get2!matrix. Otherwise, the easiest option is container!index, which works well in most other situations.
89 |
90 |
Specialized Container Functions
91 | For the following functions you need to know what kind of container you want to use:
92 |
93 |
make_stack
make_stack!container copies the items in the container to a new stack. O(N).
94 |
make_string
make_string!container copies the items in the container to a new string. O(N).
95 |
make_table
make_table!container copies the items in the container to a new table. The container should contain tuples of (key value). O(N).
96 |
97 |
98 |
merge_stack
merge_stack![c1 ... cn] concatenates a stack of containers into a single stack. O(N).
99 |
merge_string
merge_string![c1 ... cn] concatenates a stack of containers into a single string. O(N).
100 |
merge_table
merge_table![c1 ... cn] merges a stack of containers into a single table. O(N).
101 |
102 |
103 |
map
map!(f container) returns a new container with the function f applied to each item in the input container. The type of the output container is the same as the type of the input container. O(N).
104 |
map_stack
map_stack!(f container) returns a stack with the function f applied to each item in the container. O(N).
105 |
map_string
map_string!(f container) returns a string with the function f applied to each item in the container. O(N).
106 |
map_table
map_table!(f container) returns a table with the function f applied to each item in the container. The function f should return a tuple (key value). O(N).
107 |
108 |
109 |
range
range!n returns the stack [0 1 2 ... n-1]. O(n).
110 |
enumerate
enumerate![7 9 4] returns the stack of tuples [(0 7) (1 9) (2 4)], where the first item in each tuple is its index. O(N).
111 |
zip2
zip2!([1 2 3] [4 5 6]) returns the stack of tuples [(1 4) (2 5) (3 6)], where each inner tuple combines the corresponding items from the input tuple of stacks. O(N).
112 |
zip3
zip3!([1 2 3] [4 5 6] [7 8 9]) returns the stack of tuples [(1 4 7) (2 5 8) (3 6 9)], where each inner tuple combines the corresponding items from the input tuple of stacks. O(N).
113 |
zip4
zip4!([1 2] [3 4] [5 6] [7 8]) returns the stack of tuples [(1 3 5 7) (2 4 6 8)], where each inner tuple combines the corresponding items from the input tuple of stacks. O(N).
get!(key table default_value) returns the value corresponding to the key in the table, if it exists, otherwise default_value is returned. O(log N).
123 |
put
put!((key value) table) set the value corresponding to the key in the table and returns the table. Note that the table is also mutated so that other references to it are effected as well. O(log N).
124 |
get_keys
get_keys!table returns a stack of all keys in the table. O(N).
125 |
get_values
get_values!table returns a stack of all values in the table. O(N).
126 |
get_items
get_items!table returns a stack of tuples, of all pairwise keys and values in the table. O(N).
127 |
count_elements
count_elements!container returns a table where the keys are elements from the input container and the values are the number of times they occur in the input container. O(N log N).
128 |
unique
unique!container returns a stack with the unique elements of the input container. O(N log N).
129 |
130 |
131 |
Boolean Functions
132 |
133 |
boolean
Convert a value to a boolean. All values are converted to yes, except empty stack [] and empty string "" and number zero 0 and the boolean no which are all converted to no.
134 |
not
Convert a value to a boolean. All values are converted to no, except empty stack [] and empty string "" and number zero 0 and the boolean no which are all converted to yes.
135 |
all / and
Takes a stack of booleans and returns yes if all items are yes and otherwise no.
136 |
any / or
Takes a stack of booleans and returns yes if any item is yes and otherwise no.
137 |
none
Takes a stack of booleans and returns yes if all item are no and otherwise yes.
138 |
equal
Takes two values and returns yes if they are equal and otherwise no.
139 |
unequal
Takes two values and returns no if they are equal and otherwise yes.
140 |
141 |
142 |
Functions on Single Numbers
143 |
144 |
inc
Increments a number by adding 1 to it.
145 |
dec
Decrements a number by subtracting 1 from it.
146 |
neg
Negates a number.
147 |
abs
Absolute value of a number.
148 |
round
Round a number to closest integer.
149 |
round_up
Round a number up to closest integer.
150 |
round_down
Round a number down to closest integer.
151 |
sqrt
Square root of a number.
152 |
153 |
154 |
Functions on Pairs of Numbers
155 |
156 |
add
Add a tuple of two numbers.
157 |
mul
Multiply a tuple of two numbers.
158 |
sub
Subtract a tuple of two numbers.
159 |
div
Divide a tuple of two numbers.
160 |
mod
The remainder of the division of a tuple of two numbers.
161 |
min
Minimum of a tuple of two numbers.
162 |
max
Maximum of a tuple of two numbers.
163 |
less
Takes a tuple of two numbers and returns yes if the first is smaller than the second, otherwise no.
164 |
less_or_equal
Takes a tuple of two numbers and returns yes if the first is smaller or equal to the second, otherwise no.
165 |
greater
Takes a tuple of two numbers and returns yes if the first is larger than the second, otherwise no.
166 |
167 |
168 |
Functions on Stacks of Numbers
169 |
170 |
sum
Adds a stack of numbers to a single number. Returns 0 if the stack is empty.
171 |
product
Multiplies a stack of numbers to a single number. Returns 1 if the stack is empty.
172 |
min_item
Minimum of a stack of numbers. Returns inf if the stack is empty.
173 |
max_item
Maximum of a stack of numbers. Returns -inf if the stack is empty.
174 |
min_key
min_key!(key stack) returns the item in the stack for which the the result of the function application key!item is smallest. Requires the stack to be non-empty.
175 |
max_key
max_key!(key stack) returns the item in the stack for which the result of the function application key!item is largest. Requires the stack to be non-empty..
176 |
min_predicate
min_predicate!(predicate stack) compares all items in the stack and returns the item that is smallest according to the binary predicate. predicate!(left right) checks if left is smaller than right and returns a boolean. Requires the stack to be non-empty.
177 |
max_predicate
max_predicate!(predicate stack) compares all items in the stack and returns the item that is largest according to the binary predicate. predicate!(left right) checks if left is smaller than right and returns a boolean. Requires the stack to be non-empty.
178 |
179 |
180 |
Character Functions
181 |
182 |
number
Takes a character and returns its ascii number.
183 |
character
Takes a number and returns the character with that ascii number.
184 |
parse_digit
Takes a character and returns the corresponding number.
185 |
parse_natural_number
Takes a string and returns the corresponding non-negative integer.
186 |
is_digit
Is a character a digit? Returns yes or no.
187 |
is_letter
Is a character a letter? Returns yes or no.
188 |
is_upper
Is a character upper case? Returns yes or no.
189 |
is_lower
Is a character lower case? Returns yes or no.
190 |
to_upper
Takes a character and converts it to upper case. Only has an effect it the character is a lower case letter.
191 |
to_lower
Takes a character and converts it to lower case. Only has an effect it the character is an upper case letter.
192 |
193 |
194 |
195 |
--------------------------------------------------------------------------------
/docs/owl.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mabur/mang_lang/56363d40226a5490b8114cb3c842dacd64e9b9cc/docs/owl.png
--------------------------------------------------------------------------------
/docs/reference.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mabur/mang_lang/56363d40226a5490b8114cb3c842dacd64e9b9cc/docs/reference.png
--------------------------------------------------------------------------------
/docs/styles.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: 'Montserrat';
3 | font-size: 100%;
4 | margin: 10px;
5 | }
6 | table {
7 | font-size: 100%;
8 | margin: 0px;
9 | padding: 0px;
10 | }
11 | tr {
12 | margin: 0px;
13 | padding: 0px;
14 | }
15 | td {
16 | margin: 0px;
17 | padding: 0px;
18 | }
19 | q {
20 | font-size: 80%;
21 | font-style: italic;
22 | font-family: 'Mali';
23 | }
24 | .character {
25 | padding: 10px 2px 10px 2px;
26 | }
27 | h1 {
28 | font-family: 'Roboto Mono';
29 | font-weight: normal;
30 | color: #E3322F;
31 | color: #3FAF7E;
32 | margin: 40px 0px 20px 0px;
33 | }
34 | h2 {
35 | font-family: 'Roboto Mono';
36 | font-weight: normal;
37 | color: #3FAF7E;
38 | margin: 40px 0px 20px 0px;
39 | }
40 | h3 {
41 | font-family: 'Roboto Mono';
42 | font-weight: normal;
43 | color: #3FAF7E;
44 | margin: 40px 0px 20px 0px;
45 | }
46 | pre {
47 | font-family: 'Roboto Mono';
48 | font-size: 90%;
49 | color: #000000;
50 | background-color: #FFFFDD;
51 | padding: 5px;
52 | margin: 20px 0px 20px 0px;
53 | }
54 | code {
55 | background-color: #FFFFDD;
56 | font-family: 'Roboto Mono';
57 | padding: 0px 5px 0px 5px;
58 | }
59 | li {
60 | padding: 8px;
61 | }
62 | img {
63 | image-rendering: crisp-edges;
64 | image-rendering: pixelated;
65 | vertical-align: middle;
66 | padding: 0px 16px 12px 16px;
67 | }
68 | a {
69 | color: #6B6FFF;
70 | color: #417ED1;
71 | }
72 |
--------------------------------------------------------------------------------
/docs/type_annotation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mabur/mang_lang/56363d40226a5490b8114cb3c842dacd64e9b9cc/docs/type_annotation.png
--------------------------------------------------------------------------------
/documents/c_transpiler.txt:
--------------------------------------------------------------------------------
1 | TRANSPILING MANGLANG TO C
2 | * EvaluatedDict
3 | - Are like struct in C.
4 | - Require member lookup @ <-> .
5 | * Dict in evaluation
6 | - Require symbol lookup in current scope or parent.
7 | - Require while loops.
8 | - Are like scopes within functions in C.
9 | * Closures / nested functions.
10 | - Are not in C, but in Gnu C and C++.
11 | - Would goto be an option?
12 | * Goto-option
13 | - Main is entry point.
14 | - Define all symbols used in manglang within the scope of main.
15 | - Define all functions used in manglang using goto and labels inside main.
16 | - Define structs that are implicitly used in manglang as structs above main.
17 | * Serialize at the end somehow
18 | - Build a parallel serialization-object while evaluating,
19 | that is used to serialize at the end.
20 |
21 |
22 | EXAMPLE - single dict
23 |
24 | // Manglang
25 | {a=1 b=a}
26 |
27 | // c
28 | struct S_a_b {
29 | Expression a;
30 | Expression b;
31 | };
32 | int main() {
33 | Expression a = makeNumber(1);
34 | Expression b = a;
35 | print(serialize((S_a_b){a, b}));
36 | }
37 |
38 | EXAMPLE - nested dicts
39 |
40 | // Manglang
41 | {a=1 b={c=a}}
42 |
43 | // C
44 | struct S_c {
45 | Expression c;
46 | };
47 | struct S_a_b {
48 | Expression a;
49 | Expression b;
50 | };
51 | int main() {
52 | Expression a = makeNumber(1);
53 | {
54 | Expression c = a;
55 | }
56 | Expression b = makeS_c((S_c){c});
57 | print(serialize(makeS_a_b((S_a_b){a, b})));
58 | }
59 |
60 | EXAMPLE - while
61 | // Manglang
62 | {s=0 i=2 while i s += i end}
63 |
64 | // C
65 | struct S_s_i {
66 | Expression s;
67 | Expression i;
68 | };
69 | int main(){
70 | Expression s = makeNumber(0);
71 | Expression i = makeNumber(2);
72 | while (makeBoolean(i)) {
73 | increment(s, i);
74 | }
75 | print(serialize((S_s_i){s, i}));
76 | }
77 |
78 | EXAMPLE - tuple
79 | // Manglang
80 | (1 2)
81 |
82 | // C
83 | int main() {
84 | printT0((T2){1, 2});
85 | }
86 |
87 | EXAMPLE - function
88 | // Manglang
89 | {x=1 f=in x out mul!(x x)}
90 | struct S0 {
91 | int x;
92 | void* f(void*);
93 | };
94 | struct T0 {
95 | void* data0;
96 | void* data1;
97 | };
98 | void* F0(void* x) {
99 | return mul((T0){x, x});
100 | }
101 | void printS0(S0*);
102 | int main() {
103 | S0 s0;
104 | so.x = 1;
105 | s0.f = F0;
106 | printS0(&s0);
107 | }
108 |
109 | EXAMPLE - function closure
110 | // Manglang
111 | {x=1 f=in y out mul!(x y) z=f!3}
112 | struct S_x_f_z {
113 | Expression x;
114 | Expression f;
115 | Expression z;
116 | };
117 | void serializeS_x_f_z(S0);
118 | int main() {
119 | Expression x = makeNumber(1);
120 | Expression f_transform(Expression y) {
121 | return mul((T2){x, y});
122 | };
123 | Expression f = makeFunction(f_transform);
124 | Expression z = applyFunction(f, addNumber(3));
125 | print(serializeS_x_f_z((S_x_f_z){x,f,z}));
126 | }
127 |
128 |
--------------------------------------------------------------------------------
/documents/cpp_transpiler.txt:
--------------------------------------------------------------------------------
1 | TRANSPILING MANGLANG TO C++
2 | * EvaluatedDict
3 | - Are like a struct.
4 | - Require member lookup @ <-> .
5 | * Dict in evaluation
6 | - Require symbol lookup in current scope or parent.
7 | - Require while loops.
8 | - Are like scopes within functions.
9 | * Closures / nested functions.
10 | - use lambda functions
11 | * Serialize at the end somehow
12 | - Build a parallel serialization-object while evaluating,
13 | that is used to serialize at the end.
14 |
15 | // Manglang
16 | // 3
17 | // C++
18 | // makeNumber(3)
19 |
20 | // Manglang
21 | // (3)
22 | // C++
23 | // Tuple1{makeNumber(3)}
24 |
25 | // Manglang
26 | (1 2)
27 | // C++
28 | Tuple2{makeNumber(1), makeNumber(2)}
29 |
30 | // Manglang
31 | x
32 | // C++
33 | x
34 |
35 | // Manglang
36 | child@parent
37 | // C++
38 | parent.child
39 |
40 | // Manglang
41 | mul!(x x)
42 | // C++
43 | applyFunction(mul, Tuple2{x, x})
44 |
45 | // Manglang
46 | in x out mul!(x x)
47 | // C++
48 | makeFunction([=](auto x) {
49 | return applyFunction(mul, Tuple2{x, x});
50 | })
51 |
52 | // Manglang
53 | // {a=3}
54 | // C++
55 | [=](){
56 | auto a = makeNumber(3);
57 | return Dictionary_a{a};
58 | }()
59 |
60 | // Manglang
61 | {a=1 b=a}
62 | // C++
63 | [=](){
64 | auto a = makeNumber(1);
65 | auto b = a;
66 | return Dictionary_a_b{a, b};
67 | }()
68 |
69 | // Manglang
70 | {a=1 b={c=a}}
71 | // C++
72 | [=](){
73 | auto a = makeNumber(1);
74 | auto b = [=](){
75 | auto c = a;
76 | return Dictionary_c{c};
77 | }();
78 | return Dictionary_a_b{a, b};
79 | }()
80 |
81 | // Manglang
82 | {s=0 i=2 while i s += i end}
83 | // C++
84 | [=](){
85 | auto s = makeNumber(0);
86 | auto i = makeNumber(2);
87 | while (makeBoolean(i)) {
88 | increment(s, i);
89 | }
90 | return Dictionary_s_i{s, i};
91 | }()
92 |
93 | // Manglang
94 | {x=1 f=in x out mul!(x x)}
95 | // C++
96 | [=](){
97 | auto x = makeNumber(1);
98 | auto f = makeFunction([=](auto x) {
99 | return mul(Tuple2{x, x});
100 | }
101 | );
102 | return Dictionary_x_f{x, f};
103 | }()
104 |
105 | // Manglang
106 | {x=1 f=in y out mul!(x y) z=f!3}
107 | // C++
108 | [=](){
109 | auto x = makeNumber(1);
110 | auto f = makeFunction([=](auto y) {
111 | return mul((Tuple2){x, y});
112 | };
113 | auto z = applyFunction(f, makeNumber(3));
114 | return Dictionary_x_f_z{x, f, z};
115 | }
116 |
--------------------------------------------------------------------------------
/documents/manglang.tex:
--------------------------------------------------------------------------------
1 | \documentclass[a4paper,12pt]{article}
2 | \usepackage{amsmath}
3 | \usepackage{amsfonts}
4 | \usepackage{listings}
5 | \title{Manglang}
6 | \author{Magnus Burenius}
7 | \begin{document}
8 |
9 | \maketitle
10 |
11 | \section{Overiew}
12 |
13 | Mang Lang is a functional language without any side efects. The lifetime of a Mang Lang program has four stages:
14 | \begin{enumerate}
15 | \item Program written in Mang Lang as text.
16 | \item Program parsed into a tree graph.
17 | \item Program evaluated into another tree graph.
18 | \item Program result presented in Mang Lang as text.
19 | \end{enumerate}
20 |
21 | \section{Minimal Language for Data}
22 |
23 | We start by defining a minimal language for storing data, similar to Json. It consists of these primitive values:
24 | \begin{itemize}
25 | \item Numbers like: \lstinline|1.0|
26 | \item Characters like: \lstinline|'a'|
27 | \end{itemize}
28 | Values can be packaged in these collections:
29 | \begin{itemize}
30 | \item Dictionaries like: \lstinline|{x = 1 y = 2}|
31 | \item Lists like: \lstinline|(1 2 3)|
32 | \item Strings like: \lstinline|''Magnus''|
33 | \end{itemize}
34 | Dictionaries and strings can be nested.
35 |
36 | \section{Extended Language for Data}
37 |
38 | We add the posibility to refer back to symbols:
39 | \begin{itemize}
40 | \item Reference to symbol in current scope like: \lstinline|{x = 1 y = x}|
41 | \item Reference to symbol in environment scope like: \lstinline|{x = 1 y = {z = x}}|
42 | \item Reference to symbol in child scope like: \lstinline|{x = {y = 1} z = y@x}|
43 | \end{itemize}
44 |
45 | \subsection{Evaluation}
46 |
47 | To evaluate a program written in the extended language for data we follow these steps:
48 | \begin{enumerate}
49 | \item Parse input written in the extended language for data. This results in a tree graph.
50 | \item Collapse references to the expressions they refer to. This results in another tree graph.
51 | \item Serialize the evaluated tree to get the result written in the minimal language for data.
52 | \end{enumerate}
53 | We parse the input into a tree. In this tree a parent keeps references to its children, and each child also keeps a reference to its environment, which can be different from its parent. We use this when looking up references and collapsing them.
54 |
55 | \section{Minimal Language for Algorithms}
56 |
57 | We will now extend our language to handle functions. We extend the abstract syntax tree with these nodes:
58 | \begin{itemize}
59 | \item Conditionals like: \lstinline|if x then y else z|
60 | \item Functions like: \lstinline|in x out (x x)|
61 | \item Reference to function input like: \lstinline|in x out (x x)|
62 | \item Function application like: \lstinline|{f = in x out (x x) y = f!1}|
63 | \end{itemize}
64 |
65 | \subsection{Evaluation}
66 |
67 | We evaluate the program by transforming the AST to another graph. We keep a copy of the original AST. Functions know about their environment in the graph.
68 | \begin{enumerate}
69 | \item We do not collapse: numbers, strings, booleans
70 | \item We collapse conditionals.
71 | \item We collapse references.
72 | \item We collapse function calls. When collapsing a function call we evaluate the body of the function it refers to, given the input to the function.
73 | \item We do NOT collapse functions and do NOT collapse anything in the function body.
74 | \end{enumerate}
75 |
76 | \section{Implementation}
77 |
78 | \subsection{Abstract Syntax Tree}
79 | The parsing takes a string and builds an Abstract Syntax Tree.
80 |
81 | \subsection{Evaluation Tree}
82 |
83 | The evaluation takes an Abstact Syntax Tree and builds an Evaluation Tree. In the evaluation tree each node can have a pointer to its environment Node, which can be different from its parent node, e.g. for functions. The nodes in the Abstract Syntax Tree are cloned during the evaluation and given environment pointers.
84 |
85 | In the evaluation tree each node is owned by its parent. The environment pointers are raw pointers. We do not allow a node to survive its environment. Therefore we do not allow functions to survive their bound environment, i.e. to be the output.
86 |
87 | \subsection{Methods}
88 |
89 | Each Node provides an evaluation method:
90 | \begin{verbatim}
91 | shared_ptr Expression::evaluate(const Node* environment);
92 | \end{verbatim}
93 | Each Node provides a lookup method that search for a reference in the node and if nothing is find passes the query to its environment.
94 | \begin{verbatim}
95 | shared_ptr Expression::lookup(string reference);
96 | \end{verbatim}
97 | Functions provide an a method for function application:
98 |
99 | TODO: Check implementation of apply:
100 | \begin{verbatim}
101 | shared_ptr Expression::apply(shared_ptr input);
102 | \end{verbatim}
103 | How does the evaluation of the function body know about its environment?
104 | The environment for a function body is not only its parent, but also its input.
105 |
106 | \clearpage
107 |
108 | \subsection{Lookup rules}
109 |
110 | \paragraph{General Fallback}
111 | Delegate lookup to environment. If the expression does not have an environment then report run-time error for missing symbol.
112 |
113 | \paragraph{Dictionary}
114 | Try lookup for each dictionary element. If the name is not found in any of the dictionary elemenets then fallback to general lookup.
115 |
116 | \paragraph{Named Element}
117 | If the lookup name corresponds to the name of the element then return its expression. Otherwise return null.
118 |
119 | \paragraph{List}
120 | If lookup name is ``first'' or ``rest'' then return that part of the list. Otherwise?
121 |
122 | TODO: think about child lookup vs environment lookup. Will child lookup fallback to general lookup in environment? Can we have the same lookup interface for both cases or should we have two lookup interfaces?
123 |
124 | \subsection{Evaluation Rules}
125 | During evaluation we pass the environment to each expression that should be evaluated. During evaluation we also set the environment when creating new expressions.
126 |
127 | \paragraph{Number} is evaluated to itself. Has no environment.
128 | \paragraph{Character} is evaluated to itself. Has no environment.
129 | \paragraph{String} is evaluated to itself. Has no environment.
130 | \paragraph{Function} is evaluated to a copy of itself. Environment is set to its parent.
131 | \paragraph{List}
132 | \begin{enumerate}
133 | \item is evaluated to a new list where we have evaluated each of its elements. Environment is set to its parent.
134 | \end{enumerate}
135 |
136 | \paragraph{Dictionary}
137 | \begin{enumerate}
138 | \item Create a new dictionary.
139 | \item Evaluate each element, with an environment set to its parent extended with the partially evaluated dictionary.
140 | \item Set environment of new dictionary to its parent.
141 | \end{enumerate}
142 |
143 | \paragraph{Conditional}
144 | \begin{enumerate}
145 | \item Evaluate the condition.
146 | \item Delegate evaluation to either the if-child or else-child.
147 | \end{enumerate}
148 |
149 | \paragraph{Child reference}
150 | \begin{enumerate}
151 | \item Evaluate the child.
152 | \item Look up the name in evaluated child.
153 | \end{enumerate}
154 |
155 | \paragraph{Reference}
156 | \begin{enumerate}
157 | \item Look up the name in the environment.
158 | \item The found expreession is returned. Note that the environment of the node found is not changed.
159 | \end{enumerate}
160 |
161 | \paragraph{Function application}
162 | \begin{enumerate}
163 | \item Evaluate the input.
164 | \item Look up the name of the function in the environment.
165 | \item Evaluate the body of the function in its environment, extended with the input.
166 | \end{enumerate}
167 |
168 | \end{document}
169 |
--------------------------------------------------------------------------------
/documents/old_readme.md:
--------------------------------------------------------------------------------
1 | # Mang Lang
2 |
3 | Mang Lang is an experimental minimalistic programming language.
4 | Its primary goal is to be a simple and minimal language that is easy to implement and interpret.
5 | Mang lang is similar to the [Json](https://www.json.org/) data format,
6 | but with a minimal set of programming primitives added:
7 | * References to entries in dictionaries
8 | * If-then-else conditionals
9 | * First class functions
10 |
11 | Mang lang is a purely functional and interpreted language.
12 | It takes source code written in Mang lang and evaluates it:
13 | ```vhdl
14 | {
15 | rectangles = (
16 | {width = 3 height = 1}
17 | {width = 6 height = 2}
18 | {width = 3 height = 6}
19 | {width = 8 height = 4}
20 | )
21 | get_area = in {width height} out mul!(width height)
22 | areas = map!(get_area rectangles)
23 | total_area = add!areas
24 | num_rectangles = count!rectangles
25 | average_area = div!(total_area num_rectangles)
26 | }
27 | ```
28 | When we evaluate the source code above we get the result below:
29 | ```vhdl
30 | {
31 | rectangles = (
32 | {width = 3 height = 1}
33 | {width = 6 height = 2}
34 | {width = 3 height = 6}
35 | {width = 8 height = 4}
36 | )
37 | get_area = in {width height} out mul!(width height)
38 | areas = (3 12 18 32)
39 | total_area = 65
40 | num_rectangles = 4
41 | average_area = 16.25
42 | }
43 | ```
44 | Note that both the input and output of the mang lang interpreter is given as mang lang source code!
45 |
46 | For more code examples continue reading below, or look inside the
47 | [standard library](https://github.com/mabur/mang_lang/blob/master/src/built_in_functions/standard_library.h).
48 |
49 | # Design Trade-offs
50 |
51 | Mang lang has the following **design trade-offs**:
52 |
53 | 1. **Minimalistic**, instead of feature-rich.
54 | 2. **Prinicipled**, instead of pragmatic.
55 | 2. **Purely functional**, instead of imperative or object oriented.
56 | 3. **Eager evaluation**, instead of lazy evaluation.
57 | 4. **Dynamically typed and interpreted**, instead of statically typed and compiled.
58 | 5. **Code as Data and Data as Code**. The built-in data structures (lists and dictionaries) are also used to structure the code itself into multiple expressions, lines and variables. Furthermore, both the input data and output data of the Mang Lang interpretor is expressed in Mang Lang itself!
59 |
60 | # Language Comparison
61 |
62 | The minimalism of Manglang makes it resemble a simple data format like Json or Yaml.
63 | Manglang only adds conditionals and functions to allow computations.
64 |
65 | | | Json | Yaml | Manglang | Scheme | C |
66 | | :-------------- | :--- | :--- | :------- | :----- | :--- |
67 | | Numbers | Yes | Yes | Yes | Yes | Yes |
68 | | Strings | Yes | Yes | Yes | Yes | Yes |
69 | | Lists | Yes | Yes | Yes | Yes | Yes |
70 | | Symbols | Yes | Yes | Yes | Yes | Yes |
71 | | Null | Yes | Yes | - | - | Yes |
72 | | Comments | - | Yes | - | Yes | Yes |
73 | | Aliases | - | Yes | Yes | Yes | Yes |
74 | | Conditionals | - | - | Yes | Yes | Yes |
75 | | Loops | - | - | Yes | Yes | Yes |
76 | | Functions | - | - | Yes | Yes | Yes |
77 | | Mutable objects | - | - | - | Yes | Yes |
78 | | Operators | - | - | - | Yes | Yes |
79 | | Macros | - | - | - | Yes | Yes |
80 | | Enum | - | - | - | - | Yes |
81 | | Pointers | - | - | - | - | Yes |
82 | | Static types | - | - | - | - | Yes |
83 |
84 | Mang lang is similar to these languages:
85 | * [Json](https://www.json.org/)
86 | * [Yaml](https://yaml.org/)
87 | * [Jsonnet](https://jsonnet.org/)
88 | * [Dhall](https://dhall-lang.org/)
89 | * [L1](https://mlajtos.github.io/L1/)
90 | * [Azor](https://github.com/cstuartroe/azor/)
91 | * [Lisp Family](https://en.wikipedia.org/wiki/Lisp_(programming_language))
92 |
93 | # Grammar
94 |
95 | Manglang has a minimal syntax. A program/expression is built up from these building blocks:
96 |
97 | | Kind of Expression | Syntax |
98 | | :-------------------- | :-------------------------------------------- |
99 | |number | 12.34 |
100 | |character | 'a' |
101 | |string | "abc" |
102 | |list | (expression ...) |
103 | |dictionary | {name = expression ...} |
104 | |reference | name |
105 | |child reference | name@expression |
106 | |conditional | if expression then expression else expression |
107 | |function | in name out expression |
108 | |function of list | in (name ...) out expression |
109 | |function of dictionary | in {name ...} out expression |
110 | |function call | name!expression |
111 |
112 | # Examples
113 |
114 | 1. Data
115 | 1. [Numbers](#1i-numbers)
116 | 2. [Characters](#1ii-characters)
117 | 3. [Strings](#1iii-strings)
118 | 4. [Lists](#1iv-lists)
119 | 5. [Dictionaries](#1v-dictionaries)
120 | 2. References
121 | 1. [Name lookup](#2i-name-lookup)
122 | 2. [Child name lookup](#2ii-child-name-lookup)
123 | 3. Computation
124 | 1. [Conditionals](#3i-conditionals)
125 | 2. [Function calls](#3ii-function-calls)
126 | 3. [Function definitions](#3iii-function-definitions)
127 | 4. [Function of list definitions](#3iv-function-of-list-definitions)
128 | 5. [Function of dictionary definitions](#3iv-function-of-dictionary-definitions)
129 |
130 |
131 | ## 1.I Numbers
132 | Mang lang has a single number type that is used for both integers and floats:
133 | ```vhdl
134 | 12.34
135 | ```
136 |
137 | ## 1.II Characters
138 | A single ascii character is written as:
139 | ```vhdl
140 | 'a'
141 | ```
142 |
143 | ## 1.III Strings
144 | Strings are written as:
145 | ```vhdl
146 | "Mang lang"
147 | ```
148 | They can be seen as lists of characters. Example of a program using functions on strings:
149 | ```vhdl
150 | {
151 | a = "Mang lang"
152 | b = first!a
153 | c = rest!a
154 | d = reverse!a
155 | e = prepend!('E' a)
156 | }
157 | ```
158 | This program is evaluated to:
159 | ```vhdl
160 | {
161 | a = "Mang lang"
162 | b = 'M'
163 | c = "ang lang"
164 | d = "gnal gnaM"
165 | d = "EMang lang"
166 | }
167 | ```
168 |
169 | ## 1.IV Lists
170 | Lists of values are written as:
171 | ```vhdl
172 | (3 6 4)
173 | ```
174 | Example of a program using functions on lists:
175 | ```vhdl
176 | {
177 | a = (3 6 4)
178 | b = first!a
179 | c = rest!a
180 | d = reverse!a
181 | e = prepend!(9 a)
182 | }
183 | ```
184 | This program is evaluated to:
185 | ```vhdl
186 | {
187 | a = (3 6 4)
188 | b = 3
189 | c = (6 4)
190 | d = (4 6 3)
191 | e = (9 4 6 3)
192 | }
193 | ```
194 |
195 | ## 1.V Dictionaries
196 |
197 | Dictionaries are used to associate names/symbols with expressions:
198 | ```vhdl
199 | {a = 1 b = 'A' c = "abc"}
200 | ```
201 | Mang lang doesn't care about extra whitespace so the program above can also be written as:
202 | ```vhdl
203 | {
204 | a = 1
205 | b = 'A'
206 | c = "abc"
207 | }
208 | ```
209 | Dictionaries can be nested:
210 | ```vhdl
211 | {
212 | rectangle = {width = 4 height = 5}
213 | circle = {radius = 5}
214 | }
215 | ```
216 | In Mang lang dictionaries are the only way to associate names/symbols with expressions.
217 | So Mang lang uses dictionaries to represent both: variables, objects, function input, function output.
218 | This is a beautiful generalization and simplification.
219 |
220 | ## 2.I Name Lookup
221 |
222 | A name/symbol defined in a dictionary can be referenced after it is defined:
223 | ```vhdl
224 | {
225 | a = 1
226 | b = a
227 | }
228 | ```
229 | This program is evaluated to the dictionary:
230 | ```vhdl
231 | {
232 | a = 1
233 | b = 1
234 | }
235 | ```
236 | Dictionaries can be nested. You can refer to symbols in the current dictionary or in parent dictionaries in this way:
237 | ```vhdl
238 | {
239 | a = 1
240 | b = {c = 2 d = a}
241 | }
242 | ```
243 | This program is evaluated to:
244 | ```vhdl
245 | {
246 | a = 1
247 | b = {c = 2 d = 1}
248 | }
249 | ```
250 |
251 | ## 2.II Child Name Lookup
252 |
253 | In the previous section we looked at how to refer to names defined in the current dictionary, or in a parent dictionary.
254 | You can also refer to names in a child dictionary like this:
255 | ```vhdl
256 | {
257 | a = {b=2 c=3}
258 | d = c@a
259 | }
260 | ```
261 | This program is evaluated to:
262 | ```vhdl
263 | {
264 | a = {b=2 c=3}
265 | d = 3
266 | }
267 | ```
268 | The syntax `name@dictionary` is used to get the value corresponding to the name/key inside the dictionary.
269 | This syntax is reversed compared to most languages that instead write this as `dictionary.name`.
270 | However, having it like this simplifies the syntax of Mangalng and makes it easier to parse.
271 | It also makes both function application and dictionary lookup follow the same order and pattern.
272 |
273 | ## 3.I Conditionals
274 |
275 | A conditional is written as `if a then b else c` and this expression is evaluated to b or c depending of if a is true or false.
276 | Mang lang has no explicit type for boolean values but interprets other values as true or false.
277 | * Values that are interpreted as false:
278 | - the number zero `0`
279 | - the empty list `()`
280 | - the empty string `""`
281 | * Values that are interpreted as true:
282 | - all other numbers, lists and strings.
283 |
284 | Consider this program as an example:
285 |
286 | ```vhdl
287 | {
288 | a = (0 1)
289 | b = if a then
290 | first!a
291 | else
292 | 1
293 | c = if b then "hello" else "world"
294 | }
295 | ```
296 | This program is evaluated to:
297 | ```vhdl
298 | {
299 | a = (0 1)
300 | b = 0
301 | c = "world"
302 | }
303 | ```
304 |
305 | ## 3.II Function calls
306 |
307 | We have already seen some examples of calling functions in mang lang.
308 | A function is called using `!` like `function_name!input_expression`.
309 | Functions in take a single value as input.
310 | However, this single value can be a list or a dictionary, that has multiple values inside them.
311 | ```vhdl
312 | {
313 | list = (4 2 1)
314 | sum0 = add!list
315 | head0 = first!list
316 | sum1 = add!(4 2 1)
317 | head1 = first!(4 2 1)
318 | list2 = prepend!(3 list)
319 | }
320 | ```
321 | This program is evaluated to:
322 | ```vhdl
323 | {
324 | list = (4 2 1)
325 | sum0 = 7
326 | head0 = 4
327 | sum1 = 7
328 | head1 = 4
329 | list2 = (3 4 2 1)
330 | }
331 | ```
332 | Mang Lang does not have any special operators for arithmetics, boolean, list operations etc.
333 | Instead functions are used for all computations.
334 | Function calls can be nested like this:
335 |
336 | ````vhdl
337 | mul!(add!(1 2) sub!(7 2))
338 | ````
339 |
340 | This program is evaluated to `(1+2)*(7-2) = 3*5 = 15`.
341 | Function calls are right associative.
342 | Manglang does not support currying.
343 |
344 |
345 | ## 3.III Function Definitions
346 |
347 | Functions are defined using they keywords `in` and `out` like this:
348 |
349 | ```vhdl
350 | {
351 | square = in x out mul!(x x)
352 | result = square!3
353 | }
354 | ```
355 |
356 | A function definition is on the form `in x out expression`
357 | where `x` is the single input and expression is an expression using `x`.
358 | Functions are first class values and can be given a name by putting them inside a dictionary.
359 | Here are some examples of defining and calling functions:
360 | ```vhdl
361 | {
362 | square = in x out mul!(x x)
363 | inc = in x out add!(x 1)
364 | dec = in x out sub!(x 1)
365 | count = in list out if list then inc!count!rest!list else 0
366 | a = square 3
367 | b = inc 3
368 | c = dec 3
369 | d = count!(3 7 3 8 2)
370 | e = count!"apple"
371 | }
372 | ```
373 | This program is evaluated to:
374 | ```vhdl
375 | {
376 | square = in x out mul!(x x)
377 | inc = in x out add!(x 1)
378 | dec = in x out sub!(x 1)
379 | count = in list out if list then inc!count!rest!list else 0
380 | a = 9
381 | b = 4
382 | c = 2
383 | d = 5
384 | e = 5
385 | }
386 | ```
387 | The if-then-else operator is used to choose what value to return based on a condition.
388 | Functions can be recursive like the `count` example above.
389 |
390 | Function definitions and computations can be broken up into smaller parts by using dictionaries:
391 | ```vhdl
392 | {
393 | square = in x out mul!(x x)
394 | square_norm = in vec3 out result@{
395 | x = first!vec3
396 | y = second!vec3
397 | z = third!vec3
398 | result = add!(square!x square!y square!z)
399 | }
400 | vector = (3 4 5)
401 | result = square_norm!vector
402 | }
403 | ```
404 | ## 3.IV Function of List Definitions
405 |
406 | Mang lang provides syntactic sugar for defining functions that take multiple input,
407 | in the form of a list.
408 | Here are some examples of equivalent ways of defining and calling functions:
409 | ```vhdl
410 | {
411 | area1 = in rectangle out mul!(first!rectangle second!rectangle)
412 | area2 = in (width height) out mul!(width height)
413 | rectangle = (5 4)
414 | a = area1!rectangle,
415 | b = area2!rectangle,
416 | c = area1!(5 4)
417 | d = area2!(5 4)
418 | }
419 | ```
420 | The functions `area1` and `area2` are equivalent.
421 | They expect the same input and return the same result.
422 | `area2` just uses syntactic sugar to make its implementation more concise,
423 | by unpacking the elements of the input list.
424 | ## 3.V Function of Dictionary Definitions
425 |
426 | Mang lang provides syntactic sugar for defining functions that take multiple input,
427 | in the form of a dictionary with named entries.
428 | Here are some examples of equivalent ways of defining and calling functions:
429 | ```vhdl
430 | {
431 | area1 = in rectangle out mul!(width@rectangle height@rectangle)
432 | area2 = in {width height} out mul!(width height)
433 | rectangle = {width = 5 height = 4}
434 | a = area1!rectangle
435 | b = area2!rectangle
436 | c = area1!{width = 5 height = 4}
437 | d = area2!{width = 5 height = 4}
438 | }
439 | ```
440 | The functions `area1` and `area2` are equivalent.
441 | They expect the same input and return the same result.
442 | `area2` just uses syntactic sugar to make it its implementation more concise,
443 | by unpacking the elements of the input dictionary.
444 |
445 | ## List of built-in functions
446 |
447 | ### Functions for numbers:
448 | * **add**: adds a list of numbers.
449 | * **mul**: multiplies a list of numbers.
450 | * **sub**: subtracts two numbers.
451 | * **div**: divides two numbers.
452 |
453 |
454 | * **inc**: adds 1 to a number.
455 | * **dec**: subtracts 1 from a number.
456 |
457 |
458 | * **abs**: absolute value of a number.
459 | * **sqrt**: square root of a number.
460 |
461 |
462 | * **min**: smallest number in a non-empty list.
463 | * **max**: largest number in a non-empty list.
464 |
465 |
466 | * **increasing**: true if a list of numbers is increasing (<), and otherwise false.
467 | * **decreasing**: true if a list of numbers is decreasing (>), and otherwise false.
468 | * **weakly_increasing**: true if a list of numbers is weakly_increasing (<=), and otherwise false.
469 | * **weakly_decreasing**: true if a list of numbers is weakly_decreasing (>=), and otherwise false.
470 |
471 | ### Logical functions:
472 | * **equal**: true if two primitives are equal, and otherwise false.
473 | * **unequal**: false if two primitives are equal, and otherwise true.
474 | * **all**: true if all items of a list are true.
475 | * **any**: true if at least one item of a list is true.
476 | * **none**: true if all items of a list are false.
477 |
478 |
479 | ### List and string functions
480 | * **prepend**: Given input `(item list)` return a copy of the list/string with the item prepended at the beginning.
481 | * **first**: pick the first item in a non-empty list/string.
482 | * **rest**: list/string of all items except the first.
483 | * **reverse**: takes a list/string and return it in reversed order.
484 |
485 |
486 | * **map**: Given input `(f list)` return a list where the function f has been applied to each item.
487 | * **filter**: Given input `(predicate list)` return a list of all items for which the predicate is true.
488 | * **enumerate**: Given a list return a new list
489 | where each element is a dictionary `{item index}` containing the items from the original list together with the corresponding index.
490 | * **get_index**: Given input `(index list)` return the item at given index.
491 | * **concat**: concatenates two lists/strings.
492 |
493 |
494 | * **count**: The number of items of a list or string.
495 | * **count_item**: Given input `(item list)` count number of occurrences of a specific item in list.
496 | * **count_if**: Given input `(predicate list)` count number of items in list for which the predicate is true.
497 |
--------------------------------------------------------------------------------
/documents/readable_regular_expressions.txt:
--------------------------------------------------------------------------------
1 | Sök efter sträng:
2 | "hello" // fix sträng
3 | "he{l}[1...9]o" // 1-9 l
4 | "he{l}[0...1]o" // valfritt med l, högst 1 l
5 | "he{l}[...1]o" // valfritt med l, högst 1 l
6 | "he{l}[1...]o" // minst 1 l
7 | "he{l}[...]o" // godtyckligt många l
8 | "he{l}[2]o" // hello
9 | "he{l}[2...2]o" // hello
10 | "he{ll}[1]o" // hello
11 | "he{ll}[1...1]o" // hello
12 | "he{ll}o" // hello
13 | "{hello}" // hello
14 | "{hello,hi}" // hello eller hi
15 | "{hello,hi}[3]" // t.ex. hellohellohi
16 |
17 | Nestade uttryck?
18 | digit = "{0,1,2,3,4,5,6,7,8,9}"
19 | number = "{+,-}[0...1]\digit[1...]{.\digit[1...]}[0...1]"
20 | string = "\"\character[...]\""
21 | symbol = "\letter[1...]{\letter,\digit,_}[...]"
22 |
--------------------------------------------------------------------------------
/documents/transpiling.txt:
--------------------------------------------------------------------------------
1 | # Transpiling Manglang to C
2 |
3 | * Transpile to C, for speed.
4 | * Could start by transpiling to C++, and identical code as interpretor, and then gradually add speed ups by moving execution from interpretor to C compilation.
5 | * Use C-compiler for:
6 | - name-lookup
7 | - loops
8 | * A bytecode interpretor will probably be as fast?
9 |
10 | ## Dictionary with reference OK
11 | {
12 | a=1
13 | b=a
14 | }
15 | Expression evaluateDictX() {
16 | Expression a;
17 | Expression b;
18 | a = makeNumber(1);
19 | b = a;
20 | return makeDictionary({{"a",a}, {"b",b}});
21 | }
22 | int main() {serialize(evaluateDictX());}
23 |
24 | ## Dictionary with loop OK
25 |
26 | {
27 | i=2
28 | while i
29 | end
30 | }
31 | Expression evaluateDictX() {
32 | Expression i;
33 | i = makeNumber(2);
34 | while (asBoolean(i)) {
35 | }
36 | return makeDictionary({{"i", i}});
37 | }
38 |
39 | ## Nested dictionaries
40 | {
41 | a = 1
42 | b = {
43 | c = a
44 | }
45 | }
46 | {
47 | Expression result;
48 | Expression a;
49 | Expression b;
50 | result = makeNumer(1);
51 | a = result;
52 | {
53 | Expression c;
54 | result = a;
55 | c = result;
56 | result = makeDictionary({{"c", c}});
57 | }
58 | b = result;
59 | result = makeDictionary({{"a", a}, {"b", b}});
60 | }
61 |
--------------------------------------------------------------------------------
/examples/aoc_21_01.txt:
--------------------------------------------------------------------------------
1 | result@{
2 | depths = [
3 | 199
4 | 200
5 | 208
6 | 210
7 | 200
8 | 207
9 | 240
10 | 269
11 | 260
12 | 263
13 | ]
14 | result = 0
15 | previous = inf
16 | for item in depths
17 | result =
18 | if less?(previous item) then
19 | inc!result
20 | else
21 | result
22 | previous = item
23 | end
24 | }
25 |
--------------------------------------------------------------------------------
/examples/aoc_21_02.txt:
--------------------------------------------------------------------------------
1 | result@{
2 | commands = [
3 | "forward 5"
4 | "down 5"
5 | "forward 8"
6 | "up 3"
7 | "down 8"
8 | "forward 2"
9 | ]
10 | move = in (command position) out new_position@{
11 | x = position!0
12 | y = position!1
13 | steps = parse_digit!take!reverse!command
14 | new_position = is take!command
15 | 'u' then [x sub!(y steps)]
16 | 'd' then [x add!(y steps)]
17 | 'f' then [add!(x steps) y]
18 | else [inf inf]
19 | }
20 | start = [0 0]
21 | position = fold!(move commands start)
22 | result = product!position
23 | }
24 |
--------------------------------------------------------------------------------
/examples/aoc_21_03.txt:
--------------------------------------------------------------------------------
1 | result@{
2 | numbers = [
3 | "00100"
4 | "11110"
5 | "10110"
6 | "10111"
7 | "10101"
8 | "01111"
9 | "00111"
10 | "11100"
11 | "10000"
12 | "11001"
13 | "00010"
14 | "01010"
15 | ]
16 | NUM_BITS = count!take!numbers
17 |
18 | get_column = in index out
19 | map_stack!(
20 | in number out number!index
21 | numbers
22 | )
23 | count0 = in stack out
24 | count_item!('0' stack)
25 | count1 = in stack out
26 | count_item!('1' stack)
27 | pick_most_common_bit = in counts out
28 | if less?counts then 1 else 0
29 | pick_least_common_bit = in counts out
30 | if less?counts then 0 else 1
31 |
32 | dec_from_bin = in binary out sum!map_stack!(mul zip2!(binary [16 8 4 2 1]))
33 |
34 | columns = map_stack!(get_column range!NUM_BITS)
35 | counts0 = map_stack!(count0 columns)
36 | counts1 = map_stack!(count1 columns)
37 | gamma = map_stack!(pick_most_common_bit zip2!(counts0 counts1))
38 | epsilon = map_stack!(pick_least_common_bit zip2!(counts0 counts1))
39 | result = mul!(dec_from_bin!gamma dec_from_bin!epsilon)
40 | }
41 |
--------------------------------------------------------------------------------
/examples/aoc_21_05.txt:
--------------------------------------------------------------------------------
1 | result@{
2 | lines = [
3 | ([0 9] [5 9])
4 | ([8 0] [0 8])
5 | ([9 4] [3 4])
6 | ([2 2] [2 1])
7 | ([7 0] [7 4])
8 | ([6 4] [2 0])
9 | ([0 9] [2 9])
10 | ([3 4] [1 4])
11 | ([0 0] [8 8])
12 | ([5 5] [8 2])
13 | ]
14 | is_line_diagonal = in (point0 point1) out none?[
15 | equal?(point0!0 point1!0)
16 | equal?(point0!1 point1!1)
17 | ]
18 | fill_line = in (point0 point1) out positions@{
19 | x0 = point0!0
20 | x1 = point1!0
21 | y0 = point0!1
22 | y1 = point1!1
23 | dx = if less?(x0 x1) then 1 less?(x1 x0) then -1 else 0
24 | dy = if less?(y0 y1) then 1 less?(y1 y0) then -1 else 0
25 | x = x0
26 | y = y0
27 | positions = [[x y]]
28 | while unequal?(take!positions point1)
29 | x = add!(x dx)
30 | y = add!(y dy)
31 | positions += [x y]
32 | end
33 | }
34 | has_many_overlaps = in (point num_overlaps) out less?(1 num_overlaps)
35 | axis_aligned_lines = clear_if!(is_line_diagonal lines)
36 | filled_lines = merge_stack!map_stack!(fill_line axis_aligned_lines)
37 | counts = count_elements!filled_lines
38 | result = count_if!(has_many_overlaps counts)
39 | }
40 |
--------------------------------------------------------------------------------
/examples/aoc_21_06.txt:
--------------------------------------------------------------------------------
1 | num_fishes@{
2 | fishes = [3 4 3 1 2]
3 | simulate = in input out output@{
4 | output = []
5 | for fish in input
6 | output =
7 | if fish then
8 | put!(dec!fish output)
9 | else
10 | put_each!([6 8] output)
11 | end
12 | }
13 | days = 80
14 | for days
15 | fishes = simulate!fishes
16 | end
17 | num_fishes = count!fishes
18 | }
19 |
--------------------------------------------------------------------------------
/examples/aoc_21_08.txt:
--------------------------------------------------------------------------------
1 | result@{
2 | strings = [
3 | "fdgacbe" "cefdb" "cefbgd" "gcbe"
4 | "fcgedb" "cgb" "dgebacf" "gc"
5 | "cg" "cg" "fdcagb" "cbg"
6 | "efabcd" "cedba" "gadfec" "cb"
7 | "gecf" "egdcabf" "bgf" "bfgea"
8 | "gebdcfa" "ecba" "ca" "fadegcb"
9 | "cefg" "dcbef" "fcge" "gbcadfe"
10 | "ed" "bcgafe" "cdgba" "cbgef"
11 | "gbdfcae" "bgc" "cg" "cgb"
12 | "fgae" "cfgab" "fg" "bagce"
13 | ]
14 | is_easy_digit = in string out
15 | is count!string
16 | 2 then yes
17 | 4 then yes
18 | 3 then yes
19 | 7 then yes
20 | else no
21 | result = count_if!(is_easy_digit strings)
22 | }
23 |
--------------------------------------------------------------------------------
/examples/aoc_21_09.txt:
--------------------------------------------------------------------------------
1 | risk_levels@{
2 | map = [
3 | "bbbbbbbbbbbb"
4 | "b2199943210b"
5 | "b3987894921b"
6 | "b9856789892b"
7 | "b8767896789b"
8 | "b9899965678b"
9 | "bbbbbbbbbbbb"
10 | ]
11 | parse_height = in character out
12 | is character
13 | 'b' then 10
14 | else parse_digit!character
15 | height = count!map
16 | width = count!take!map
17 | num_low_points = 0
18 | risk_levels = 0
19 | y = 0
20 | while less?(y sub!(height 2))
21 | row_above = map!0
22 | row_middle = map!1
23 | row_below = map!2
24 | x = 0
25 | while less?(x sub!(width 2))
26 | point = parse_height!row_middle!1
27 | neighbours = [
28 | parse_height!row_above!1
29 | parse_height!row_middle!0
30 | parse_height!row_middle!2
31 | parse_height!row_below!1
32 | ]
33 | is_low_point = less?(point min_item!neighbours)
34 | for is_low_point
35 | num_low_points = inc!num_low_points
36 | risk_levels = sum![risk_levels point 1]
37 | end
38 | row_above = drop!row_above
39 | row_middle = drop!row_middle
40 | row_below = drop!row_below
41 | x = inc!x
42 | end
43 | map = drop!map
44 | y = inc!y
45 | end
46 | }
47 |
--------------------------------------------------------------------------------
/examples/aoc_21_10.txt:
--------------------------------------------------------------------------------
1 | score@{
2 | strings = [
3 | "[({(<(())[]>[[{[]{<()<>>"
4 | "[(()[<>])]({[<{<<[]>>("
5 | "{([(<{}[<>[]}>{[]{[(<()>"
6 | "(((({<>}<{<{<>}{[]{[]{}"
7 | "[[<[([]))<([[{}[[()]]]"
8 | "[{[{({}]{}}([{[{{{}}([]"
9 | "{<[[]]>}<{[{[{[]{()[[[]"
10 | "[<(<(<(<{}))><([]([]()"
11 | "<{([([[(<>()){}]>(<<{{"
12 | "<{([{{}}[<[[[<>{}]]]>[]]"
13 | ]
14 | get_closer = in c out is c
15 | '(' then ')'
16 | '{' then '}'
17 | '[' then ']'
18 | '<' then '>'
19 | else '.'
20 | score_string = in string out score@result@{
21 | result = {
22 | score = 0
23 | open = put!(take!string "")
24 | }
25 | string = drop!string
26 | while all?[not?score@result open@result string]
27 | c = take!string
28 | string = drop!string
29 | open = open@result
30 | result = is c
31 | get_closer!take!open then {open = drop!open score = 0}
32 | '{' then {open = put!(c open) score = 0}
33 | '(' then {open = put!(c open) score = 0}
34 | '[' then {open = put!(c open) score = 0}
35 | '<' then {open = put!(c open) score = 0}
36 | ')' then {open = "" score = 3}
37 | ']' then {open = "" score = 57}
38 | '}' then {open = "" score = 1197}
39 | '>' then {open = "" score = 25137}
40 | else {open = "" score = 1000000000}
41 | end
42 | }
43 | score = sum!map_stack!(score_string strings)
44 | }
45 |
--------------------------------------------------------------------------------
/examples/aoc_21_13.txt:
--------------------------------------------------------------------------------
1 | folded_points@{
2 | points = [
3 | (6 10)
4 | (0 14)
5 | (9 10)
6 | (0 3)
7 | (10 4)
8 | (4 11)
9 | (6 0)
10 | (6 12)
11 | (4 1)
12 | (0 13)
13 | (10 12)
14 | (3 4)
15 | (3 0)
16 | (8 4)
17 | (1 10)
18 | (2 14)
19 | (8 10)
20 | (9 0)
21 | ]
22 | fold_y = in (x y) out
23 | if less?(y 7) then
24 | (x y)
25 | else
26 | (x sub!(mul!(2 7) y))
27 | fold_x = in (x y) out
28 | if less?(x 5) then
29 | (x y)
30 | else
31 | (sub!(mul!(2 5) x) y)
32 | folded_points = map_stack!(fold_x points)
33 | }
34 |
--------------------------------------------------------------------------------
/examples/aoc_21_14.txt:
--------------------------------------------------------------------------------
1 | result@{
2 | start = "NNCB"
3 | rules = [
4 | ("CH" 'B')
5 | ("HH" 'N')
6 | ("CB" 'H')
7 | ("NH" 'C')
8 | ("HB" 'C')
9 | ("HC" 'B')
10 | ("HN" 'C')
11 | ("NN" 'C')
12 | ("BH" 'H')
13 | ("NC" 'B')
14 | ("NB" 'B')
15 | ("BN" 'B')
16 | ("BB" 'N')
17 | ("BC" 'B')
18 | ("CC" 'N')
19 | ("CN" 'C')
20 | ]
21 | polymer = start
22 | n = 10
23 | for n
24 | result = ""
25 | while drop!polymer
26 | a = polymer!0
27 | b = polymer!1
28 | prefix = put_each!([b a] "")
29 | remaining_rules = rules
30 | for rule in remaining_rules
31 | prefix = if
32 | drop!drop!prefix then prefix
33 | equal?(rule!0 prefix) then put_each!([b rule!1 a] "")
34 | else prefix
35 | end
36 | while drop!prefix
37 | result += take!prefix
38 | prefix = drop!prefix
39 | end
40 | polymer = drop!polymer
41 | end
42 | result += take!polymer
43 | polymer = reverse!result
44 | end
45 | result = polymer
46 |
47 | alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
48 | counts = map_stack!(in c out count_item!(c polymer) alphabet)
49 | counts = clear_if!(in c out equal?(c 0) counts)
50 | result = sub!(max_item!counts min_item!counts)
51 | }
52 |
--------------------------------------------------------------------------------
/examples/aoc_22_01.txt:
--------------------------------------------------------------------------------
1 | result@{
2 | input =
3 | "1000
4 | 2000
5 | 3000
6 |
7 | 4000
8 |
9 | 5000
10 | 6000
11 |
12 | 7000
13 | 8000
14 | 9000
15 |
16 | 10000"
17 | groups = split!("" split!(newline input))
18 | aggregate_group = in strings out
19 | sum!map!(parse_natural_number strings)
20 | result = max_item!map!(aggregate_group groups)
21 | }
22 |
--------------------------------------------------------------------------------
/examples/aoc_22_02.txt:
--------------------------------------------------------------------------------
1 | result@{
2 | input =
3 | "A Y
4 | B X
5 | C Z"
6 |
7 | OPPONENT_ROCK = 'A'
8 | OPPONENT_PAPER = 'B'
9 | OPPONENT_SCISSORS = 'C'
10 |
11 | YOU_ROCK = 'X'
12 | YOU_PAPER = 'Y'
13 | YOU_SCISSORS = 'Z'
14 |
15 | SCORE_TABLE = <
16 | (YOU_ROCK <(OPPONENT_ROCK 4) (OPPONENT_PAPER 1) (OPPONENT_SCISSORS 7)>)
17 | (YOU_PAPER <(OPPONENT_ROCK 8) (OPPONENT_PAPER 5) (OPPONENT_SCISSORS 2)>)
18 | (YOU_SCISSORS <(OPPONENT_ROCK 3) (OPPONENT_PAPER 9) (OPPONENT_SCISSORS 6)>)
19 | >
20 | parse_round = in round out (round!0 round!2)
21 | score_round = in (opponent you) out get!(opponent get!(you SCORE_TABLE <>) -inf)
22 | rounds = map!(parse_round split!(newline input))
23 | scores = map!(score_round rounds)
24 | result = sum!scores
25 | }
26 |
--------------------------------------------------------------------------------
/examples/aoc_22_03.txt:
--------------------------------------------------------------------------------
1 | result@{
2 | input =
3 | "vJrwpWtwJgWrhcsFMMfFFhFp
4 | jqHRNqRjqzjGDLGLrsFMfFZSrLrFZsSL
5 | PmmdzqPrVvPwwTWBwg
6 | wMqvLMZHhHMvwLHjbvcjnnSBnvTQFn
7 | ttgJtRGJQctTZtZT
8 | CrZsJsPPZsGzwwsLwLmpwMDw"
9 |
10 | common_item = in string out result@{
11 | size = count!string
12 | half_size = div!(size 2)
13 | first_string = take_many!(half_size string)
14 | second_string = drop_many!(half_size string)
15 | products = cartesian_product2!(first_string second_string)
16 | pick_first = in (a b) out a
17 | result = pick_first!take!drop_while!(unequal products)
18 | }
19 | priority = in c out
20 | if is_lower?c then
21 | add!(sub!(number!c number!'a') 1)
22 | else
23 | add!(sub!(number!c number!'A') 27)
24 | common_items = map_stack!(common_item split!(newline input))
25 | priorities = map_stack!(priority common_items)
26 | result = sum!priorities
27 | }
28 |
--------------------------------------------------------------------------------
/examples/aoc_22_04.txt:
--------------------------------------------------------------------------------
1 | result@{
2 | input =
3 | "2-4,6-8
4 | 2-3,4-5
5 | 5-7,7-9
6 | 2-8,3-7
7 | 6-6,4-6
8 | 2-6,4-8"
9 | parse_section = in string out map_stack!(
10 | parse_natural_number split!('-' string)
11 | )
12 | parse_row = in row out map_stack!(
13 | parse_section split!(',' row)
14 | )
15 | does_contain = in sections out result@{
16 | a = get0!get0!sections
17 | b = get1!get0!sections
18 | c = get0!get1!sections
19 | d = get1!get1!sections
20 | result = or?[
21 | and?[less_or_equal?(a c) less_or_equal?(d b)]
22 | and?[less_or_equal?(c a) less_or_equal?(b d)]
23 | ]
24 | }
25 | sections = map_stack!(parse_row split!(newline input))
26 | result = count_if!(does_contain sections)
27 | }
28 |
--------------------------------------------------------------------------------
/examples/aoc_22_05.txt:
--------------------------------------------------------------------------------
1 | result@{
2 | input =
3 | " [D]
4 | [N] [C]
5 | [Z] [M] [P]
6 | 1 2 3
7 |
8 | move 1 from 2 to 1
9 | move 3 from 1 to 3
10 | move 2 from 2 to 1
11 | move 1 from 1 to 2"
12 | lines = split!(newline input)
13 | groups = split!("" lines)
14 | stacks = groups!0
15 | movements = groups!1
16 | stacks = map!(make_stack stacks)
17 | stacks = transpose!stacks
18 | is_not_upper_case = in c out not?is_upper?c
19 | is_empty = in container out not?container
20 | strip_characters = in row out clear_if!(is_not_upper_case row)
21 | stacks = map_stack!(strip_characters stacks)
22 | stacks = clear_if!(is_empty stacks)
23 | stacks = make_table!enumerate!stacks
24 | for movement in movements
25 | words = split!(' ' movement)
26 | n = parse_natural_number!words!1
27 | source = dec!parse_natural_number!words!3
28 | target = dec!parse_natural_number!words!5
29 | for n
30 | source_stack = stacks!source
31 | item = take!source_stack
32 | source_stack = drop!source_stack
33 | target_stack = stacks!target
34 | target_stack += item
35 | stacks += (source source_stack)
36 | stacks += (target target_stack)
37 | end
38 | end
39 | result = map_string!(get0 get_values!stacks)
40 | }
41 |
--------------------------------------------------------------------------------
/examples/aoc_22_06.txt:
--------------------------------------------------------------------------------
1 | result@{
2 | input = "mjqjpqmgbljsphdztnvjfqwrcgsmlb"
3 | N = 4
4 | n = N
5 | while less?(count!unique!take_many!(N input) N)
6 | input = drop!input
7 | n = inc!n
8 | end
9 | result = n
10 | }
11 |
--------------------------------------------------------------------------------
/examples/aoc_22_07.txt:
--------------------------------------------------------------------------------
1 | result@{
2 | input = "$ cd /
3 | $ ls
4 | dir a
5 | 14848514 b.txt
6 | 8504156 c.dat
7 | dir d
8 | $ cd a
9 | $ ls
10 | dir e
11 | 29116 f
12 | 2557 g
13 | 62596 h.lst
14 | $ cd e
15 | $ ls
16 | 584 i
17 | $ cd ..
18 | $ cd ..
19 | $ cd d
20 | $ ls
21 | 4060174 j
22 | 8033020 d.log
23 | 5626152 d.ext
24 | 7214296 k"
25 |
26 | Strings = [String]
27 |
28 | parse_command_string = in String:command_string out Strings :
29 | clear_item!("" split!(newline drop!command_string))
30 | command_strings = Strings : drop!split!('$' input)
31 | commands = map_stack!(parse_command_string command_strings)
32 |
33 | State = {
34 | current_path = Strings
35 | path_and_size = <(Strings Number)>
36 | }
37 | state = State:{
38 | current_path = []
39 | path_and_size = <>
40 | }
41 | handle_cd = in (Strings:command State:state) out State:{
42 | current_path = Strings : current_path@state
43 | path_and_size = path_and_size@state
44 | new_directory = String : drop_many!(3 take!command)
45 | current_path = Strings : is new_directory
46 | ".." then Strings:drop!current_path
47 | else put!(String:new_directory Strings:current_path)
48 | }
49 | handle_ls = in (Strings:command State:state) out State:{
50 | current_path = Strings : current_path@state
51 | path_and_size = path_and_size@state
52 | command = Strings : drop!command
53 | for item in command
54 | parts = split!(' ' item)
55 | size = String : parts!0
56 | file = String : parts!1
57 | file_path = Strings : put!(file current_path)
58 | path_and_size = is Character : take!size
59 | 'd' then path_and_size
60 | else put!((Strings:file_path Number:parse_natural_number!size) path_and_size)
61 | end
62 | }
63 | handle_command = in (Strings:command State:state) out State:is take!take!command
64 | 'c' then State:handle_cd!(command state)
65 | 'l' then State:handle_ls!(command state)
66 | else State:state
67 |
68 | state = fold!(handle_command commands state)
69 |
70 | update_directory_sizes = in (path_and_size directory_sizes) out directory_sizes@{
71 | path = path_and_size!0
72 | size = path_and_size!1
73 | path = drop!path
74 | for path
75 | directory_size = get!(path directory_sizes 0)
76 | directory_size = add!(directory_size size)
77 | directory_sizes = put!((path directory_size) directory_sizes)
78 | end
79 | }
80 | directory_sizes = fold!(update_directory_sizes path_and_size@state <>)
81 | sizes = get_values!directory_sizes
82 | is_big_size = in size out less?(100000 size)
83 | result = sum!clear_if!(is_big_size sizes)
84 | }
85 |
--------------------------------------------------------------------------------
/examples/aoc_22_08.txt:
--------------------------------------------------------------------------------
1 | result@{
2 | input = "30373
3 | 25512
4 | 65332
5 | 33549
6 | 35390"
7 |
8 | parse_line = in string out map_stack!(parse_digit string)
9 | lines = split!(newline input)
10 | array = map_stack!(parse_line lines)
11 |
12 | is_max_so_far = in numbers out reverse!result@{
13 | maximum = neg!inf
14 | result = []
15 | for number in numbers
16 | result += less?(maximum number)
17 | maximum = max!(maximum number)
18 | end
19 | }
20 |
21 | get0 = in array out array
22 | get1 = in array out transpose!array
23 | get2 = in array out map_stack!(reverse array)
24 | get3 = in array out map_stack!(reverse transpose!array)
25 |
26 | get0inv = in array out array
27 | get1inv = in array out transpose!array
28 | get2inv = in array out map_stack!(reverse array)
29 | get3inv = in array out transpose!map_stack!(reverse array)
30 |
31 | mask0 = merge_stack!get0inv!map_stack!(is_max_so_far get0!array)
32 | mask1 = merge_stack!get1inv!map_stack!(is_max_so_far get1!array)
33 | mask2 = merge_stack!get2inv!map_stack!(is_max_so_far get2!array)
34 | mask3 = merge_stack!get3inv!map_stack!(is_max_so_far get3!array)
35 |
36 | is_visible = in (a b c d) out any?[a b c d]
37 | result = count_if!(is_visible zip4!(mask0 mask1 mask2 mask3))
38 | }
39 |
--------------------------------------------------------------------------------
/examples/aoc_22_09.txt:
--------------------------------------------------------------------------------
1 | result@{
2 | input = "R 4
3 | U 4
4 | L 3
5 | D 1
6 | R 4
7 | D 1
8 | L 5
9 | R 2"
10 |
11 | head_movements = <
12 | ('L' [-1 0])
13 | ('R' [+1 0])
14 | ('U' [0 +1])
15 | ('D' [0 -1])
16 | >
17 | tail_movements = <
18 | ([-1 +2] [-1 +1])
19 | ([+0 +2] [+0 +1])
20 | ([+1 +2] [+1 +1])
21 |
22 | ([-1 -2] [-1 -1])
23 | ([+0 -2] [+0 -1])
24 | ([+1 -2] [+1 -1])
25 |
26 | ([+2 -1] [+1 -1])
27 | ([+2 +0] [+1 +0])
28 | ([+2 +1] [+1 +1])
29 |
30 | ([-2 -1] [-1 -1])
31 | ([-2 +0] [-1 +0])
32 | ([-2 +1] [-1 +1])
33 | >
34 |
35 | heads = [[0 0]]
36 | tails = [[0 0]]
37 |
38 | lines = split!(newline input)
39 | for line in lines
40 | direction = line!0
41 | steps = parse_digit!line!2
42 | for steps
43 | head = take!heads
44 | tail = take!tails
45 | head_movement = head_movements!direction
46 | head = addv!(head head_movement)
47 | offset = subv!(head tail)
48 | tail_movement = get!(offset tail_movements [0 0])
49 | tail = addv!(tail tail_movement)
50 | tails += tail
51 | heads += head
52 | end
53 | end
54 | result = {
55 | heads = reverse!heads
56 | tails = reverse!tails
57 | }
58 | result = count!unique!tails
59 | }
60 |
--------------------------------------------------------------------------------
/examples/aoc_22_10.txt:
--------------------------------------------------------------------------------
1 | result@{
2 | input = "addx 15
3 | addx -11
4 | addx 6
5 | addx -3
6 | addx 5
7 | addx -1
8 | addx -8
9 | addx 13
10 | addx 4
11 | noop
12 | addx -1
13 | addx 5
14 | addx -1
15 | addx 5
16 | addx -1
17 | addx 5
18 | addx -1
19 | addx 5
20 | addx -1
21 | addx -35
22 | addx 1
23 | addx 24
24 | addx -19
25 | addx 1
26 | addx 16
27 | addx -11
28 | noop
29 | noop
30 | addx 21
31 | addx -15
32 | noop
33 | noop
34 | addx -3
35 | addx 9
36 | addx 1
37 | addx -3
38 | addx 8
39 | addx 1
40 | addx 5
41 | noop
42 | noop
43 | noop
44 | noop
45 | noop
46 | addx -36
47 | noop
48 | addx 1
49 | addx 7
50 | noop
51 | noop
52 | noop
53 | addx 2
54 | addx 6
55 | noop
56 | noop
57 | noop
58 | noop
59 | noop
60 | addx 1
61 | noop
62 | noop
63 | addx 7
64 | addx 1
65 | noop
66 | addx -13
67 | addx 13
68 | addx 7
69 | noop
70 | addx 1
71 | addx -33
72 | noop
73 | noop
74 | noop
75 | addx 2
76 | noop
77 | noop
78 | noop
79 | addx 8
80 | noop
81 | addx -1
82 | addx 2
83 | addx 1
84 | noop
85 | addx 17
86 | addx -9
87 | addx 1
88 | addx 1
89 | addx -3
90 | addx 11
91 | noop
92 | noop
93 | addx 1
94 | noop
95 | addx 1
96 | noop
97 | noop
98 | addx -13
99 | addx -19
100 | addx 1
101 | addx 3
102 | addx 26
103 | addx -30
104 | addx 12
105 | addx -1
106 | addx 3
107 | addx 1
108 | noop
109 | noop
110 | noop
111 | addx -9
112 | addx 18
113 | addx 1
114 | addx 2
115 | noop
116 | noop
117 | addx 9
118 | noop
119 | noop
120 | noop
121 | addx -1
122 | addx 2
123 | addx -37
124 | addx 1
125 | addx 3
126 | noop
127 | addx 15
128 | addx -21
129 | addx 22
130 | addx -6
131 | addx 1
132 | noop
133 | addx 2
134 | addx 1
135 | noop
136 | addx -10
137 | noop
138 | noop
139 | addx 20
140 | addx 1
141 | addx 2
142 | addx 2
143 | addx -6
144 | addx -11
145 | noop
146 | noop
147 | noop"
148 |
149 | state = [1 1]
150 | update_state_noop = in state out
151 | put!(take!state state)
152 | update_state_add = in (state n) out
153 | merge_stack![[add!(n take!state) take!state] state]
154 | update_state = in (line state) out if
155 | equal?(line!0 'n') then update_state_noop!state
156 | equal?(line!5 '-') then update_state_add!(state neg!parse_natural_number!drop_many!(6 line))
157 | else update_state_add!(state parse_natural_number!drop_many!(5 line))
158 |
159 | lines = split!(newline input)
160 | state = reverse!fold!(update_state lines state)
161 | signal_strengths = map_stack!(mul enumerate!state)
162 | indices = [20 60 100 140 180 220]
163 | get_signal_strength = in i out signal_strengths!i
164 | result = sum!map_stack!(get_signal_strength indices)
165 | }
166 |
--------------------------------------------------------------------------------
/examples/aoc_22_12.txt:
--------------------------------------------------------------------------------
1 | result@{
2 | input =
3 | "Sabqponm
4 | abcryxxl
5 | accszExk
6 | acctuvwj
7 | abdefghi"
8 |
9 | count_alphabet = in c out sub!(number!c number!'a')
10 |
11 | heights = <>
12 | start = [nan nan]
13 | goal = [nan nan]
14 | lines = split!(newline input)
15 | y = 0
16 | for line in lines
17 | x = 0
18 | for c in line
19 | start = is c 'S' then [x y] else start
20 | goal = is c 'E' then [x y] else goal
21 | height = is c
22 | 'S' then count_alphabet!'a'
23 | 'E' then count_alphabet!'z'
24 | else count_alphabet!c
25 | heights += ([x y] height)
26 | x = inc!x
27 | end
28 | y = inc!y
29 | end
30 |
31 | visited = <(start yes)>
32 | active_frontier = [start]
33 | steps = 0
34 | while not!get!(goal visited no)
35 | next_frontier = []
36 | for position in active_frontier
37 | height = heights!position
38 | x = position!0
39 | y = position!1
40 | neighbours = [[x inc!y] [x dec!y] [inc!x y] [dec!x y]]
41 | for neighbour in neighbours
42 | neighbour_height = get!(neighbour heights inf)
43 | height_difference = sub!(neighbour_height height)
44 | ok_neighbour = and?[
45 | less?(height_difference 2)
46 | not!get!(neighbour visited no)
47 | ]
48 | for ok_neighbour
49 | next_frontier = put!(neighbour next_frontier)
50 | visited = put!((neighbour yes) visited)
51 | end
52 | end
53 | end
54 | steps = inc!steps
55 | active_frontier = next_frontier
56 | end
57 |
58 | result = steps
59 | }
60 |
--------------------------------------------------------------------------------
/examples/aoc_22_13.txt:
--------------------------------------------------------------------------------
1 | result@{
2 | input =
3 | "[1,1,3,1,1]
4 | [1,1,5,1,1]
5 |
6 | [[1],[2,3,4]]
7 | [[1],4]
8 |
9 | [9]
10 | [[8,7,6]]
11 |
12 | [[4,4],4,4]
13 | [[4,4],4,4,4]
14 |
15 | [7,7,7,7]
16 | [7,7,7]
17 |
18 | []
19 | [3]
20 |
21 | [[[]]]
22 | [[]]
23 |
24 | [1,[2,[3,[4,[5,6,7]]]],8,9]
25 | [1,[2,[3,[4,[5,6,0]]]],8,9]"
26 |
27 | pairs = split!("" split!(newline input))
28 |
29 | is_right_order_integers = in pair out
30 | less?(
31 | parse_natural_number!pair!0
32 | parse_natural_number!pair!1
33 | )
34 | is_right_order_lists = in pair out result@{
35 | left = pair!0
36 | right = pair!1
37 | is_right_order_so_far = yes
38 | while and?[left right is_right_order_so_far]
39 | is_right_order_so_far = and?[
40 | is_right_order_so_far
41 | is_right_order?[left right]
42 | ]
43 | left = drop!left
44 | right = drop!right
45 | end
46 | is_right_order_so_far = and?[
47 | is_right_order_so_far less_or_equal?(count!left count!right)
48 | ]
49 | }
50 | is_right_order = in pair out result@{
51 | left = pair!0
52 | right = pair!1
53 | result = yes
54 | }
55 |
56 | index = 1
57 | index_sum = 0
58 | for pair in pairs
59 | index_sum =
60 | if is_right_order?pair then
61 | add!(index index_sum)
62 | else
63 | index_sum
64 | index = inc!index
65 | end
66 | result = index_sum
67 | }
68 |
--------------------------------------------------------------------------------
/examples/aoc_22_14.txt:
--------------------------------------------------------------------------------
1 | result@{
2 | input =
3 | "498,4 -> 498,6 -> 496,6
4 | 503,4 -> 502,4 -> 502,9 -> 494,9"
5 | parse_point = in string out
6 | map_stack!(parse_natural_number split!(',' string))
7 | parse_path = in line out
8 | map_stack!(parse_point split!('-' clear_item!('>' clear_item!(' ' line))))
9 | lines = split!(newline input)
10 | paths = map_stack!(parse_path lines)
11 |
12 | get_direction = in (source target) out direction@{
13 | delta = subv!(target source)
14 | dx = delta!0
15 | dy = delta!1
16 | direction = [
17 | if less?(dx 0) then -1 less?(0 dx) then 1 else 0
18 | if less?(dy 0) then -1 less?(0 dy) then 1 else 0
19 | ]
20 | }
21 |
22 | filled = <>
23 | for path in paths
24 | while drop!path
25 | source = path!0
26 | target = path!1
27 | delta = get_direction!(source target)
28 | point = source
29 | while unequal?(point target)
30 | filled += (point yes)
31 | point = addv!(point delta)
32 | end
33 | filled += (point yes)
34 | path = drop!path
35 | end
36 | end
37 |
38 | maxy = max_item!map_stack!(get0 get_keys!filled)
39 |
40 | continue_spawning = yes
41 | n = 0
42 | while continue_spawning
43 | point = [500 0]
44 | continue_moving = yes
45 | while and?[continue_moving less?(point!1 maxy)]
46 | targets = [
47 | addv!(point [0 1])
48 | addv!(point [-1 1])
49 | addv!(point [+1 1])
50 | ]
51 | is_target_filled = in target out get!(target filled no)
52 | targets = drop_while!(is_target_filled targets)
53 | continue_moving = if targets then yes else no
54 | point = if continue_moving then take!targets else point
55 | filled = if continue_moving then filled else put!((point yes) filled)
56 | end
57 | continue_spawning = less?(point!1 maxy)
58 | n = inc!n
59 | end
60 |
61 | result = dec!n
62 | }
63 |
--------------------------------------------------------------------------------
/examples/aoc_22_18.txt:
--------------------------------------------------------------------------------
1 | result@{
2 | input =
3 | "2,2,2
4 | 1,2,2
5 | 3,2,2
6 | 2,1,2
7 | 2,3,2
8 | 2,2,1
9 | 2,2,3
10 | 2,2,4
11 | 2,2,6
12 | 1,2,5
13 | 3,2,5
14 | 2,1,5
15 | 2,3,5"
16 |
17 | X_PLANE = 0
18 | Y_PLANE = 1
19 | Z_PLANE = 2
20 |
21 | parse_point = in string out map_stack!(parse_natural_number split!(',' string))
22 | points = map_stack!(parse_point split!(newline input))
23 |
24 | plane_counts = <>
25 | for point in points
26 | x = point!0
27 | y = point!1
28 | z = point!2
29 |
30 | p0 = [x y z X_PLANE]
31 | p1 = [x y z Y_PLANE]
32 | p2 = [x y z Z_PLANE]
33 | p3 = [inc!x y z X_PLANE]
34 | p4 = [x inc!y z Y_PLANE]
35 | p5 = [x y inc!z Z_PLANE]
36 |
37 | planes = [p0 p1 p2 p3 p4 p5]
38 | for plane in planes
39 | n = get!(plane plane_counts 0)
40 | plane_counts += (plane inc!n)
41 | end
42 | end
43 |
44 | is_surface_plane = in (plane counts) out equal?(counts 1)
45 | result = count_if!(is_surface_plane plane_counts)
46 | }
47 |
--------------------------------------------------------------------------------
/examples/aoc_22_21.txt:
--------------------------------------------------------------------------------
1 | result@{
2 | input =
3 | "root: pppw + sjmn
4 | dbpl: 5
5 | cczh: sllz + lgvd
6 | zczc: 2
7 | ptdq: humn - dvpt
8 | dvpt: 3
9 | lfqf: 4
10 | humn: 5
11 | ljgn: 2
12 | sjmn: drzm * dbpl
13 | sllz: 4
14 | pppw: cczh / lfqf
15 | lgvd: ljgn * ptdq
16 | drzm: hmdt - zczc
17 | hmdt: 32"
18 |
19 | parse_line = in string out (take_many!(4 string) drop_many!(6 string))
20 | monkey_strings = map_table!(parse_line split!(newline input))
21 | operations = <'+':add '-':sub '*':mul '/':div>
22 | evaluate_operation = in string out dynamic result@{
23 | left = evaluate_node!take_many!(4 string)
24 | right = evaluate_node!drop_many!(7 string)
25 | operation = operations!string!5
26 | result = operation!(left right)
27 | }
28 | evaluate_node = in key out result@{
29 | string = monkey_strings!key
30 | result =
31 | if is_digit?take!string then
32 | parse_natural_number!string
33 | else
34 | evaluate_operation!string
35 | }
36 | result = evaluate_node!"root"
37 | }
38 |
--------------------------------------------------------------------------------
/examples/hello_world.txt:
--------------------------------------------------------------------------------
1 | "hello_world"
--------------------------------------------------------------------------------
/examples/raytracer.txt:
--------------------------------------------------------------------------------
1 | result@{
2 |
3 | Intersection = {
4 | distance = Number
5 | position = Numbers
6 | normal = Numbers
7 | color = Numbers
8 | }
9 |
10 | default_intersection = Intersection:{
11 | distance = inf
12 | position = []
13 | normal = []
14 | color = []
15 | }
16 |
17 | Light = {
18 | direction = Numbers
19 | color = Numbers
20 | }
21 |
22 | Sphere = {
23 | position = Numbers
24 | squared_radius = Number
25 | color = Numbers
26 | }
27 |
28 | Lights = [Light]
29 | Spheres = [Sphere]
30 |
31 | World = {
32 | spheres = Spheres
33 | lights = Lights
34 | atmosphere_color = Numbers
35 | }
36 |
37 | muls = in (Number:scalar Numbers:vector) out Numbers:
38 | map!(in x out mul!(scalar x) vector)
39 |
40 | R = 100000.0
41 | MAX_C = 1.0
42 | MIN_C = 0.1
43 |
44 | world = World:{
45 | spheres = [
46 | {position=[-2 0 6] squared_radius=1 color=[MAX_C MAX_C MIN_C]}
47 | {position=[0 0 5] squared_radius=1 color=[MAX_C MIN_C MIN_C]}
48 | {position=[2 0 4] squared_radius=1 color=[mul!(2 MIN_C) mul!(4 MIN_C) MAX_C]}
49 | {position=[0 add!(1 R) 0] squared_radius=mul!(R R) color=[MIN_C MAX_C MIN_C]}
50 | {position=[0 sub!(-1 R) 0] squared_radius=mul!(R R) color=[MAX_C MAX_C MAX_C]}
51 | ]
52 | lights = [
53 | {direction=[+1 +1 +2] color=muls!(0.4 [1 0.8 0.5])}
54 | {direction=[-1 -1 -2] color=muls!(0.4 [0.5 0.5 1])}
55 | ]
56 | atmosphere_color = muls!(0.3 [0.5 0.5 1])
57 | }
58 |
59 | normalize = in Numbers:vector out Numbers:
60 | muls!(div!(1 norm!vector) vector)
61 |
62 | findSingleIntersection = in (Numbers:start Numbers:direction Sphere:sphere) out Intersection:intersection@{
63 | intersection = default_intersection
64 | offset = subv!(position@sphere start)
65 | c = dot!(direction offset)
66 | is_backwards = less?(c 0.0)
67 | for is_backwards
68 | return
69 | end
70 | discriminant = add!(sub!(mul!(c c) squared_norm!offset) squared_radius@sphere)
71 | is_outside = less?(discriminant 0.0)
72 | for is_outside
73 | return
74 | end
75 | intersection = {
76 | distance = sub!(c sqrt!discriminant)
77 | position = addv!(start muls!(distance direction))
78 | normal = normalize!subv!(position position@sphere)
79 | color = color@sphere
80 | }
81 | }
82 |
83 | findIntersection = in (Numbers:start Numbers:direction Spheres:spheres) out Intersection:i1@{
84 | i1 = default_intersection
85 | s = spheres
86 | for sphere in s
87 | i2 = findSingleIntersection!(start direction sphere)
88 | i1 = if less?(distance@i2 distance@i1) then i2 else i1
89 | end
90 | }
91 |
92 | shadeSingleLight = in (Intersection:intersection Light:light) out result@{
93 | geometry = max!(neg!dot!(direction@light normal@intersection) 0.0)
94 | result = muls!(geometry mulv!(color@intersection color@light))
95 | }
96 |
97 | shadeAtmosphere = in (Intersection:intersection Numbers:atmosphere_color) out
98 | Numbers:muls!(sqrt!get2!position@intersection atmosphere_color)
99 |
100 | shade = in (Intersection:intersection world) out Numbers:color@{
101 | color = [1 1 1]
102 | is_infinite = equal?(inf distance@intersection)
103 | for is_infinite
104 | return
105 | end
106 | color = shadeAtmosphere!(intersection atmosphere_color@world)
107 | lights = lights@world
108 | for light in lights
109 | color = addv!(color shadeSingleLight!(intersection light))
110 | end
111 | }
112 |
113 | colorU8fromF64 = in Number:c out Number:round!min!(mul!(255 c) 255)
114 |
115 | writePixel = in (String:image Number:x Number:y Number:width Number:height World:world) out String:image_out@{
116 | start = [0 0 0]
117 | xd = sub!(x div!(width 2))
118 | yd = sub!(y div!(height 2))
119 | zd = div!(height 2)
120 | direction = normalize![xd yd zd]
121 | intersection = findIntersection!(start direction spheres@world)
122 | color = shade!(intersection world)
123 |
124 | image_out = image
125 | image_out ++= serialize_natural_number!colorU8fromF64!color!0
126 | image_out ++= " "
127 | image_out ++= serialize_natural_number!colorU8fromF64!color!1
128 | image_out ++= " "
129 | image_out ++= serialize_natural_number!colorU8fromF64!color!2
130 | image_out ++= " "
131 | }
132 |
133 | writeImage = in World:world out String:image@{
134 | WIDTH = 64
135 | HEIGHT = 40
136 | image = ""
137 | image ++= "P3"
138 | image += newline
139 | image ++= serialize_natural_number!WIDTH
140 | image += newline
141 | image ++= serialize_natural_number!HEIGHT
142 | image += newline
143 | image ++= "255"
144 | image += newline
145 | ys = range!HEIGHT
146 | for y in ys
147 | xs = range!WIDTH
148 | for x in xs
149 | image = writePixel!(image x y WIDTH HEIGHT world)
150 | end
151 | end
152 | image = reverse!image
153 | }
154 |
155 | result = writeImage!world
156 | }
157 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | Manglang is an experimental programming language. Learn more about it in the [documentation](https://mabur.github.io/mang_lang/).
2 |
--------------------------------------------------------------------------------
/src/.gitignore:
--------------------------------------------------------------------------------
1 | /.idea/
2 | /build/
3 | /cmake-build*/
4 |
--------------------------------------------------------------------------------
/src/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 2.8.2)
2 | project(mang_lang_cpp)
3 |
4 | set(CMAKE_CXX_STANDARD 14)
5 |
6 | add_library(shared
7 | built_in_functions/arithmetic.cpp
8 | built_in_functions/binary_tuple.cpp
9 | built_in_functions/built_in_functions.cpp
10 | built_in_functions/container.cpp
11 | passes/bind.cpp
12 | passes/evaluate.cpp
13 | passes/parse.cpp
14 | passes/serialize.cpp
15 | expression.cpp
16 | factory.cpp
17 | mang_lang.cpp
18 | parsing.cpp
19 | )
20 |
21 | add_executable(manglang interpreter.cpp)
22 | add_executable(tests tests.cpp)
23 |
24 | target_link_libraries(manglang shared)
25 | target_link_libraries(tests shared)
26 |
27 | if (NOT MSVC)
28 | target_compile_options(shared PRIVATE -Wall -pedantic -Werror)
29 | target_compile_options(manglang PRIVATE -Wall -pedantic -Werror)
30 | target_compile_options(tests PRIVATE -Wall -pedantic -Werror)
31 | endif()
32 |
33 | #target_link_options(manglang PRIVATE -static)
34 | #target_link_options(tests PRIVATE -static)
35 |
--------------------------------------------------------------------------------
/src/built_in_functions/arithmetic.cpp:
--------------------------------------------------------------------------------
1 | #include "arithmetic.h"
2 |
3 | #include
4 | #include
5 |
6 | #include "binary_tuple.h"
7 | #include "../factory.h"
8 |
9 | namespace arithmetic {
10 | namespace {
11 |
12 | void checkStaticTypeUnaryFunction(Expression in, ExpressionType expected, const std::string& function) {
13 | if (in.type != ANY && in.type != expected) {
14 | throw std::runtime_error(
15 | std::string{"\n\nI have found a static type error."} +
16 | "\nIt happens when calling the built-in function " + function + ". " +
17 | "\nThe function expects to be called with a " + NAMES[expected] + "," +
18 | "\nbut now got " + NAMES[in.type] +
19 | ".\n"
20 | );
21 | }
22 | }
23 |
24 | void checkDynamicTypeUnaryFunction(Expression in, ExpressionType expected, const std::string& function) {
25 | if (in.type != expected) {
26 | throw std::runtime_error(
27 | std::string{"\n\nI have found a dynamic type error."} +
28 | "\nIt happens when calling the built-in function " + function + ". " +
29 | "\nThe function expects to be called with a " + NAMES[expected] + "," +
30 | "\nbut now got " + NAMES[in.type] +
31 | ".\n"
32 | );
33 | }
34 | }
35 |
36 | void checkStaticTypeBinaryFunction(Expression in, ExpressionType expected, const std::string& function) {
37 | const auto tuple = getStaticBinaryTuple(in, function);
38 | const auto left = tuple.left.type;
39 | const auto right = tuple.right.type;
40 | if (left != ANY && left != expected) {
41 | throw std::runtime_error(
42 | std::string{"\n\nI have found a static type error."} +
43 | "\nIt happens when calling the built-in function " + function + ". " +
44 | "\nThe function expects to be called with a tuple of two " + NAMES[expected] + "s," +
45 | "\nbut now the first item in the tuple is " + NAMES[left] +
46 | ".\n"
47 | );
48 | }
49 | if (right != ANY && right != expected) {
50 | throw std::runtime_error(
51 | std::string{"\n\nI have found a static type error."} +
52 | "\nIt happens when calling the built-in function " + function + ". " +
53 | "\nThe function expects to be called with a tuple of two " + NAMES[expected] + "s," +
54 | "\nbut now the second item in the tuple is " + NAMES[right] +
55 | ".\n"
56 | );
57 | }
58 | }
59 |
60 | void checkDynamicTypeBinaryFunction(Expression in, ExpressionType expected, const std::string& function) {
61 | const auto tuple = getDynamicBinaryTuple(in, function);
62 | const auto left = tuple.left.type;
63 | const auto right = tuple.right.type;
64 | if (left != expected) {
65 | throw std::runtime_error(
66 | std::string{"\n\nI have found a dynamic type error."} +
67 | "\nIt happens when calling the built-in function " + function + ". " +
68 | "\nThe function expects to be called with a tuple of two " + NAMES[expected] + "s," +
69 | "\nbut now the first item in the tuple is " + NAMES[left] +
70 | ".\n"
71 | );
72 | }
73 | if (right != expected) {
74 | throw std::runtime_error(
75 | std::string{"\n\nI have found a dynamic type error."} +
76 | "\nIt happens when calling the built-in function " + function + ". " +
77 | "\nThe function expects to be called with a tuple of two " + NAMES[expected] + "s," +
78 | "\nbut now the second item in the tuple is " + NAMES[right] +
79 | ".\n"
80 | );
81 | }
82 | }
83 |
84 | Expression makeNumber(double x) {
85 | return makeNumber(CodeRange{}, x);
86 | }
87 |
88 | template
89 | Expression binaryOperation(Expression in, BinaryOperation operation, const std::string& function) {
90 | const auto tuple = getDynamicBinaryTuple(in, function);
91 | const auto left = getNumber(tuple.left);
92 | const auto right = getNumber(tuple.right);
93 | return makeNumber(operation(left, right));
94 | }
95 |
96 | } // namespace
97 |
98 | Expression add(Expression in) {
99 | checkDynamicTypeBinaryFunction(in, NUMBER, "add");
100 | return binaryOperation(in, std::plus<>(), "add");
101 | }
102 |
103 | Expression mul(Expression in) {
104 | checkDynamicTypeBinaryFunction(in, NUMBER, "mul");
105 | return binaryOperation(in, std::multiplies<>(), "mul");
106 | }
107 |
108 | Expression sub(Expression in) {
109 | checkDynamicTypeBinaryFunction(in, NUMBER, "sub");
110 | return binaryOperation(in, std::minus<>(), "sub");
111 | }
112 |
113 | Expression div(Expression in) {
114 | checkDynamicTypeBinaryFunction(in, NUMBER, "div");
115 | return binaryOperation(in, std::divides<>(), "div");
116 | }
117 |
118 | Expression mod(Expression in) {
119 | checkDynamicTypeBinaryFunction(in, NUMBER, "mod");
120 | return binaryOperation(
121 | in, [](double a, double b){return std::fmod(a, b);}, "mod"
122 | );
123 | }
124 |
125 | Expression less(Expression in) {
126 | checkDynamicTypeBinaryFunction(in, NUMBER, "less");
127 | const auto tuple = getDynamicBinaryTuple(in, "less");
128 | const auto left = getNumber(tuple.left);
129 | const auto right = getNumber(tuple.right);
130 | return left < right ?
131 | Expression{YES, 0, CodeRange{}} : Expression{NO, 0, CodeRange{}};
132 | }
133 |
134 | Expression sqrt(Expression in) {
135 | checkDynamicTypeUnaryFunction(in, NUMBER, "sqrt");
136 | return makeNumber(std::sqrt(getNumber(in)));
137 | }
138 |
139 | Expression round(Expression in) {
140 | checkDynamicTypeUnaryFunction(in, NUMBER, "round");
141 | return makeNumber(std::round(getNumber(in)));
142 | }
143 |
144 | Expression round_up(Expression in) {
145 | checkDynamicTypeUnaryFunction(in, NUMBER, "round_up");
146 | return makeNumber(std::ceil(getNumber(in)));
147 | }
148 |
149 | Expression round_down(Expression in) {
150 | checkDynamicTypeUnaryFunction(in, NUMBER, "round_down");
151 | return makeNumber(std::floor(getNumber(in)));
152 | }
153 |
154 | Expression ascii_number(Expression in) {
155 | checkDynamicTypeUnaryFunction(in, CHARACTER, "ascii_number");
156 | return makeNumber(getCharacter(in));
157 | }
158 |
159 | Expression ascii_character(Expression in) {
160 | checkDynamicTypeUnaryFunction(in, NUMBER, "ascii_character");
161 | return makeCharacter(CodeRange{}, static_cast(getNumber(in)));
162 | }
163 |
164 | Expression FunctionNumberToNumber::operator()(Expression in) const {
165 | checkStaticTypeUnaryFunction(in, NUMBER, name);
166 | return makeNumber(1);
167 | }
168 |
169 | Expression FunctionNumberToCharacter::operator()(Expression in) const {
170 | checkStaticTypeUnaryFunction(in, NUMBER, name);
171 | return makeCharacter(CodeRange{}, 'a');
172 | }
173 | Expression FunctionCharacterToNumber::operator()(Expression in) const {
174 | checkStaticTypeUnaryFunction(in, CHARACTER, name);
175 | return makeNumber(1);
176 | }
177 |
178 | Expression FunctionNumberNumberToBoolean::operator()(Expression in) const {
179 | checkStaticTypeBinaryFunction(in, NUMBER, name);
180 | return Expression{YES, 0, CodeRange{}};
181 | }
182 |
183 | Expression FunctionNumberNumberToNumber::operator()(Expression in) const {
184 | checkStaticTypeBinaryFunction(in, NUMBER, name);
185 | return makeNumber(1);
186 | }
187 |
188 | }
189 |
--------------------------------------------------------------------------------
/src/built_in_functions/arithmetic.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | struct Expression;
6 |
7 | namespace arithmetic {
8 |
9 | Expression add(Expression in);
10 | Expression mul(Expression in);
11 | Expression sub(Expression in);
12 | Expression div(Expression in);
13 | Expression mod(Expression in);
14 | Expression less(Expression in);
15 |
16 | Expression sqrt(Expression in);
17 | Expression round(Expression in);
18 | Expression round_up(Expression in);
19 | Expression round_down(Expression in);
20 | Expression ascii_number(Expression in);
21 | Expression ascii_character(Expression in);
22 |
23 | struct FunctionNumberToNumber {
24 | std::string name;
25 | Expression operator()(Expression in) const;
26 | };
27 |
28 | struct FunctionNumberToCharacter {
29 | std::string name;
30 | Expression operator()(Expression in) const;
31 | };
32 |
33 | struct FunctionCharacterToNumber {
34 | std::string name;
35 | Expression operator()(Expression in) const;
36 | };
37 |
38 | struct FunctionNumberNumberToNumber {
39 | std::string name;
40 | Expression operator()(Expression in) const;
41 | };
42 |
43 | struct FunctionNumberNumberToBoolean {
44 | std::string name;
45 | Expression operator()(Expression in) const;
46 | };
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/src/built_in_functions/binary_tuple.cpp:
--------------------------------------------------------------------------------
1 | #include "binary_tuple.h"
2 |
3 | #include "../factory.h"
4 |
5 | BinaryTuple getDynamicBinaryTuple(Expression in, const std::string& function) {
6 | if (in.type != EVALUATED_TUPLE) {
7 | throw std::runtime_error{
8 | "I found a dynamic type error while calling the function " + function + ". " +
9 | "The function expected a tuple of two items, " +
10 | "but it got a " + NAMES[in.type]
11 | };
12 | }
13 | const auto evaluated_tuple = storage.evaluated_tuples.at(in.index);
14 | const auto tuple_count = evaluated_tuple.last - evaluated_tuple.first;
15 | if (tuple_count != 2) {
16 | throw std::runtime_error{
17 | "I found a dynamic type error while calling the function " + function + ". " +
18 | "The function expected a tuple of two items, " +
19 | "but it got " + std::to_string(tuple_count) + " items."
20 | };
21 | }
22 | const auto left = storage.expressions.at(evaluated_tuple.first + 0);
23 | const auto right = storage.expressions.at(evaluated_tuple.first + 1);
24 | return BinaryTuple{left, right};
25 | }
26 |
27 | BinaryTuple getStaticBinaryTuple(Expression in, const std::string& function) {
28 | if (in.type != EVALUATED_TUPLE) {
29 | throw std::runtime_error{
30 | "I found a static type error while calling the function " + function + ". " +
31 | "The function expected a tuple of two items, " +
32 | "but it got a " + NAMES[in.type]
33 | };
34 | }
35 | const auto evaluated_tuple = storage.evaluated_tuples.at(in.index);
36 | const auto count = evaluated_tuple.last - evaluated_tuple.first;
37 | if (count != 2) {
38 | throw std::runtime_error{
39 | "I found a static type error while calling the function " + function + ". " +
40 | "The function expected a tuple of two items, " +
41 | "but it got " + std::to_string(count) + " items."
42 | };
43 | }
44 | const auto left = storage.expressions.at(evaluated_tuple.first + 0);
45 | const auto right = storage.expressions.at(evaluated_tuple.first + 1);
46 | return BinaryTuple{left, right};
47 | }
48 |
--------------------------------------------------------------------------------
/src/built_in_functions/binary_tuple.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | #include "../expression.h"
6 |
7 | struct BinaryTuple {
8 | Expression left;
9 | Expression right;
10 | };
11 |
12 | BinaryTuple getDynamicBinaryTuple(Expression in, const std::string& function);
13 | BinaryTuple getStaticBinaryTuple(Expression in, const std::string& function);
14 |
--------------------------------------------------------------------------------
/src/built_in_functions/built_in_functions.cpp:
--------------------------------------------------------------------------------
1 | #include "built_in_functions.h"
2 |
3 | #include "../factory.h"
4 | #include "arithmetic.h"
5 | #include "container.h"
6 |
7 | Definition makeDefinitionBuiltIn(const std::string& name, std::function function) {
8 | static size_t i = 0;
9 | return {
10 | {makeName(CodeRange{}, name).index, i++},
11 | makeFunctionBuiltIn(CodeRange{}, {function}),
12 | };
13 | }
14 |
15 | Definition makeDefinitionBuiltInTyped(const std::string& name, std::function function) {
16 | static size_t i = 0;
17 | return {
18 | {makeName(CodeRange{}, name).index, i++},
19 | makeFunctionBuiltIn(CodeRange{}, {function})
20 | };
21 | }
22 |
23 | Expression builtIns() {
24 | const auto definitions = std::vector{
25 | makeDefinitionBuiltIn("clear", container_functions::clear),
26 | makeDefinitionBuiltIn("put", container_functions::put),
27 | makeDefinitionBuiltIn("take", container_functions::take),
28 | makeDefinitionBuiltIn("drop", container_functions::drop),
29 | makeDefinitionBuiltIn("get", container_functions::get),
30 | makeDefinitionBuiltIn("add", arithmetic::add),
31 | makeDefinitionBuiltIn("mul", arithmetic::mul),
32 | makeDefinitionBuiltIn("sub", arithmetic::sub),
33 | makeDefinitionBuiltIn("div", arithmetic::div),
34 | makeDefinitionBuiltIn("mod", arithmetic::mod),
35 | makeDefinitionBuiltIn("less", arithmetic::less),
36 | makeDefinitionBuiltIn("round", arithmetic::round),
37 | makeDefinitionBuiltIn("round_up", arithmetic::round_up),
38 | makeDefinitionBuiltIn("round_down", arithmetic::round_down),
39 | makeDefinitionBuiltIn("sqrt", arithmetic::sqrt),
40 | makeDefinitionBuiltIn("number", arithmetic::ascii_number),
41 | makeDefinitionBuiltIn("character", arithmetic::ascii_character),
42 | };
43 | return makeEvaluatedDictionary(CodeRange{},
44 | EvaluatedDictionary{Expression{}, definitions}
45 | );
46 | }
47 |
48 | Expression builtInsTypes() {
49 | const auto definitions = std::vector{
50 | makeDefinitionBuiltInTyped("clear", container_functions::clearTyped),
51 | makeDefinitionBuiltInTyped("put", container_functions::putTyped),
52 | makeDefinitionBuiltInTyped("take", container_functions::takeTyped),
53 | makeDefinitionBuiltInTyped("drop", container_functions::dropTyped),
54 | makeDefinitionBuiltInTyped("get", container_functions::getTyped),
55 | makeDefinitionBuiltInTyped("add", arithmetic::FunctionNumberNumberToNumber{"add"}),
56 | makeDefinitionBuiltInTyped("mul", arithmetic::FunctionNumberNumberToNumber{"mul"}),
57 | makeDefinitionBuiltInTyped("sub", arithmetic::FunctionNumberNumberToNumber{"sub"}),
58 | makeDefinitionBuiltInTyped("div", arithmetic::FunctionNumberNumberToNumber{"div"}),
59 | makeDefinitionBuiltInTyped("mod", arithmetic::FunctionNumberNumberToNumber{"mod"}),
60 | makeDefinitionBuiltInTyped("less", arithmetic::FunctionNumberNumberToBoolean{"less"}),
61 | makeDefinitionBuiltInTyped("round", arithmetic::FunctionNumberToNumber{"round"}),
62 | makeDefinitionBuiltInTyped("round_up", arithmetic::FunctionNumberToNumber{"round_up"}),
63 | makeDefinitionBuiltInTyped("round_down", arithmetic::FunctionNumberToNumber{"round_down"}),
64 | makeDefinitionBuiltInTyped("sqrt", arithmetic::FunctionNumberToNumber{"sqrt"}),
65 | makeDefinitionBuiltInTyped("number", arithmetic::FunctionCharacterToNumber{"number"}),
66 | makeDefinitionBuiltInTyped("character", arithmetic::FunctionNumberToCharacter{"character"}),
67 | };
68 | return makeEvaluatedDictionary(CodeRange{},
69 | EvaluatedDictionary{Expression{}, definitions}
70 | );
71 | }
72 |
--------------------------------------------------------------------------------
/src/built_in_functions/built_in_functions.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | struct Expression;
4 |
5 | Expression builtIns();
6 | Expression builtInsTypes();
7 |
--------------------------------------------------------------------------------
/src/built_in_functions/container.cpp:
--------------------------------------------------------------------------------
1 | #include "container.h"
2 |
3 | #include
4 |
5 | #include "binary_tuple.h"
6 | #include "../passes/serialize.h"
7 | #include "../expression.h"
8 | #include "../factory.h"
9 |
10 | // Note that we need code range since we use this during parsing.
11 | CodeRange addCodeRanges(Expression rest, Expression top) {
12 | const auto first_character = std::min(rest.range.first, top.range.first);
13 | const auto last_character = std::max(rest.range.last, top.range.last);
14 | return CodeRange{first_character, last_character};
15 | }
16 |
17 | Expression putString(Expression rest, Expression top) {
18 | return makeString(addCodeRanges(top, rest), String{top, rest});
19 | }
20 |
21 | Expression putStack(Expression rest, Expression top) {
22 | return makeStack(addCodeRanges(top, rest), Stack{top, rest});
23 | }
24 |
25 | Expression putEvaluatedStack(Expression rest, Expression top) {
26 | return makeEvaluatedStack(addCodeRanges(top, rest),
27 | EvaluatedStack{top, rest});
28 | }
29 |
30 | Expression putTable(Expression table, Expression item) {
31 | const auto tuple = getDynamicBinaryTuple(item, "put table");
32 | const auto key = tuple.left;
33 | const auto value = tuple.right;
34 | auto& rows = storage.evaluated_tables.at(table.index).rows;
35 | std::string s;
36 | serialize(s, key);
37 | rows[s] = {key, value};
38 | return table;
39 | }
40 |
41 | Expression putTableTyped(Expression table, Expression item) {
42 | if (item.type == ANY) {
43 | return table;
44 | }
45 | const auto tuple = getStaticBinaryTuple(item, "putTable");
46 | const auto key = tuple.left;
47 | const auto value = tuple.right;
48 | auto& rows = storage.evaluated_tables.at(table.index).rows;
49 | std::string s;
50 | serialize_types(s, key);
51 | rows[s] = {key, value};
52 | return table;
53 | }
54 |
55 | namespace container_functions {
56 |
57 | Expression clear(Expression in) {
58 | switch (in.type) {
59 | case EVALUATED_STACK: return Expression{EMPTY_STACK, 0, CodeRange{}};
60 | case EMPTY_STACK: return Expression{EMPTY_STACK, 0, CodeRange{}};
61 | case STRING: return Expression{EMPTY_STRING, 0, CodeRange{}};
62 | case EMPTY_STRING: return Expression{EMPTY_STRING, 0, CodeRange{}};
63 | case EVALUATED_TABLE: return makeEvaluatedTable(CodeRange{}, EvaluatedTable{});
64 | case NUMBER: return makeNumber(CodeRange{}, 0);
65 | case YES: return Expression{NO, 0, CodeRange{}};
66 | case NO: return in;
67 | default: throw UnexpectedExpression(in.type, "clear operation");
68 | }
69 | }
70 |
71 | Expression clearTyped(Expression in) {
72 | // TODO: shouldn't clear EVALUATED_STACK give EMPTY_STACK,
73 | // so that it can be populated with items of different type?
74 | //switch (in.type) {
75 | // case EVALUATED_STACK: return makeEmptyStack(CodeRange{}, EmptyStack{});
76 | // case EMPTY_STACK: return makeEmptyStack(CodeRange{}, EmptyStack{});
77 | // case STRING: return makeEmptyString(CodeRange{}, EmptyString{});
78 | // case EMPTY_STRING: return makeEmptyString(CodeRange{}, EmptyString{});
79 | // case EVALUATED_TABLE: return makeEvaluatedTable(CodeRange{}, EvaluatedTable{});
80 | // case NUMBER: return makeNumber(CodeRange{}, 0);
81 | // default: throw UnexpectedExpression(in.type, "clear operation");
82 | //}
83 | switch (in.type) {
84 | case EVALUATED_STACK: return in;
85 | case EMPTY_STACK: return in;
86 | case STRING: return in;
87 | case EMPTY_STRING: return in;
88 | case EVALUATED_TABLE: return in;
89 | case NUMBER: return in;
90 | case YES: return in;
91 | case NO: return in;
92 | default: throw UnexpectedExpression(in.type, "clearTyped operation");
93 | }
94 | }
95 |
96 | Expression putNumber(Expression collection, Expression item) {
97 | if (item.type != ANY && item.type != NUMBER) {
98 | throw std::runtime_error(
99 | std::string{"\n\nI have found a static type error."} +
100 | "\nIt happens for the operation put!(NUMBER item). " +
101 | "\nIt expects the item to be a " + NAMES[NUMBER] + "," +
102 | "\nbut now it got a " + NAMES[item.type] +
103 | ".\n"
104 | );
105 | }
106 | return makeNumber({}, getNumber(collection) + getNumber(item));
107 | }
108 |
109 | Expression putBoolean(Expression, Expression item) {
110 | return item;
111 | }
112 |
113 | Expression put(Expression in) {
114 | const auto tuple = getDynamicBinaryTuple(in, "put");
115 | const auto item = tuple.left;
116 | const auto collection = tuple.right;
117 | switch (collection.type) {
118 | case EVALUATED_STACK: return putEvaluatedStack(collection, item);
119 | case EMPTY_STACK: return putEvaluatedStack(collection, item);
120 | case STRING: return putString(collection, item);
121 | case EMPTY_STRING: return putString(collection, item);
122 | case EVALUATED_TABLE: return putTable(collection, item);
123 | case NUMBER: return putNumber(collection, item);
124 | case YES: return item;
125 | case NO: return item;
126 | default: throw UnexpectedExpression(in.type, "put operation");
127 | }
128 | }
129 |
130 | Expression putTyped(Expression in) {
131 | const auto tuple = getStaticBinaryTuple(in, "put");
132 | const auto item = tuple.left;
133 | const auto collection = tuple.right;
134 | if (item.type == ANY) {
135 | return collection;
136 | }
137 | switch (collection.type) {
138 | case EVALUATED_STACK: return putEvaluatedStack(collection, item);
139 | case EMPTY_STACK: return putEvaluatedStack(collection, item);
140 | case STRING: return collection; // TODO: type check item
141 | case EMPTY_STRING: return putString(collection, item);
142 | case EVALUATED_TABLE: return putTableTyped(collection, item);
143 | case NUMBER: return putNumber(collection, item);
144 | case YES: return item; // TODO: type check item
145 | case NO: return item;// TODO: type check item
146 | default: throw UnexpectedExpression(in.type, "putTyped operation");
147 | }
148 | }
149 |
150 | template
151 | Expression takeTable(const T& table) {
152 | if (table.empty()) {
153 | throw std::runtime_error("Cannot take item from empty table");
154 | }
155 | const auto& pair = table.begin()->second;
156 | return makeEvaluatedTuple2(pair.key, pair.value);
157 | }
158 |
159 | template
160 | Expression takeTableTyped(const T& table, Expression expression) {
161 | const auto range = expression.range;
162 | if (table.empty()) {
163 | return makeEvaluatedTuple2(Expression{ANY, 0, range}, Expression{ANY, 0, range});
164 | }
165 | const auto& pair = table.begin()->second;
166 | return makeEvaluatedTuple2(pair.key, pair.value);
167 | }
168 |
169 | template
170 | Expression dropTable(const T& table) {
171 | return makeEvaluatedTableView({}, EvaluatedTableView{++table.begin(), table.end()});
172 | }
173 |
174 | Expression dropNumber(Expression in) {
175 | return makeNumber({}, getNumber(in) - 1);
176 | }
177 |
178 | Expression take(Expression in) {
179 | const auto type = in.type;
180 | const auto index = in.index;
181 | switch (type) {
182 | case EVALUATED_STACK: return storage.evaluated_stacks.at(index).top;
183 | case STRING: return storage.strings.at(index).top;
184 | case EVALUATED_TABLE: return takeTable(storage.evaluated_tables.at(index));
185 | case EVALUATED_TABLE_VIEW: return takeTable(storage.evaluated_table_views.at(index));
186 | case NUMBER: return makeNumber({}, 1);
187 | case YES: return in;
188 | case NO: return in;
189 | default: throw UnexpectedExpression(type, "take");
190 | }
191 | }
192 |
193 | Expression takeTyped(Expression in) {
194 | const auto type = in.type;
195 | const auto index = in.index;
196 | switch (type) {
197 | case EVALUATED_STACK: return storage.evaluated_stacks.at(index).top;
198 | case STRING: return storage.strings.at(index).top;
199 | case EVALUATED_TABLE: return takeTableTyped(storage.evaluated_tables.at(index), in);
200 | case EVALUATED_TABLE_VIEW: return takeTableTyped(storage.evaluated_table_views.at(index), in);
201 | case EMPTY_STACK: return Expression{ANY, 0, in.range};
202 | case EMPTY_STRING: return Expression{CHARACTER, 0, in.range};
203 | case NUMBER: return in;
204 | case YES: return in;
205 | case NO: return in;
206 | default: throw UnexpectedExpression(type, "take");
207 | }
208 | }
209 |
210 | Expression drop(Expression in) {
211 | switch (in.type) {
212 | case EVALUATED_STACK: return storage.evaluated_stacks.at(in.index).rest;
213 | case STRING: return storage.strings.at(in.index).rest;
214 | case EVALUATED_TABLE: return dropTable(storage.evaluated_tables.at(in.index));
215 | case EVALUATED_TABLE_VIEW: return dropTable(storage.evaluated_table_views.at(in.index));
216 | case EMPTY_STACK: return in;
217 | case EMPTY_STRING: return in;
218 | case NUMBER: return dropNumber(in);
219 | case NO: return in;
220 | case YES: return Expression{NO, 0, CodeRange{}};
221 | default: throw UnexpectedExpression(in.type, "drop");
222 | }
223 | }
224 |
225 | Expression dropTyped(Expression in) {
226 | switch (in.type) {
227 | case EVALUATED_STACK: return in;
228 | case STRING: return in;
229 | case EVALUATED_TABLE: return in;
230 | case EVALUATED_TABLE_VIEW: return in;
231 | case EMPTY_STACK: return in;
232 | case EMPTY_STRING: return in;
233 | case NUMBER: return in;
234 | case NO: return in;
235 | case YES: return in;
236 | default: throw UnexpectedExpression(in.type,
237 | "drop typed" + describeLocation(in.range)
238 | );
239 | }
240 | }
241 |
242 | Expression get(Expression in) {
243 | if (in.type != EVALUATED_TUPLE) {
244 | throw std::runtime_error(
245 | std::string{"\n\nI have found a dynamic type error."} +
246 | "\nIt happens for the function get!(key table default). " +
247 | "\nIt expects a tuple of three items," +
248 | "\nbut now it got a " + NAMES[in.type] +
249 | ".\n"
250 | );
251 | }
252 | const auto evaluated_tuple = storage.evaluated_tuples.at(in.index);
253 | const auto count = evaluated_tuple.last - evaluated_tuple.first;
254 | if (count != 3) {
255 | throw std::runtime_error(
256 | std::string{"\n\nI have found a dynamic type error."} +
257 | "\nIt happens for the function get!(key table default). " +
258 | "\nIt expects a tuple of three items," +
259 | "\nbut now it got " + std::to_string(count) + "items" +
260 | ".\n"
261 | );
262 | }
263 | const auto key = storage.expressions.at(evaluated_tuple.first + 0);
264 | const auto table = storage.expressions.at(evaluated_tuple.first + 1);
265 | const auto default_value = storage.expressions.at(evaluated_tuple.first + 2);
266 | if (table.type != EVALUATED_TABLE) {
267 | throw std::runtime_error(
268 | std::string{"\n\nI have found a dynamic type error."} +
269 | "\nIt happens for the function get!(key table default). " +
270 | "\nIt expects a tuple where the second item is a table," +
271 | "\nbut now it got a " + NAMES[table.type] +
272 | ".\n"
273 | );
274 | }
275 | std::string name;
276 | serialize(name, key);
277 | const auto& rows = storage.evaluated_tables.at(table.index).rows;
278 | const auto iterator = rows.find(name);
279 | return iterator == rows.end() ?
280 | default_value : iterator->second.value;
281 | }
282 |
283 | Expression getTyped(Expression in) {
284 | if (in.type != EVALUATED_TUPLE) {
285 | throw std::runtime_error(
286 | std::string{"\n\nI have found a static type error."} +
287 | "\nIt happens for the function get!(key table default). " +
288 | "\nIt expects a tuple of three items," +
289 | "\nbut now it got a " + NAMES[in.type] +
290 | ".\n"
291 | );
292 | }
293 | const auto evaluated_tuple = storage.evaluated_tuples.at(in.index);
294 | const auto count = evaluated_tuple.last - evaluated_tuple.first;
295 | if (count != 3) {
296 | throw std::runtime_error(
297 | std::string{"\n\nI have found a static type error."} +
298 | "\nIt happens for the function get!(key table default). " +
299 | "\nIt expects a tuple of three items," +
300 | "\nbut now it got " + std::to_string(count) + "items" +
301 | ".\n"
302 | );
303 | }
304 | const auto table = storage.expressions.at(evaluated_tuple.first + 1);
305 | const auto default_value = storage.expressions.at(evaluated_tuple.first + 2);
306 | if (table.type != EVALUATED_TABLE) {
307 | throw std::runtime_error(
308 | std::string{"\n\nI have found a dynamic type error."} +
309 | "\nIt happens for the function get!(key table default). " +
310 | "\nIt expects a tuple where the second item is a table," +
311 | "\nbut now it got a " + NAMES[table.type] +
312 | ".\n"
313 | );
314 | }
315 | return default_value;
316 | }
317 |
318 | }
319 |
--------------------------------------------------------------------------------
/src/built_in_functions/container.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "../expression.h"
4 |
5 | Expression putString(Expression rest, Expression top);
6 | Expression putStack(Expression rest, Expression top);
7 | Expression putEvaluatedStack(Expression rest, Expression top);
8 |
9 | namespace container_functions {
10 |
11 | Expression clear(Expression in);
12 | Expression clearTyped(Expression in);
13 | Expression put(Expression in);
14 | Expression putTyped(Expression in);
15 | Expression take(Expression in);
16 | Expression takeTyped(Expression in);
17 | Expression drop(Expression in);
18 | Expression dropTyped(Expression in);
19 | Expression get(Expression in);
20 | Expression getTyped(Expression in);
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/src/built_in_functions/standard_library.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | const std::string STANDARD_LIBRARY = R"(
6 | {
7 | Any = dynamic 0
8 | Number = 0
9 | Boolean = no
10 | Character = 'a'
11 | Stack = []
12 | String = ""
13 | Table = <>
14 | Numbers = [Number]
15 | Function = in x out x
16 |
17 | boolean = in x out Boolean:if x then yes else no
18 | not = in x out Boolean:if x then no else yes
19 |
20 | equal = in (left right) out Boolean:is left right then yes else no
21 | unequal = in (left right) out Boolean:is left right then no else yes
22 |
23 | less_or_equal = in (Number:left Number:right) out Boolean:not?less?(right left)
24 |
25 | inf = div!(1 0)
26 | nan = div!(0 0)
27 | pi = 3.14159265359
28 | tau = 6.28318530718
29 |
30 | inc = in Number:x out Number:add!(x 1)
31 | dec = in Number:x out Number:sub!(x 1)
32 | neg = in Number:x out Number:sub!(0 x)
33 | abs = in Number:x out Number:if less?(0 x) then x else neg!x
34 |
35 | newline = character!10
36 |
37 | is_digit = in Character:c out Boolean:is_increasing?[number!'0' number!c number!'9']
38 | is_upper = in Character:c out Boolean:is_increasing?[number!'A' number!c number!'Z']
39 | is_lower = in Character:c out Boolean:is_increasing?[number!'a' number!c number!'z']
40 | is_letter = in Character:c out Boolean:any?[is_upper?c is_lower?c]
41 |
42 | parse_digit = in Character:c out Number:sub!(number!c number!'0')
43 | serialize_digit = in Number:x out Character:character!add!(x number!'0')
44 |
45 | parse_natural_number = in String:string out Number:number@{
46 | reversed_string = reverse!string
47 | number = 0
48 | x = 1
49 | for c in reversed_string
50 | digit = parse_digit!c
51 | number = add!(number mul!(x digit))
52 | x = mul!(x 10)
53 | end
54 | }
55 |
56 | serialize_natural_number = in Number:number out String:string@{
57 | x = number
58 | string = ""
59 | string += serialize_digit!mod!(x 10)
60 | x = round_down!div!(x 10)
61 | while less?(0 x)
62 | string += serialize_digit!mod!(x 10)
63 | x = round_down!div!(x 10)
64 | end
65 | }
66 |
67 | to_upper = in Character:c out Character:
68 | if is_lower?c then
69 | character!sub!(number!c 32)
70 | else
71 | c
72 |
73 | to_lower = in Character:c out Character:
74 | if is_upper?c then
75 | character!add!(number!c 32)
76 | else
77 | c
78 |
79 | fold = in (Function:operation in_stream init) out result@{
80 | result = init
81 | s = in_stream
82 | for item in s
83 | result = operation!(item result)
84 | end
85 | }
86 |
87 | put_each = in (in_stream out_stream) out out_stream:fold!(
88 | put
89 | in_stream
90 | out_stream
91 | )
92 |
93 | reverse = in container out container:put_each!(
94 | container
95 | clear!container
96 | )
97 |
98 | make_stack = in in_stream out Stack:reverse!put_each!(
99 | in_stream
100 | []
101 | )
102 |
103 | make_string = in in_stream out String:reverse!put_each!(
104 | in_stream
105 | ""
106 | )
107 |
108 | make_table = in in_stream out Table:put_each!(
109 | in_stream
110 | <>
111 | )
112 |
113 | merge_generic = in (in_streams out_stream) out out_stream:fold!(
114 | put_each
115 | in_streams
116 | out_stream
117 | )
118 |
119 | merge_stack = in in_streams out Stack:reverse!merge_generic!(
120 | in_streams
121 | []
122 | )
123 |
124 | merge_string = in in_streams out String:reverse!merge_generic!(
125 | in_streams
126 | ""
127 | )
128 |
129 | merge_table = in containers out Table:merge_generic!(
130 | reverse!containers
131 | <>
132 | )
133 |
134 | map_generic = in (Function:f in_stream out_stream) out fold!(
135 | in (item stream) out put!(f!item stream)
136 | in_stream
137 | out_stream
138 | )
139 |
140 | map = in (Function:f container) out reverse!map_generic!(
141 | f
142 | container
143 | clear!container
144 | )
145 |
146 | map_stack = in (Function:f in_stream) out Stack:reverse!map_generic!(
147 | f
148 | in_stream
149 | []
150 | )
151 |
152 | map_string = in (Function:f in_stream) out String:reverse!map_generic!(
153 | f
154 | in_stream
155 | ""
156 | )
157 |
158 | map_table = in (Function:f in_stream) out Table:map_generic!(
159 | f
160 | in_stream
161 | <>
162 | )
163 |
164 | zip2 = in (a b) out Stack:reverse!result@{
165 | a2 = a
166 | b2 = b
167 | result = []
168 | while and?[a2 b2]
169 | result += (take!a2 take!b2)
170 | a2--
171 | b2--
172 | end
173 | }
174 |
175 | zip3 = in (a b c) out Stack:reverse!result@{
176 | a2 = a
177 | b2 = b
178 | c2 = c
179 | result = []
180 | while and?[a2 b2 c2]
181 | result += (take!a2 take!b2 take!c2)
182 | a2--
183 | b2--
184 | c2--
185 | end
186 | }
187 |
188 | zip4 = in (a b c d) out Stack:reverse!result@{
189 | a2 = a
190 | b2 = b
191 | c2 = c
192 | d2 = d
193 | result = []
194 | while and?[a2 b2 c2 d2]
195 | result += (take!a2 take!b2 take!c2 take!d2)
196 | a2--
197 | b2--
198 | c2--
199 | d2--
200 | end
201 | }
202 |
203 | consecutive_pairs = in in_stream out Stack:reverse!result@{
204 | s = in_stream
205 | result = []
206 | while if s then boolean!drop!s else no
207 | result += (take!s take!drop!s)
208 | s--
209 | end
210 | }
211 |
212 | min = in (Number:left Number:right) out Number:if less?(left right) then left else right
213 | max = in (Number:left Number:right) out Number:if less?(left right) then right else left
214 |
215 | min_item = in Numbers:in_stream out Number:fold!(min in_stream inf)
216 |
217 | max_item = in Numbers:in_stream out Number:fold!(max in_stream -inf)
218 |
219 | min_predicate = in (Function:predicate in_stream) out fold!(
220 | in (left right) out if predicate?(left right) then left else right
221 | drop!in_stream
222 | take!in_stream
223 | )
224 |
225 | max_predicate = in (Function:predicate in_stream) out fold!(
226 | in (left right) out if predicate?(left right) then right else left
227 | drop!in_stream
228 | take!in_stream
229 | )
230 |
231 | min_key = in (Function:key in_stream) out fold!(
232 | in (left right) out if less?(key!left key!right) then left else right
233 | drop!in_stream
234 | take!in_stream
235 | )
236 |
237 | max_key = in (Function:key in_stream) out fold!(
238 | in (left right) out if less?(key!left key!right) then right else left
239 | drop!in_stream
240 | take!in_stream
241 | )
242 |
243 | sum = in Numbers:in_stream out Number:fold!(add in_stream 0)
244 |
245 | product = in Numbers:in_stream out Number:fold!(mul in_stream 1)
246 |
247 | clear_if = in (Function:predicate container) out container:reverse!fold!(
248 | in (item container) out
249 | if predicate?item then
250 | container
251 | else
252 | put!(item container)
253 | container
254 | clear!container
255 | )
256 |
257 | clear_item = in (item container) out container:
258 | clear_if?(in x out equal?(x item) container)
259 |
260 | take_many = in (Number:n container_in) out container_in:reverse!container_out@{
261 | container = container_in
262 | container_out = clear!container
263 | m = n
264 | for m
265 | container_out += take!container
266 | container--
267 | end
268 | }
269 |
270 | take_while = in (Function:predicate container_in) out container_in:reverse!container_out@{
271 | container = container_in
272 | container_out = clear!container
273 | while if container then predicate?take!container else no
274 | container_out += take!container
275 | container--
276 | end
277 | }
278 |
279 | take_until_item = in (item container) out container:
280 | take_while!(in x out unequal?(x item) container)
281 |
282 | drop_many = in (Number:n in_stream) out in_stream:stream@{
283 | stream = in_stream
284 | m = n
285 | for m
286 | stream--
287 | end
288 | }
289 |
290 | drop_while = in (Function:predicate in_stream) out in_stream:stream@{
291 | stream = in_stream
292 | while if stream then predicate?take!stream else no
293 | stream--
294 | end
295 | }
296 |
297 | drop_until_item = in (item in_stream) out in_stream:
298 | drop_while?(in x out unequal?(x item) in_stream)
299 |
300 | replace = in (new_item container) out map!(
301 | in old_item out new_item
302 | container
303 | )
304 |
305 | replace_if = in (Function:predicate new_item container) out map!(
306 | in old_item out
307 | if predicate?old_item then
308 | new_item
309 | else
310 | old_item
311 | container
312 | )
313 |
314 | replace_item = in (old_item new_item container) out
315 | replace_if?(in x out equal?(x old_item) new_item container)
316 |
317 | count = in in_stream out Number:fold!(
318 | in (item n) out inc!n
319 | in_stream
320 | 0
321 | )
322 |
323 | count_if = in (Function:predicate in_stream) out Number:fold!(
324 | in (item n) out if predicate?item then inc!n else n
325 | in_stream
326 | 0
327 | )
328 |
329 | count_item = in (item in_stream) out Number:
330 | count_if!(in x out equal?(x item) in_stream)
331 |
332 | range = in Number:n out Numbers:numbers@{
333 | numbers = []
334 | m = n
335 | for m
336 | numbers += dec!m
337 | end
338 | }
339 |
340 | enumerate = in in_stream out Stack:zip2!(range!count!in_stream in_stream)
341 |
342 | get0 = in in_stream out in_stream!0
343 | get1 = in in_stream out in_stream!1
344 | get2 = in in_stream out in_stream!2
345 | get3 = in in_stream out in_stream!3
346 | get4 = in in_stream out in_stream!4
347 | get5 = in in_stream out in_stream!5
348 | get6 = in in_stream out in_stream!6
349 | get7 = in in_stream out in_stream!7
350 | get8 = in in_stream out in_stream!8
351 | get9 = in in_stream out in_stream!9
352 |
353 | split = in (delimiter container) out Stack:reverse!result@{
354 | word = take_until_item!(delimiter container)
355 | sub_container = drop_until_item!(delimiter container)
356 | result = [word]
357 | while sub_container
358 | sub_container--
359 | word = take_until_item!(delimiter sub_container)
360 | sub_container = drop_until_item!(delimiter sub_container)
361 | result += word
362 | end
363 | }
364 |
365 | cartesian_product2 = in (a b) out Stack:result@{
366 | result = []
367 | d = b
368 | for item_d in d
369 | c = a
370 | for item_c in c
371 | result += (item_c item_d)
372 | end
373 | end
374 | }
375 |
376 | put_column = in (column rows) out Stack:reverse!new_rows@{
377 | remaining_rows = rows
378 | new_rows = []
379 | c = column
380 | for item in c
381 | row = take!remaining_rows
382 | row += item
383 | new_rows += row
384 | remaining_rows--
385 | end
386 | }
387 |
388 | transpose = in rows out Stack:map!(reverse columns@{
389 | columns = replace!([] take!rows)
390 | r = rows
391 | for row in r
392 | columns = put_column!(row columns)
393 | end
394 | })
395 |
396 | all = in in_stream out Boolean:not?drop_while!(boolean in_stream)
397 | none = in in_stream out Boolean:not?drop_while!(not in_stream)
398 | any = in in_stream out Boolean:boolean?drop_while!(not in_stream)
399 |
400 | and = in in_stream out Boolean:all?in_stream
401 | or = in in_stream out Boolean:any?in_stream
402 |
403 | is_increasing = in Numbers:numbers out Boolean:not?drop_while!(
404 | less_or_equal
405 | consecutive_pairs!numbers
406 | )
407 |
408 | less_or_equal_top = in (Numbers:left Numbers:right) out Boolean:
409 | if left then
410 | if right then
411 | is_increasing?[take!left take!right]
412 | else
413 | yes
414 | else
415 | no
416 |
417 | merge_sorted = in (left_in right_in) out Stack:reverse!stack@{
418 | left = left_in
419 | right = right_in
420 | stack = []
421 | while or?[left right]
422 | while less_or_equal_top?(left right)
423 | stack += take!left
424 | left--
425 | end
426 | while less_or_equal_top?(right left)
427 | stack += take!right
428 | right--
429 | end
430 | end
431 | }
432 |
433 | unique = in in_stream out Stack:get_keys!fold!(
434 | in (item table) out put!((item 0) table)
435 | in_stream
436 | <>
437 | )
438 |
439 | count_elements = in in_stream out Table:fold!(
440 | in (item table) out put!((item inc!get!(item table 0)) table)
441 | in_stream
442 | <>
443 | )
444 |
445 | get_items = make_stack
446 |
447 | get_keys = in Table:table out Stack:map_stack!(
448 | in (key value) out key
449 | table
450 | )
451 |
452 | get_values = in Table:table out Stack:map_stack!(
453 | in (key value) out value
454 | table
455 | )
456 |
457 | addv = in (Numbers:a Numbers:b) out Numbers:map!(add zip2!(a b))
458 | subv = in (Numbers:a Numbers:b) out Numbers:map!(sub zip2!(a b))
459 | mulv = in (Numbers:a Numbers:b) out Numbers:map!(mul zip2!(a b))
460 | divv = in (Numbers:a Numbers:b) out Numbers:map!(div zip2!(a b))
461 | dot = in (Numbers:a Numbers:b) out Number:sum!mulv!(a b)
462 | squared_norm = in Numbers:a out Number:dot!(a a)
463 | norm = in Numbers:a out Number:sqrt!squared_norm!a
464 | }
465 | )";
466 |
--------------------------------------------------------------------------------
/src/expression.cpp:
--------------------------------------------------------------------------------
1 | #include "expression.h"
2 | #include
3 |
4 | #include "factory.h"
5 |
6 | StaticTypeError::StaticTypeError(
7 | ExpressionType type, const std::string& location)
8 | : std::runtime_error("Static type error " + NAMES[type] + " for " + location)
9 | {}
10 |
11 | UnexpectedExpression::UnexpectedExpression(
12 | ExpressionType type, const std::string& location)
13 | : std::runtime_error("Unexpected expression " + NAMES[type] + " for " + location)
14 | {}
15 |
16 | MissingSymbol::MissingSymbol(
17 | const std::string& symbol, const std::string& location)
18 | : std::runtime_error("Cannot find symbol " + symbol + " in " + location)
19 | {}
20 |
21 | MissingKey::MissingKey(
22 | const std::string& key)
23 | : std::runtime_error("Cannot find key " + key + " in table")
24 | {}
25 |
26 | const Expression* EvaluatedDictionary::optionalLookup(size_t name) const {
27 | for (const auto& definition: definitions) {
28 | if (definition.name.global_index == name) {
29 | return &definition.expression;
30 | }
31 | }
32 | return nullptr;
33 | }
34 |
35 | Expression EvaluatedDictionary::lookup(size_t name) const {
36 | const auto expression = optionalLookup(name);
37 | if (expression) {
38 | return *expression;
39 | }
40 | throw MissingSymbol(storage.names.at(name), "dictionary");
41 | }
42 |
--------------------------------------------------------------------------------
/src/expression.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include