├── .github └── workflows │ └── github-actions.yml ├── .gitignore ├── docs ├── atom.png ├── c1.png ├── c2.png ├── c3.png ├── c4.png ├── c5.png ├── c6.png ├── container.png ├── container2.png ├── control_flow.png ├── function.png ├── functions.html ├── index.html ├── owl.png ├── reference.png ├── styles.css └── type_annotation.png ├── documents ├── c_transpiler.txt ├── cpp_transpiler.txt ├── ideas.txt ├── manglang.tex ├── old_readme.md ├── readable_regular_expressions.txt └── transpiling.txt ├── examples ├── aoc_21_01.txt ├── aoc_21_02.txt ├── aoc_21_03.txt ├── aoc_21_05.txt ├── aoc_21_06.txt ├── aoc_21_08.txt ├── aoc_21_09.txt ├── aoc_21_10.txt ├── aoc_21_13.txt ├── aoc_21_14.txt ├── aoc_22_01.txt ├── aoc_22_02.txt ├── aoc_22_03.txt ├── aoc_22_04.txt ├── aoc_22_05.txt ├── aoc_22_06.txt ├── aoc_22_07.txt ├── aoc_22_08.txt ├── aoc_22_09.txt ├── aoc_22_10.txt ├── aoc_22_12.txt ├── aoc_22_13.txt ├── aoc_22_14.txt ├── aoc_22_18.txt ├── aoc_22_21.txt ├── hello_world.txt └── raytracer.txt ├── readme.md └── src ├── .gitignore ├── CMakeLists.txt ├── built_in_functions ├── arithmetic.cpp ├── arithmetic.h ├── binary_tuple.cpp ├── binary_tuple.h ├── built_in_functions.cpp ├── built_in_functions.h ├── container.cpp ├── container.h └── standard_library.h ├── expression.cpp ├── expression.h ├── factory.cpp ├── factory.h ├── interpreter.cpp ├── mang_lang.cpp ├── mang_lang.h ├── parsing.cpp ├── parsing.h ├── passes ├── bind.cpp ├── bind.h ├── evaluate.cpp ├── evaluate.h ├── parse.cpp ├── parse.h ├── serialize.cpp └── serialize.h └── tests.cpp /.github/workflows/github-actions.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: [push] 3 | jobs: 4 | build: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - name: Checkout code 8 | uses: actions/checkout@v2 9 | 10 | - name: Build C++ 11 | run: | 12 | mkdir src/build 13 | cd src/build 14 | cmake -DCMAKE_BUILD_TYPE=Release .. 15 | make 16 | 17 | - name: Tests 18 | run: | 19 | ./src/build/tests 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /documents/*.aux 2 | /documents/*.log 3 | /documents/*.pdf 4 | /documents/*.synctex.gz 5 | /examples/*_evaluated.txt 6 | /tutorials/ 7 | -------------------------------------------------------------------------------- /docs/atom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mabur/mang_lang/56363d40226a5490b8114cb3c842dacd64e9b9cc/docs/atom.png -------------------------------------------------------------------------------- /docs/c1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mabur/mang_lang/56363d40226a5490b8114cb3c842dacd64e9b9cc/docs/c1.png -------------------------------------------------------------------------------- /docs/c2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mabur/mang_lang/56363d40226a5490b8114cb3c842dacd64e9b9cc/docs/c2.png -------------------------------------------------------------------------------- /docs/c3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mabur/mang_lang/56363d40226a5490b8114cb3c842dacd64e9b9cc/docs/c3.png -------------------------------------------------------------------------------- /docs/c4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mabur/mang_lang/56363d40226a5490b8114cb3c842dacd64e9b9cc/docs/c4.png -------------------------------------------------------------------------------- /docs/c5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mabur/mang_lang/56363d40226a5490b8114cb3c842dacd64e9b9cc/docs/c5.png -------------------------------------------------------------------------------- /docs/c6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mabur/mang_lang/56363d40226a5490b8114cb3c842dacd64e9b9cc/docs/c6.png -------------------------------------------------------------------------------- /docs/container.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mabur/mang_lang/56363d40226a5490b8114cb3c842dacd64e9b9cc/docs/container.png -------------------------------------------------------------------------------- /docs/container2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mabur/mang_lang/56363d40226a5490b8114cb3c842dacd64e9b9cc/docs/container2.png -------------------------------------------------------------------------------- /docs/control_flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mabur/mang_lang/56363d40226a5490b8114cb3c842dacd64e9b9cc/docs/control_flow.png -------------------------------------------------------------------------------- /docs/function.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mabur/mang_lang/56363d40226a5490b8114cb3c842dacd64e9b9cc/docs/function.png -------------------------------------------------------------------------------- /docs/functions.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Manglang Functions 9 | 10 | 11 | 12 |

Functions

13 | 14 |

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).
114 |
split
split!(' ' "hey hey, ok") returns ["hey" "hey," "ok"] and split!(1 [1 2 3 1 1 4]) returns [[] [2 3] [] [4]]. O(N).
115 |
cartesian_product2
cartesian_product2!([0 1] "ab") returns the stack of tuples [(1 'b') (0 'b') (1 'a') (0 'a')]. O(MN).
116 |
put_column
put_column!([1 2] [[3] [4]]) returns [[1 3] [2 4]]. O(N).
117 |
transpose
transpose![[1 2] [3 4]] returns [[1 3] [2 4]]. O(NM).
118 |
119 | 120 |

Table Functions

121 |
122 |
get
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 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "parsing.h" 11 | 12 | enum ExpressionType { 13 | CHARACTER, 14 | CONDITIONAL, 15 | IS, 16 | ALTERNATIVE, 17 | TABLE, 18 | EVALUATED_TABLE, 19 | EVALUATED_TABLE_VIEW, 20 | DICTIONARY, 21 | EVALUATED_DICTIONARY, 22 | TUPLE, 23 | EVALUATED_TUPLE, 24 | FUNCTION, 25 | FUNCTION_BUILT_IN, 26 | FUNCTION_DICTIONARY, 27 | FUNCTION_TUPLE, 28 | STACK, 29 | EVALUATED_STACK, 30 | EMPTY_STACK, 31 | LOOKUP_CHILD, 32 | FUNCTION_APPLICATION, 33 | LOOKUP_SYMBOL, 34 | NAME, 35 | ARGUMENT, 36 | DEFINITION, 37 | PUT_ASSIGNMENT, 38 | PUT_EACH_ASSIGNMENT, 39 | DROP_ASSIGNMENT, 40 | WHILE_STATEMENT, 41 | FOR_STATEMENT, 42 | FOR_SIMPLE_STATEMENT, 43 | WHILE_END_STATEMENT, 44 | FOR_END_STATEMENT, 45 | FOR_SIMPLE_END_STATEMENT, 46 | RETURN_STATEMENT, 47 | NUMBER, 48 | STRING, 49 | EMPTY_STRING, 50 | YES, 51 | NO, 52 | DYNAMIC_EXPRESSION, 53 | TYPED_EXPRESSION, 54 | ANY, 55 | }; 56 | 57 | const auto NAMES = std::vector{ 58 | "CHARACTER", 59 | "CONDITIONAL", 60 | "IS", 61 | "ALTERNATIVE", 62 | "TABLE", 63 | "EVALUATED_TABLE", 64 | "EVALUATED_TABLE_VIEW", 65 | "DICTIONARY", 66 | "EVALUATED_DICTIONARY", 67 | "TUPLE", 68 | "EVALUATED_TUPLE", 69 | "FUNCTION", 70 | "FUNCTION_BUILT_IN", 71 | "FUNCTION_DICTIONARY", 72 | "FUNCTION_TUPLE", 73 | "STACK", 74 | "EVALUATED_STACK", 75 | "EMPTY_STACK", 76 | "LOOKUP_CHILD", 77 | "FUNCTION_APPLICATION", 78 | "LOOKUP_SYMBOL", 79 | "NAME", 80 | "ARGUMENT", 81 | "DEFINITION", 82 | "PUT_ASSIGNMENT", 83 | "PUT_EACH_ASSIGNMENT", 84 | "DROP_ASSIGNMENT", 85 | "WHILE_STATEMENT", 86 | "FOR_STATEMENT", 87 | "FOR_SIMPLE_STATEMENT,", 88 | "WHILE_END_STATEMENT", 89 | "FOR_END_STATEMENT", 90 | "FOR_SIMPLE_END_STATEMENT", 91 | "RETURN_STATEMENT", 92 | "NUMBER", 93 | "STRING", 94 | "EMPTY_STRING", 95 | "YES", 96 | "NO", 97 | "DYNAMIC_EXPRESSION", 98 | "TYPED_EXPRESSION", 99 | "ANY", 100 | }; 101 | 102 | struct StaticTypeError : public std::runtime_error 103 | { 104 | StaticTypeError(ExpressionType type, const std::string& location); 105 | using runtime_error::runtime_error; 106 | }; 107 | 108 | struct UnexpectedExpression : public std::runtime_error 109 | { 110 | UnexpectedExpression(ExpressionType type, const std::string& location); 111 | using runtime_error::runtime_error; 112 | }; 113 | 114 | struct MissingSymbol : public std::runtime_error 115 | { 116 | MissingSymbol(const std::string& symbol, const std::string& location); 117 | using runtime_error::runtime_error; 118 | }; 119 | 120 | struct MissingKey : public std::runtime_error 121 | { 122 | MissingKey(const std::string& key); 123 | using runtime_error::runtime_error; 124 | }; 125 | 126 | // TODO: Pack tighter? 127 | // Bit size Current Pack1 Pack2 128 | // Expression::type 16 4 4 129 | // Expression::index 64 64 64-4 130 | // Expression::range 128 32 0 131 | // Expression 256 128 64 132 | struct Expression { 133 | ExpressionType type = ANY; 134 | size_t index = 0; 135 | CodeRange range; 136 | }; 137 | 138 | using Number = double; 139 | using Character = char; 140 | using Name = std::string; 141 | 142 | struct BoundGlobalName { 143 | size_t global_index; // Index to this name in the global storage. 144 | int parent_steps = -1; // Number of steps to parent. -1 if unresolved yet. 145 | }; 146 | 147 | struct BoundLocalName { 148 | size_t global_index; // Index to this name in the global storage. 149 | size_t dictionary_index; // Index to this name and its data in the dictionary. 150 | }; 151 | 152 | struct Argument { 153 | Expression type; // Optional 154 | size_t name; 155 | }; 156 | 157 | // TODO: type alias instead of struct? 158 | struct DynamicExpression { 159 | Expression expression; 160 | }; 161 | 162 | struct TypedExpression { 163 | BoundGlobalName type_name; 164 | Expression value; 165 | }; 166 | 167 | struct Alternative { 168 | Expression left; 169 | Expression right; 170 | }; 171 | 172 | struct Conditional { 173 | Expression alternative_first; 174 | Expression alternative_last; 175 | Expression expression_else; 176 | }; 177 | 178 | struct IsExpression { 179 | Expression input; 180 | Expression alternative_first; 181 | Expression alternative_last; 182 | Expression expression_else; 183 | }; 184 | 185 | struct Function { 186 | Expression environment; 187 | size_t argument; 188 | Expression body; 189 | }; 190 | 191 | struct FunctionBuiltIn { 192 | std::function function; 193 | }; 194 | 195 | struct FunctionDictionary { 196 | Expression environment; // TODO: use this. 197 | size_t first_argument; 198 | size_t last_argument; // Exclusive, to handle empty range 199 | Expression body; 200 | }; 201 | 202 | struct FunctionTuple { 203 | Expression environment; 204 | size_t first_argument; 205 | size_t last_argument; // Exclusive, to handle empty range 206 | Expression body; 207 | }; 208 | 209 | struct LookupChild { 210 | size_t name; 211 | Expression child; 212 | }; 213 | 214 | struct FunctionApplication { 215 | BoundGlobalName name; 216 | Expression child; 217 | }; 218 | 219 | struct LookupSymbol { 220 | BoundGlobalName name; 221 | }; 222 | 223 | struct String { 224 | Expression top; 225 | Expression rest; 226 | }; 227 | 228 | struct Tuple { 229 | size_t first; 230 | size_t last; 231 | }; 232 | 233 | // TODO: add special case for tuple of size 2. 234 | // TODO: merge with Tuple for storage but keep type-code to know if it is evaluated. 235 | struct EvaluatedTuple { 236 | size_t first; 237 | size_t last; 238 | }; 239 | 240 | struct Stack { 241 | Expression top; 242 | Expression rest; 243 | }; 244 | 245 | // TODO: merge with Stack for storage but keep type-code to know if it is evaluated. 246 | struct EvaluatedStack { 247 | Expression top; 248 | Expression rest; 249 | }; 250 | 251 | // STATEMENTS BEGIN 252 | 253 | struct Definition { 254 | BoundLocalName name; 255 | Expression expression; 256 | }; 257 | 258 | struct PutAssignment { 259 | BoundLocalName name; 260 | Expression expression; 261 | }; 262 | 263 | struct PutEachAssignment { 264 | BoundLocalName name; 265 | Expression expression; 266 | }; 267 | 268 | struct DropAssignment { 269 | BoundLocalName name; 270 | }; 271 | 272 | struct WhileStatement { 273 | Expression expression; 274 | size_t end_index; 275 | }; 276 | 277 | struct ForStatement { 278 | BoundLocalName item_name; 279 | BoundLocalName container_name; 280 | size_t end_index; 281 | }; 282 | 283 | struct ForSimpleStatement { 284 | BoundLocalName container_name; 285 | size_t end_index; 286 | }; 287 | 288 | struct WhileEndStatement { 289 | size_t start_index; 290 | }; 291 | 292 | struct ForEndStatement { 293 | size_t start_index; 294 | }; 295 | 296 | struct ForSimpleEndStatement { 297 | size_t start_index; 298 | }; 299 | 300 | // STATEMENTS END 301 | 302 | struct Dictionary { 303 | size_t statement_first; 304 | size_t statement_last; 305 | size_t definition_count; 306 | }; 307 | 308 | // TODO: make cheaper to copy. 309 | // Bottleneck. 310 | // Use index range for definitions. 311 | // But then need to know all members of the dictionary before it is evaluated. 312 | // so that they are all added together, at least the first time. 313 | // Alternatively, use unordered_map for fast lookup of all names 314 | // and not just definitions. 315 | // Bottleneck. 316 | /* 317 | struct EvaluatedDictionary { 318 | Expression environment; 319 | size_t name_first; // Index to constant names/keys. Could possibly be shared across instances. 320 | size_t name_last; // Index to constant names/keys. Could possibly be shared across instances. 321 | size_t value_first; // Index to values/expression that can change during the evaluation. 322 | size_t value_last; // Index to values/expression that can change during the evaluation. 323 | }; 324 | */ 325 | struct EvaluatedDictionary { 326 | EvaluatedDictionary(const EvaluatedDictionary&) = delete; 327 | EvaluatedDictionary(EvaluatedDictionary&&) = default; 328 | EvaluatedDictionary& operator=(const EvaluatedDictionary&) = delete; 329 | EvaluatedDictionary& operator=(EvaluatedDictionary&&) = default; 330 | 331 | Expression environment; 332 | std::vector definitions; 333 | 334 | const Expression* optionalLookup(size_t name) const; 335 | Expression lookup(size_t name) const; 336 | }; 337 | 338 | struct Row { 339 | Expression key; 340 | Expression value; 341 | }; 342 | 343 | // TODO: make cheaper to copy or pass by reference or pointer? 344 | struct Table { 345 | std::vector rows; 346 | }; 347 | 348 | // TODO: make cheaper to copy or pass by reference or pointer? 349 | struct EvaluatedTable { 350 | using Iterator = std::map::const_iterator; 351 | std::map rows; 352 | Iterator begin() const {return rows.begin();} 353 | Iterator end() const {return rows.end();} 354 | bool empty() const {return rows.empty();} 355 | }; 356 | 357 | struct EvaluatedTableView { 358 | using Iterator = std::map::const_iterator; 359 | Iterator first; 360 | Iterator last; 361 | Iterator begin() const {return first;} 362 | Iterator end() const {return last;} 363 | bool empty() const {return first == last;} 364 | }; 365 | -------------------------------------------------------------------------------- /src/factory.cpp: -------------------------------------------------------------------------------- 1 | #include "factory.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "passes/serialize.h" 8 | 9 | Storage storage; 10 | 11 | namespace { 12 | 13 | template 14 | Expression makeExpression( 15 | CodeRange code, 16 | ElementType&& expression, 17 | ExpressionType type, 18 | ArrayType& array 19 | ) { 20 | array.emplace_back(std::move(expression)); 21 | return Expression{type, array.size() - 1, code}; 22 | } 23 | 24 | } // namespace 25 | 26 | void clearMemory() { 27 | storage = Storage{}; 28 | } 29 | 30 | // MAKERS: 31 | 32 | Expression makeNumber(CodeRange code, Number expression) { 33 | static_assert(sizeof(Number) == sizeof(size_t), ""); 34 | auto result = Expression{}; 35 | result.type = NUMBER; 36 | result.range = code; 37 | memcpy(&result.index, &expression, sizeof(Number)); 38 | return result; 39 | } 40 | 41 | Expression makeCharacter(CodeRange code, Character expression) { 42 | return Expression{CHARACTER, static_cast(expression), code}; 43 | } 44 | 45 | Expression makeDynamicExpression(CodeRange code, DynamicExpression expression) { 46 | return makeExpression(code, expression, DYNAMIC_EXPRESSION, storage.dynamic_expressions); 47 | } 48 | 49 | Expression makeTypedExpression(CodeRange code, TypedExpression expression) { 50 | return makeExpression(code, expression, TYPED_EXPRESSION, storage.typed_expressions); 51 | } 52 | 53 | Expression makeConditional(CodeRange code, Conditional expression) { 54 | return makeExpression(code, expression, CONDITIONAL, storage.conditionals); 55 | } 56 | 57 | Expression makeIs(CodeRange code, IsExpression expression) { 58 | return makeExpression(code, expression, IS, storage.is_expressions); 59 | } 60 | 61 | Expression makeAlternative(CodeRange code, Alternative expression) { 62 | return makeExpression(code, expression, ALTERNATIVE, storage.alternatives); 63 | } 64 | 65 | Expression makeDictionary(CodeRange code, Dictionary expression) { 66 | return makeExpression(code, std::move(expression), DICTIONARY, storage.dictionaries); 67 | } 68 | 69 | Expression makeEvaluatedDictionary(CodeRange code, EvaluatedDictionary expression) { 70 | return makeExpression(code, expression, EVALUATED_DICTIONARY, storage.evaluated_dictionaries); 71 | } 72 | 73 | Expression makeFunction(CodeRange code, Function expression) { 74 | return makeExpression(code, expression, FUNCTION, storage.functions); 75 | } 76 | 77 | Expression makeFunctionBuiltIn(CodeRange code, FunctionBuiltIn expression) { 78 | return makeExpression(code, expression, FUNCTION_BUILT_IN, storage.built_in_functions); 79 | } 80 | 81 | Expression makeFunctionDictionary(CodeRange code, FunctionDictionary expression) { 82 | return makeExpression(code, expression, FUNCTION_DICTIONARY, storage.dictionary_functions); 83 | } 84 | 85 | Expression makeFunctionTuple(CodeRange code, FunctionTuple expression) { 86 | return makeExpression(code, expression, FUNCTION_TUPLE, storage.tuple_functions); 87 | } 88 | 89 | Expression makeTuple(CodeRange code, Tuple expression) { 90 | return makeExpression(code, expression, TUPLE, storage.tuples); 91 | } 92 | 93 | Expression makeEvaluatedTuple(CodeRange code, EvaluatedTuple expression) { 94 | return makeExpression(code, expression, EVALUATED_TUPLE, storage.evaluated_tuples); 95 | } 96 | 97 | Expression makeEvaluatedTuple2(Expression a, Expression b) { 98 | const auto first = storage.expressions.size(); 99 | storage.expressions.push_back(a); 100 | storage.expressions.push_back(b); 101 | const auto last = storage.expressions.size(); 102 | return makeEvaluatedTuple({}, EvaluatedTuple{first, last}); 103 | } 104 | 105 | Expression makeStack(CodeRange code, Stack expression) { 106 | return makeExpression(code, expression, STACK, storage.stacks); 107 | } 108 | 109 | Expression makeEvaluatedStack(CodeRange code, EvaluatedStack expression) { 110 | return makeExpression(code, expression, EVALUATED_STACK, storage.evaluated_stacks); 111 | } 112 | 113 | Expression makeTable(CodeRange code, Table expression) { 114 | return makeExpression(code, expression, TABLE, storage.tables); 115 | } 116 | 117 | Expression makeEvaluatedTable(CodeRange code, EvaluatedTable expression) { 118 | return makeExpression(code, expression, EVALUATED_TABLE, storage.evaluated_tables); 119 | } 120 | 121 | Expression makeEvaluatedTableView(CodeRange code, EvaluatedTableView expression) { 122 | return makeExpression(code, expression, EVALUATED_TABLE_VIEW, storage.evaluated_table_views); 123 | } 124 | 125 | Expression makeLookupChild(CodeRange code, LookupChild expression) { 126 | return makeExpression(code, expression, LOOKUP_CHILD, storage.child_lookups); 127 | } 128 | 129 | Expression makeFunctionApplication(CodeRange code, FunctionApplication expression) { 130 | return makeExpression(code, expression, FUNCTION_APPLICATION, storage.function_applications); 131 | } 132 | 133 | Expression makeLookupSymbol(CodeRange code, LookupSymbol expression) { 134 | return makeExpression(code, expression, LOOKUP_SYMBOL, storage.symbol_lookups); 135 | } 136 | 137 | Expression makeName(CodeRange code, Name expression) { 138 | const auto name_index = storage.name_indices.find(expression); 139 | if (name_index == storage.name_indices.end()) { 140 | storage.name_indices[expression] = storage.names.size(); 141 | return makeExpression(code, expression, NAME, storage.names); 142 | } 143 | else { 144 | return Expression{NAME, name_index->second, code}; 145 | } 146 | } 147 | 148 | Expression makeArgument(CodeRange code, Argument expression) { 149 | return makeExpression(code, expression, ARGUMENT, storage.arguments); 150 | } 151 | 152 | Expression makeDefinition(CodeRange code, Definition expression) { 153 | return makeExpression(code, expression, DEFINITION, storage.definitions); 154 | } 155 | 156 | Expression makePutAssignment(CodeRange code, PutAssignment expression) { 157 | return makeExpression(code, expression, PUT_ASSIGNMENT, storage.put_assignments); 158 | } 159 | 160 | Expression makePutEachAssignment(CodeRange code, PutEachAssignment expression) { 161 | return makeExpression(code, expression, PUT_EACH_ASSIGNMENT, storage.put_each_assignments); 162 | } 163 | 164 | Expression makeDropAssignment(CodeRange code, DropAssignment expression) { 165 | return makeExpression(code, expression, DROP_ASSIGNMENT, storage.drop_assignments); 166 | } 167 | 168 | Expression makeWhileStatement(CodeRange code, WhileStatement expression) { 169 | return makeExpression(code, expression, WHILE_STATEMENT, storage.while_statements); 170 | } 171 | 172 | Expression makeForStatement(CodeRange code, ForStatement expression) { 173 | return makeExpression(code, expression, FOR_STATEMENT, storage.for_statements); 174 | } 175 | 176 | Expression makeForSimpleStatement(CodeRange code, ForSimpleStatement expression) { 177 | return makeExpression(code, expression, FOR_SIMPLE_STATEMENT, storage.for_simple_statements); 178 | } 179 | 180 | Expression makeWhileEndStatement(CodeRange code, WhileEndStatement expression) { 181 | return makeExpression(code, expression, WHILE_END_STATEMENT, storage.while_end_statements); 182 | } 183 | 184 | Expression makeForEndStatement(CodeRange code, ForEndStatement expression) { 185 | return makeExpression(code, expression, FOR_END_STATEMENT, storage.for_end_statements); 186 | } 187 | 188 | Expression makeForSimpleEndStatement(CodeRange code, ForSimpleEndStatement expression) { 189 | return makeExpression(code, expression, FOR_SIMPLE_END_STATEMENT, storage.for_simple_end_statements); 190 | } 191 | 192 | Expression makeString(CodeRange code, String expression) { 193 | return makeExpression(code, expression, STRING, storage.strings); 194 | } 195 | 196 | // GETTERS 197 | 198 | Character getCharacter(Expression expression) { 199 | if (expression.index > 127) { 200 | throw std::runtime_error{ 201 | "I found an error while retrieving a character. " 202 | "A character should have an ASCII value in the range 0-127. " 203 | "But I found one with the ASCII value " + std::to_string(expression.index) 204 | }; 205 | } 206 | return static_cast(expression.index); 207 | } 208 | 209 | Number getNumber(Expression expression) { 210 | static_assert(sizeof(Number) == sizeof(size_t), ""); 211 | Number result; 212 | memcpy(&result, &expression.index, sizeof(Number)); 213 | return result; 214 | } 215 | -------------------------------------------------------------------------------- /src/factory.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "expression.h" 4 | 5 | struct Storage { 6 | std::vector dynamic_expressions; 7 | std::vector typed_expressions; 8 | std::vector evaluated_dictionaries; 9 | std::vector dictionaries; 10 | std::vector conditionals; 11 | std::vector is_expressions; 12 | std::vector alternatives; 13 | std::vector functions; 14 | std::vector built_in_functions; 15 | std::vector dictionary_functions; 16 | std::vector tuple_functions; 17 | std::vector tuples; 18 | std::vector evaluated_tuples; 19 | std::vector stacks; 20 | std::vector evaluated_stacks; 21 | std::vector tables; 22 | std::vector evaluated_tables; 23 | std::vector evaluated_table_views; 24 | std::vector child_lookups; 25 | std::vector function_applications; 26 | std::vector symbol_lookups; 27 | std::vector names; 28 | std::unordered_map name_indices; 29 | std::vector arguments; 30 | std::vector while_statements; 31 | std::vector for_statements; 32 | std::vector for_simple_statements; 33 | std::vector while_end_statements; 34 | std::vector for_end_statements; 35 | std::vector for_simple_end_statements; 36 | std::vector definitions; 37 | std::vector put_assignments; 38 | std::vector put_each_assignments; 39 | std::vector drop_assignments; 40 | std::vector statements; 41 | std::vector expressions; 42 | std::vector strings; 43 | }; 44 | 45 | extern Storage storage; 46 | 47 | void clearMemory(); 48 | 49 | Character getCharacter(Expression expression); 50 | Number getNumber(Expression expression); 51 | 52 | Expression makeNumber(CodeRange code, Number expression); 53 | Expression makeCharacter(CodeRange code, Character expression); 54 | Expression makeDynamicExpression(CodeRange code, DynamicExpression expression); 55 | Expression makeTypedExpression(CodeRange code, TypedExpression expression); 56 | Expression makeConditional(CodeRange code, Conditional expression); 57 | Expression makeIs(CodeRange code, IsExpression expression); 58 | Expression makeAlternative(CodeRange code, Alternative expression); 59 | Expression makeDictionary(CodeRange code, Dictionary expression); 60 | Expression makeEvaluatedDictionary(CodeRange code, EvaluatedDictionary expression); 61 | Expression makeFunction(CodeRange code, Function expression); 62 | Expression makeFunctionBuiltIn(CodeRange code, FunctionBuiltIn expression); 63 | Expression makeFunctionDictionary(CodeRange code, FunctionDictionary expression); 64 | Expression makeFunctionTuple(CodeRange code, FunctionTuple expression); 65 | Expression makeTuple(CodeRange code, Tuple expression); 66 | Expression makeEvaluatedTuple(CodeRange code, EvaluatedTuple expression); 67 | Expression makeEvaluatedTuple2(Expression a, Expression b); 68 | Expression makeStack(CodeRange code, Stack expression); 69 | Expression makeEvaluatedStack(CodeRange code, EvaluatedStack expression); 70 | Expression makeTable(CodeRange code, Table expression); 71 | Expression makeEvaluatedTable(CodeRange code, EvaluatedTable expression); 72 | Expression makeEvaluatedTableView(CodeRange code, EvaluatedTableView expression); 73 | Expression makeLookupChild(CodeRange code, LookupChild expression); 74 | Expression makeFunctionApplication(CodeRange code, FunctionApplication expression); 75 | Expression makeLookupSymbol(CodeRange code, LookupSymbol expression); 76 | Expression makeName(CodeRange code, Name expression); 77 | Expression makeArgument(CodeRange code, Argument expression); 78 | Expression makeDefinition(CodeRange code, Definition expression); 79 | Expression makePutAssignment(CodeRange code, PutAssignment expression); 80 | Expression makePutEachAssignment(CodeRange code, PutEachAssignment expression); 81 | Expression makeDropAssignment(CodeRange code, DropAssignment expression); 82 | Expression makeWhileStatement(CodeRange code, WhileStatement expression); 83 | Expression makeForStatement(CodeRange code, ForStatement expression); 84 | Expression makeForSimpleStatement(CodeRange code, ForSimpleStatement expression); 85 | Expression makeWhileEndStatement(CodeRange code, WhileEndStatement expression); 86 | Expression makeForEndStatement(CodeRange code, ForEndStatement expression); 87 | Expression makeForSimpleEndStatement(CodeRange code, ForSimpleEndStatement expression); 88 | Expression makeString(CodeRange code, String expression); 89 | -------------------------------------------------------------------------------- /src/interpreter.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "mang_lang.h" 7 | 8 | namespace CommandLineArgumentIndex { 9 | enum {PROGRAM_PATH, INPUT_PATH, OUTPUT_PATH}; 10 | } 11 | 12 | int main(int argc, char **argv) { 13 | using namespace std; 14 | if (argc < CommandLineArgumentIndex::INPUT_PATH + 1) { 15 | cout << "Expected input file." << endl; 16 | return 1; 17 | } 18 | const auto input_file_path = std::string{ 19 | argv[CommandLineArgumentIndex::INPUT_PATH] 20 | }; 21 | auto output_file_path = std::string{}; 22 | if (argc < CommandLineArgumentIndex::OUTPUT_PATH + 1) { 23 | output_file_path = input_file_path.substr(0, input_file_path.find_last_of('.')) 24 | + "_evaluated.txt"; 25 | } else { 26 | output_file_path = argv[CommandLineArgumentIndex::OUTPUT_PATH]; 27 | } 28 | 29 | cout << "Reading program from " << input_file_path << " ... "; 30 | auto input_file = ifstream{input_file_path}; 31 | const auto code = string{(istreambuf_iterator(input_file)), istreambuf_iterator()}; 32 | input_file.close(); 33 | cout << "Done." << endl; 34 | 35 | try { 36 | cout << "Evaluating program ... "; 37 | const auto start = std::chrono::steady_clock::now(); 38 | const auto result = evaluate_all(code); 39 | const auto end = std::chrono::steady_clock::now(); 40 | const auto duration_total = std::chrono::duration{end - start}; 41 | cout << "Done. " << std::fixed << std::setprecision(1) 42 | << duration_total.count() << " seconds." << endl; 43 | 44 | cout << "Writing result to " << output_file_path << " ... "; 45 | auto output_file = ofstream{output_file_path}; 46 | output_file << result; 47 | output_file.close(); 48 | cout << "Done." << endl; 49 | } catch (const std::runtime_error& e) { 50 | cout << e.what() << endl; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/mang_lang.cpp: -------------------------------------------------------------------------------- 1 | #include "mang_lang.h" 2 | #include "factory.h" 3 | #include "built_in_functions/built_in_functions.h" 4 | #include "built_in_functions/standard_library.h" 5 | #include "passes/bind.h" 6 | #include "passes/evaluate.h" 7 | #include "passes/parse.h" 8 | #include "passes/serialize.h" 9 | 10 | static 11 | Expression parse(const std::string& string) { 12 | const auto result = makeCodeCharacters(string); 13 | return parseExpression({result.data(), result.data() + result.size()}); 14 | } 15 | 16 | std::string reformat(std::string code) { 17 | std::string result; 18 | serialize(result, parse(code)); 19 | clearMemory(); 20 | return result; 21 | } 22 | 23 | std::string evaluate_types(std::string code) { 24 | const auto built_ins = builtInsTypes(); 25 | const auto std_ast = parse(STANDARD_LIBRARY); 26 | const auto code_ast = parse(code); 27 | // bind(std_ast, built_ins); 28 | // bind(code_ast, std_ast); 29 | const auto standard_library = evaluate_types(std_ast, built_ins); 30 | std::string result; 31 | serialize_types(result, evaluate_types(code_ast, standard_library)); 32 | clearMemory(); 33 | return result; 34 | } 35 | 36 | std::string evaluate_all(std::string code) { 37 | const auto built_ins = builtIns(); 38 | const auto built_ins_types = builtInsTypes(); 39 | const auto std_ast = parse(STANDARD_LIBRARY); 40 | const auto code_ast = parse(code); 41 | // bind(std_ast, built_ins_types); 42 | // bind(code_ast, std_ast); 43 | const auto std_checked = evaluate_types(std_ast, built_ins_types); 44 | const auto code_checked = evaluate_types(code_ast, std_checked); 45 | const auto std_evaluated = evaluate(std_ast, built_ins); 46 | const auto code_evaluated = evaluate(code_ast, std_evaluated); 47 | std::string result; 48 | serialize(result, code_evaluated); 49 | clearMemory(); 50 | std::ignore = code_checked; 51 | return result; 52 | } 53 | -------------------------------------------------------------------------------- /src/mang_lang.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | std::string reformat(std::string code); 5 | std::string evaluate_types(std::string code); 6 | std::string evaluate_all(std::string code); 7 | -------------------------------------------------------------------------------- /src/parsing.cpp: -------------------------------------------------------------------------------- 1 | #include "parsing.h" 2 | #include 3 | #include 4 | #include 5 | 6 | std::string serializeCodeCharacter(const CodeCharacter* c) { 7 | return "row " + std::to_string(c->row + 1) + " and column " 8 | + std::to_string(c->column + 1); 9 | } 10 | 11 | std::string describeLocation(CodeRange code) { 12 | if (code.begin() == nullptr || code.end() == nullptr) { 13 | return " at unknown location."; 14 | } 15 | return " between " + 16 | serializeCodeCharacter(code.begin()) + " and " + 17 | serializeCodeCharacter(code.end()); 18 | } 19 | 20 | ParseException::ParseException(const std::string& description, CodeRange code) 21 | : std::runtime_error(description + describeLocation(code)) 22 | {} 23 | 24 | char rawCharacter(CodeCharacter c) { 25 | return c.character; 26 | } 27 | 28 | std::string rawString(CodeRange code) { 29 | auto s = std::string{}; 30 | std::transform(code.begin(), code.end(), std::back_inserter(s), rawCharacter); 31 | return s; 32 | } 33 | 34 | std::vector makeCodeCharacters(const std::string& string) { 35 | auto result = std::vector{}; 36 | auto column = size_t{0}; 37 | auto row = size_t{0}; 38 | for (const auto& character : string) { 39 | result.push_back(CodeCharacter{character, row, column}); 40 | ++column; 41 | if (character == '\n') { 42 | ++row; 43 | column = 0; 44 | } 45 | } 46 | return result; 47 | } 48 | 49 | bool isDigit(CodeCharacter c) { 50 | return isdigit(c.character); 51 | } 52 | 53 | bool isSign(CodeCharacter c) { 54 | return c.character == '+' || c.character == '-'; 55 | } 56 | 57 | bool isLetter(CodeCharacter c) { 58 | return std::isalpha(c.character); 59 | } 60 | 61 | bool isNameCharacter(CodeCharacter c) { 62 | return isLetter(c) || isDigit(c) || c.character == '_'; 63 | } 64 | 65 | bool isWhiteSpace(CodeCharacter c) { 66 | return isspace(c.character); 67 | } 68 | 69 | bool haveSameCharacters(CodeCharacter a, CodeCharacter b) { 70 | return a.character == b.character; 71 | } 72 | 73 | bool isKeyword(CodeRange code, const std::string& word) { 74 | const auto w = makeCodeCharacters(word); 75 | if (code.size() < w.size()) { 76 | return false; 77 | } 78 | if (!std::equal(w.begin(), w.end(), code.begin(), haveSameCharacters)) { 79 | return false; 80 | } 81 | const auto after = code.begin() + w.size(); 82 | if (after == code.end()) { 83 | return true; 84 | } 85 | return !isNameCharacter(*after); 86 | } 87 | 88 | bool startsWith(CodeRange code, const std::string& word) { 89 | const auto w = makeCodeCharacters(word); 90 | if (code.size() < w.size()) { 91 | return false; 92 | } 93 | return std::equal(w.begin(), w.end(), code.begin(), haveSameCharacters); 94 | } 95 | 96 | bool startsWith(CodeRange code, char c) { 97 | return !code.empty() && code.begin()->character == c; 98 | } 99 | 100 | CodeRange parseWhiteSpace(CodeRange code) { 101 | return parseWhile(code, isWhiteSpace); 102 | } 103 | 104 | CodeRange parseCharacter(CodeRange code) { 105 | throwIfEmpty(code); 106 | auto it = code.begin(); 107 | ++it; 108 | return CodeRange{it, code.end()}; 109 | } 110 | 111 | CodeRange parseCharacter(CodeRange code, char expected) { 112 | throwIfEmpty(code); 113 | auto it = code.begin(); 114 | const auto actual = it->character; 115 | if (it->character != expected) { 116 | const auto description = std::string{"Parsing expected \'"} 117 | + expected + "\' but got \'" + actual + "\'"; 118 | throw ParseException(description, CodeRange{it, code.end()}); 119 | } 120 | ++it; 121 | return CodeRange{it, code.end()}; 122 | } 123 | 124 | CodeRange parseOptionalCharacter(CodeRange code, char c) { 125 | if (code.empty()) { 126 | return code; 127 | } 128 | auto it = code.begin(); 129 | if (it->character == c) { 130 | ++it; 131 | } 132 | return CodeRange{it, code.end()}; 133 | } 134 | 135 | CodeRange parseKeyword(CodeRange code, const std::string& keyword) { 136 | if (code.size() < keyword.size()) { 137 | throw ParseException( 138 | "Reached end of file when parsing " + keyword, code 139 | ); 140 | } 141 | for (const auto c : keyword) { 142 | code = parseCharacter(code, c); 143 | } 144 | return code; 145 | } 146 | 147 | void throwIfEmpty(CodeRange code) { 148 | if (code.empty()) { 149 | throw ParseException( 150 | "Unexpected end of source while parsing", code 151 | ); 152 | } 153 | } 154 | 155 | void throwParseException(CodeRange code) { 156 | throw ParseException("Does not recognize expression to parse", code); 157 | } 158 | -------------------------------------------------------------------------------- /src/parsing.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | struct CodeCharacter { 9 | char character = 'a'; 10 | size_t row = 0; 11 | size_t column = 0; 12 | }; 13 | 14 | struct CodeRange { 15 | const CodeCharacter* first = nullptr; 16 | const CodeCharacter* last = nullptr; 17 | const CodeCharacter* begin() const {return first;} 18 | const CodeCharacter* end() const {return last;} 19 | bool empty() const {return first == last;} 20 | size_t size() const {return last - first;} 21 | }; 22 | 23 | struct ParseException : public std::runtime_error 24 | { 25 | ParseException(const std::string& description, CodeRange code); 26 | using runtime_error::runtime_error; 27 | }; 28 | 29 | std::string describeLocation(CodeRange code); 30 | 31 | void throwIfEmpty(CodeRange code); 32 | void throwParseException(CodeRange code); 33 | 34 | char rawCharacter(CodeCharacter c); 35 | 36 | std::string rawString(CodeRange code); 37 | 38 | std::vector makeCodeCharacters(const std::string& string); 39 | 40 | bool haveSameCharacters(CodeCharacter a, CodeCharacter b); 41 | 42 | bool isDigit(CodeCharacter c); 43 | 44 | bool isSign(CodeCharacter c); 45 | 46 | bool isLetter(CodeCharacter c); 47 | 48 | bool isNameCharacter(CodeCharacter c); 49 | 50 | bool isWhiteSpace(CodeCharacter c); 51 | 52 | bool isKeyword(CodeRange code, const std::string& word); 53 | 54 | bool startsWith(CodeRange code, const std::string& word); 55 | 56 | bool startsWith(CodeRange code, char c); 57 | 58 | CodeRange parseWhiteSpace(CodeRange code); 59 | 60 | CodeRange parseCharacter(CodeRange code); 61 | 62 | CodeRange parseCharacter(CodeRange code, char expected); 63 | 64 | template 65 | CodeRange parseCharacter(CodeRange code, Predicate predicate) { 66 | throwIfEmpty(code); 67 | auto it = code.begin(); 68 | if (!predicate(*it)) { 69 | throw ParseException(std::string{"Parser got unexpected char"} + it->character); 70 | } 71 | ++it; 72 | return CodeRange{it, code.end()}; 73 | } 74 | 75 | CodeRange parseOptionalCharacter(CodeRange code, char c); 76 | 77 | template 78 | CodeRange parseOptionalCharacter(CodeRange code, Predicate predicate) { 79 | auto it = code.first; 80 | if (it == code.last) { 81 | return {it, code.end()}; 82 | } 83 | if (predicate(*it)) { 84 | ++it; 85 | } 86 | return CodeRange{it, code.end()}; 87 | } 88 | 89 | CodeRange parseKeyword(CodeRange code, const std::string& keyword); 90 | 91 | template 92 | CodeRange parseWhile(CodeRange code, Predicate predicate) { 93 | return CodeRange{std::find_if_not(code.begin(), code.end(), predicate), code.end()}; 94 | } 95 | -------------------------------------------------------------------------------- /src/passes/bind.cpp: -------------------------------------------------------------------------------- 1 | #include "bind.h" 2 | 3 | #include 4 | 5 | #include "../expression.h" 6 | #include "../factory.h" 7 | 8 | namespace { 9 | 10 | struct DictionaryNameIndexer { 11 | size_t size() const { 12 | return dictionary_index_from_global_index.size(); 13 | } 14 | void bindName(BoundLocalName& name) { 15 | name.dictionary_index = getDictionaryIndex(name.global_index); 16 | } 17 | private: 18 | size_t getDictionaryIndex(size_t global_index) { 19 | const auto it = dictionary_index_from_global_index.find(global_index); 20 | if (it != dictionary_index_from_global_index.end()) { 21 | return it->second; 22 | } 23 | else { 24 | const auto count = size(); 25 | dictionary_index_from_global_index[global_index] = count; 26 | return count; 27 | } 28 | } 29 | std::unordered_map dictionary_index_from_global_index; 30 | }; 31 | 32 | void bindFunctionDictionary(Expression function_dictionary, Expression environment) { 33 | const auto function_dictionary_struct = storage.dictionary_functions.at(function_dictionary.index); 34 | bind(function_dictionary_struct.body, environment); 35 | } 36 | 37 | void bindFunctionTuple(Expression function_tuple, Expression environment) { 38 | const auto function_tuple_struct = storage.tuple_functions.at(function_tuple.index); 39 | bind(function_tuple_struct.body, environment); 40 | } 41 | 42 | void bindFunction(Expression function, Expression environment) { 43 | const auto function_struct = storage.functions.at(function.index); 44 | bind(function_struct.body, environment); 45 | } 46 | 47 | void bindStack(Expression stack, Expression environment) { 48 | while (stack.type != EMPTY_STACK) { 49 | if (stack.type != STACK) { 50 | throw std::runtime_error( 51 | std::string{"\n\nI have found a static type error."} + 52 | "\nIt happens in bindStack. " + 53 | "\nInstead of a stack I got a " + NAMES[stack.type] + 54 | ".\n" 55 | ); 56 | } 57 | const auto stack_struct = storage.stacks.at(stack.index); 58 | const auto top = stack_struct.top; 59 | const auto rest = stack_struct.rest; 60 | bind(top, environment); 61 | stack = rest; 62 | } 63 | } 64 | 65 | void bindTuple(Expression tuple, Expression environment) { 66 | const auto tuple_struct = storage.tuples.at(tuple.index); 67 | for (size_t i = tuple_struct.first; i < tuple_struct.last; ++i) { 68 | const auto expression = storage.expressions.at(i); 69 | bind(expression, environment); 70 | } 71 | } 72 | 73 | void bindTable(Expression table, Expression environment) { 74 | const auto table_struct = storage.tables.at(table.index); 75 | for (const auto& row : table_struct.rows) { 76 | bind(row.key, environment); 77 | bind(row.value, environment); 78 | } 79 | } 80 | 81 | void bindLookupChild(Expression lookup_child, Expression environment) { 82 | const auto lookup_child_struct = storage.child_lookups.at(lookup_child.index); 83 | bind(lookup_child_struct.child, environment); 84 | } 85 | 86 | void bindDynamicExpression(Expression expression, Expression environment) { 87 | const auto inner_expression = storage.dynamic_expressions.at(expression.index).expression; 88 | bind(inner_expression, environment); 89 | } 90 | 91 | void bindConditional(Expression conditional, Expression environment) { 92 | const auto conditional_struct = storage.conditionals.at(conditional.index); 93 | for (auto a = conditional_struct.alternative_first; 94 | a.index <= conditional_struct.alternative_last.index; 95 | ++a.index 96 | ) { 97 | const auto alternative = storage.alternatives.at(a.index); 98 | bind(alternative.left, environment); 99 | bind(alternative.right, environment); 100 | } 101 | bind(conditional_struct.expression_else, environment); 102 | } 103 | 104 | void bindIs(Expression is, Expression environment) { 105 | const auto is_struct = storage.is_expressions.at(is.index); 106 | bind(is_struct.input, environment); 107 | for (auto a = is_struct.alternative_first; 108 | a.index <= is_struct.alternative_last.index; 109 | ++a.index 110 | ) { 111 | const auto alternative = storage.alternatives.at(a.index); 112 | bind(alternative.left, environment); 113 | bind(alternative.right, environment); 114 | } 115 | bind(is_struct.expression_else, environment); 116 | } 117 | 118 | void bindLookupSymbol(Expression expression, Expression environment) { 119 | (void)expression; 120 | (void)environment; 121 | // TODO: resolve and bind name. 122 | } 123 | 124 | void bindTypedExpression(Expression expression, Expression environment) { 125 | const auto expression_struct = storage.typed_expressions.at(expression.index); 126 | // TODO: resolve type_name and bind it. 127 | bind(expression_struct.value, environment); 128 | } 129 | 130 | void bindFunctionApplication( 131 | Expression function_application, Expression environment 132 | ) { 133 | const auto function_application_struct = storage.function_applications.at(function_application.index); 134 | // TODO: resolve and bind function name. 135 | bind(function_application_struct.child, environment); 136 | } 137 | 138 | void bindDictionary(Expression dictionary, Expression environment) { 139 | auto& dictionary_struct = storage.dictionaries.at(dictionary.index); 140 | auto indexer = DictionaryNameIndexer{}; 141 | for (size_t i = dictionary_struct.statement_first; i < dictionary_struct.statement_last; ++i) { 142 | const auto statement = storage.statements.at(i); 143 | const auto type = statement.type; 144 | if (type == DEFINITION) { 145 | auto& definition = storage.definitions.at(statement.index); 146 | indexer.bindName(definition.name); 147 | bind(definition.expression, environment); 148 | } 149 | else if (type == PUT_ASSIGNMENT) { 150 | auto& put_assignment = storage.put_assignments.at(statement.index); 151 | indexer.bindName(put_assignment.name); 152 | bind(put_assignment.expression, environment); 153 | } 154 | else if (type == PUT_EACH_ASSIGNMENT) { 155 | auto& put_each_assignment = storage.put_each_assignments.at(statement.index); 156 | indexer.bindName(put_each_assignment.name); 157 | bind(put_each_assignment.expression, environment); 158 | } 159 | else if (type == DROP_ASSIGNMENT) { 160 | auto& drop_assignment = storage.drop_assignments.at(statement.index); 161 | indexer.bindName(drop_assignment.name); 162 | } 163 | else if (type == FOR_STATEMENT) { 164 | auto& for_statement = storage.for_statements.at(statement.index); 165 | indexer.bindName(for_statement.item_name); 166 | indexer.bindName(for_statement.container_name); 167 | } 168 | else if (type == FOR_SIMPLE_STATEMENT) { 169 | auto& for_statement = storage.for_simple_statements.at(statement.index); 170 | indexer.bindName(for_statement.container_name); 171 | } 172 | else if (type == WHILE_STATEMENT) { 173 | const auto while_statement = storage.while_statements.at(statement.index); 174 | bind(while_statement.expression, environment); 175 | } 176 | } 177 | dictionary_struct.definition_count = indexer.size(); 178 | } 179 | 180 | } // namespace 181 | 182 | void bind(Expression expression, Expression environment) { 183 | switch (expression.type) { 184 | case NUMBER: return; 185 | case CHARACTER: return; 186 | case YES: return; 187 | case NO: return; 188 | case EMPTY_STRING: return; 189 | case STRING: return; 190 | case EMPTY_STACK: return; 191 | 192 | case FUNCTION: return bindFunction(expression, environment); 193 | case FUNCTION_TUPLE: return bindFunctionTuple(expression, environment); 194 | case FUNCTION_DICTIONARY: return bindFunctionDictionary(expression, environment); 195 | case LOOKUP_SYMBOL: return bindLookupSymbol(expression, environment); 196 | 197 | case STACK: return bindStack(expression, environment); 198 | case TUPLE: return bindTuple(expression, environment); 199 | case TABLE: return bindTable(expression, environment); 200 | case LOOKUP_CHILD: return bindLookupChild(expression, environment); 201 | case TYPED_EXPRESSION: return bindTypedExpression(expression, environment); 202 | 203 | // These are different for types and values: 204 | case DYNAMIC_EXPRESSION: return bindDynamicExpression(expression, environment); 205 | case CONDITIONAL: return bindConditional(expression, environment); 206 | case IS: return bindIs(expression, environment); 207 | case DICTIONARY: return bindDictionary(expression, environment); 208 | case FUNCTION_APPLICATION: return bindFunctionApplication(expression, environment); 209 | 210 | default: throw UnexpectedExpression(expression.type, "bind operation"); 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /src/passes/bind.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct Expression; 4 | 5 | void bind(Expression expression, Expression environment); 6 | -------------------------------------------------------------------------------- /src/passes/evaluate.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct Expression; 4 | 5 | Expression evaluate_types(Expression expression, Expression environment); 6 | Expression evaluate(Expression expression, Expression environment); 7 | -------------------------------------------------------------------------------- /src/passes/parse.cpp: -------------------------------------------------------------------------------- 1 | #include "parse.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "../factory.h" 8 | #include "../built_in_functions/container.h" 9 | 10 | namespace { 11 | 12 | BoundLocalName getUnboundLocalName(Expression name) { 13 | return BoundLocalName{name.index, 0}; 14 | } 15 | 16 | struct DictionaryNameIndexer { 17 | size_t size() const { 18 | return dictionary_index_from_global_index.size(); 19 | } 20 | void bindName(BoundLocalName& name) { 21 | name.dictionary_index = getDictionaryIndex(name.global_index); 22 | } 23 | private: 24 | size_t getDictionaryIndex(size_t global_index) { 25 | const auto it = dictionary_index_from_global_index.find(global_index); 26 | if (it != dictionary_index_from_global_index.end()) { 27 | return it->second; 28 | } 29 | else { 30 | const auto count = size(); 31 | dictionary_index_from_global_index[global_index] = count; 32 | return count; 33 | } 34 | } 35 | std::unordered_map dictionary_index_from_global_index; 36 | }; 37 | 38 | const CodeCharacter* end(Expression expression) { 39 | return expression.range.last; 40 | } 41 | 42 | Expression parseCharacterExpression(CodeRange code) { 43 | auto first = code.begin(); 44 | code = parseCharacter(code, '\''); 45 | const auto it = code.begin(); 46 | code = parseCharacter(code); 47 | const auto value = it->character; 48 | code = parseCharacter(code, '\''); 49 | return makeCharacter(CodeRange{first, code.begin()}, {value}); 50 | } 51 | 52 | Expression parseAlternative(CodeRange code) { 53 | auto first = code.begin(); 54 | auto left = parseExpression(code); 55 | code.first = end(left); 56 | code = parseWhiteSpace(code); 57 | code = parseKeyword(code, "then"); 58 | code = parseWhiteSpace(code); 59 | auto right = parseExpression(code); 60 | code.first = end(right); 61 | code = parseWhiteSpace(code); 62 | return makeAlternative({first, code.first}, Alternative{left, right}); 63 | } 64 | 65 | Expression parseConditional(CodeRange code) { 66 | auto first = code.begin(); 67 | 68 | code = parseKeyword(code, "if"); 69 | code = parseWhiteSpace(code); 70 | 71 | auto alternatives = std::vector{}; 72 | 73 | while (!isKeyword(code, "else")) { 74 | alternatives.push_back(parseAlternative(code)); 75 | code.first = end(alternatives.back()); 76 | } 77 | 78 | code = parseKeyword(code, "else"); 79 | code = parseWhiteSpace(code); 80 | auto expression_else = parseExpression(code); 81 | code.first = end(expression_else); 82 | code = parseWhiteSpace(code); 83 | 84 | // TODO: verify parsing of nested alternatives. This looks suspicious. 85 | return makeConditional( 86 | CodeRange{first, code.begin()}, 87 | Conditional{alternatives.front(), alternatives.back(), expression_else} 88 | ); 89 | } 90 | 91 | Expression parseIs(CodeRange code) { 92 | auto first = code.begin(); 93 | 94 | code = parseKeyword(code, "is"); 95 | code = parseWhiteSpace(code); 96 | auto input = parseExpression(code); 97 | code.first = end(input); 98 | code = parseWhiteSpace(code); 99 | 100 | auto alternatives = std::vector{}; 101 | 102 | while (!isKeyword(code, "else")) { 103 | alternatives.push_back(parseAlternative(code)); 104 | code.first = end(alternatives.back()); 105 | } 106 | 107 | code = parseKeyword(code, "else"); 108 | code = parseWhiteSpace(code); 109 | auto expression_else = parseExpression(code); 110 | code.first = end(expression_else); 111 | code = parseWhiteSpace(code); 112 | 113 | // TODO: verify parsing of nested alternatives. This looks suspicious. 114 | return makeIs( 115 | CodeRange{first, code.begin()}, 116 | IsExpression{input, alternatives.front(), alternatives.back(), expression_else} 117 | ); 118 | } 119 | 120 | Expression parseName(CodeRange code) { 121 | auto first = code.begin(); 122 | code = parseWhile(code, isNameCharacter); 123 | return makeName( 124 | CodeRange{first, code.first}, 125 | rawString({first, code.first}) 126 | ); 127 | } 128 | 129 | Expression parseArgument(CodeRange code) { 130 | auto first = code.begin(); 131 | auto first_name = parseName(code); 132 | code.first = end(first_name); 133 | code = parseWhiteSpace(code); 134 | if (startsWith(code, ':')) { 135 | code = parseCharacter(code); 136 | code = parseWhiteSpace(code); 137 | auto second_name = parseName(code); 138 | code.first = end(second_name); 139 | const auto type = makeLookupSymbol( 140 | CodeRange{first, end(first_name)}, {first_name.index} 141 | ); 142 | return makeArgument( 143 | CodeRange{first, code.first}, Argument{type, second_name.index} 144 | ); 145 | } 146 | else { 147 | return makeArgument( 148 | CodeRange{first, code.first}, Argument{{}, first_name.index} 149 | ); 150 | } 151 | } 152 | 153 | Expression parseNamedElement(CodeRange code) { 154 | auto first = code.begin(); 155 | auto name = parseName(code); 156 | code.first = end(name); 157 | code = parseWhiteSpace(code); 158 | 159 | if (startsWith(code, '=')) { 160 | code = parseCharacter(code, '='); 161 | code = parseWhiteSpace(code); 162 | auto expression = parseExpression(code); 163 | code.first = end(expression); 164 | code = parseWhiteSpace(code); 165 | return makeDefinition( 166 | CodeRange{first, code.first}, 167 | Definition{getUnboundLocalName(name), expression} 168 | ); 169 | } 170 | else if (startsWith(code, '-')) { 171 | code = parseKeyword(code, "--"); 172 | code = parseWhiteSpace(code); 173 | return makeDropAssignment( 174 | CodeRange{first, code.first}, 175 | DropAssignment{getUnboundLocalName(name)} 176 | ); 177 | } 178 | else if (startsWith(code, "+=")) { 179 | code = parseKeyword(code, "+="); 180 | code = parseWhiteSpace(code); 181 | auto expression = parseExpression(code); 182 | code.first = end(expression); 183 | code = parseWhiteSpace(code); 184 | return makePutAssignment( 185 | CodeRange{first, code.first}, 186 | PutAssignment{getUnboundLocalName(name), expression} 187 | ); 188 | } 189 | else { 190 | code = parseKeyword(code, "++="); 191 | code = parseWhiteSpace(code); 192 | auto expression = parseExpression(code); 193 | code.first = end(expression); 194 | code = parseWhiteSpace(code); 195 | return makePutEachAssignment( 196 | CodeRange{first, code.first}, 197 | PutEachAssignment{getUnboundLocalName(name), expression} 198 | ); 199 | } 200 | } 201 | 202 | Expression parseWhileStatement(CodeRange code) { 203 | auto first = code.begin(); 204 | code = parseKeyword(code, "while"); 205 | code = parseWhiteSpace(code); 206 | auto expression = parseExpression(code); 207 | code.first = end(expression); 208 | code = parseWhiteSpace(code); 209 | return makeWhileStatement(CodeRange{first, code.first}, {expression, 0}); 210 | } 211 | 212 | Expression parseForStatement(CodeRange code) { 213 | const auto first = code.begin(); 214 | code = parseKeyword(code, "for"); 215 | code = parseWhiteSpace(code); 216 | const auto first_name = parseName(code); 217 | code.first = end(first_name); 218 | code = parseWhiteSpace(code); 219 | if (isKeyword(code, "in")) { 220 | code = parseKeyword(code, "in"); 221 | code = parseWhiteSpace(code); 222 | auto second_name = parseName(code); 223 | code.first = end(second_name); 224 | return makeForStatement( 225 | CodeRange{first, code.first}, 226 | ForStatement{ 227 | getUnboundLocalName(first_name), 228 | getUnboundLocalName(second_name), 229 | 0, 230 | } 231 | ); 232 | } 233 | else { 234 | return makeForSimpleStatement(CodeRange{first, code.first}, 235 | ForSimpleStatement{getUnboundLocalName(first_name), 0} 236 | ); 237 | } 238 | } 239 | 240 | Expression parseWhileEndStatement(CodeRange code, size_t start_index) { 241 | auto first = code.begin(); 242 | code = parseKeyword(code, "end"); 243 | code = parseWhiteSpace(code); 244 | return makeWhileEndStatement(CodeRange{first, code.first}, {start_index}); 245 | } 246 | 247 | Expression parseForEndStatement(CodeRange code, size_t start_index) { 248 | auto first = code.begin(); 249 | code = parseKeyword(code, "end"); 250 | code = parseWhiteSpace(code); 251 | return makeForEndStatement(CodeRange{first, code.first}, {start_index}); 252 | } 253 | 254 | Expression parseForSimpleEndStatement(CodeRange code, size_t start_index) { 255 | auto first = code.begin(); 256 | code = parseKeyword(code, "end"); 257 | code = parseWhiteSpace(code); 258 | return makeForSimpleEndStatement(CodeRange{first, code.first}, {start_index}); 259 | } 260 | 261 | Expression parseReturnStatement(CodeRange code) { 262 | auto first = code.begin(); 263 | code = parseKeyword(code, "return"); 264 | code = parseWhiteSpace(code); 265 | return Expression{RETURN_STATEMENT, 0, CodeRange{first, code.first}}; 266 | } 267 | 268 | void bindDictionaryNames(Dictionary& dictionary_struct) { 269 | auto indexer = DictionaryNameIndexer{}; 270 | 271 | for (size_t i = dictionary_struct.statement_first; i < dictionary_struct.statement_last; ++i) { 272 | const auto statement = storage.statements.at(i); 273 | const auto type = statement.type; 274 | if (type == DEFINITION) { 275 | auto& definition = storage.definitions.at(statement.index); 276 | indexer.bindName(definition.name); 277 | } 278 | else if (type == PUT_ASSIGNMENT) { 279 | auto& put_assignment = storage.put_assignments.at(statement.index); 280 | indexer.bindName(put_assignment.name); 281 | } 282 | else if (type == PUT_EACH_ASSIGNMENT) { 283 | auto& put_each_assignment = storage.put_each_assignments.at(statement.index); 284 | indexer.bindName(put_each_assignment.name); 285 | } 286 | else if (type == DROP_ASSIGNMENT) { 287 | auto& drop_assignment = storage.drop_assignments.at(statement.index); 288 | indexer.bindName(drop_assignment.name); 289 | } 290 | else if (type == FOR_STATEMENT) { 291 | auto& for_statement = storage.for_statements.at(statement.index); 292 | indexer.bindName(for_statement.item_name); 293 | indexer.bindName(for_statement.container_name); 294 | } 295 | else if (type == FOR_SIMPLE_STATEMENT) { 296 | auto& for_statement = storage.for_simple_statements.at(statement.index); 297 | indexer.bindName(for_statement.container_name); 298 | } 299 | } 300 | dictionary_struct.definition_count = indexer.size(); 301 | } 302 | 303 | Expression parseDictionary(CodeRange code) { 304 | auto first = code.begin(); 305 | code = parseCharacter(code, '{'); 306 | code = parseWhiteSpace(code); 307 | auto statements = std::vector{}; 308 | auto loop_start_indices = std::vector{}; 309 | while (!::startsWith(code, '}')) { 310 | code = parseWhiteSpace(code); 311 | throwIfEmpty(code); 312 | if (isKeyword(code, "while")) { 313 | loop_start_indices.push_back(statements.size()); 314 | statements.push_back(parseWhileStatement(code)); 315 | } 316 | else if (isKeyword(code, "for")) { 317 | loop_start_indices.push_back(statements.size()); 318 | statements.push_back(parseForStatement(code)); 319 | } 320 | else if (isKeyword(code, "end")) { 321 | const auto loop_end_index = statements.size(); 322 | if (loop_start_indices.empty()) { 323 | throw ParseException("end not matching while or for", code); 324 | } 325 | const auto loop_start_index = loop_start_indices.back(); 326 | loop_start_indices.pop_back(); 327 | const auto start_expression = statements.at(loop_start_index); 328 | if (start_expression.type == WHILE_STATEMENT) { 329 | storage.while_statements.at(start_expression.index).end_index = loop_end_index; 330 | statements.push_back(parseWhileEndStatement(code, loop_start_index)); 331 | } else if (start_expression.type == FOR_STATEMENT) { 332 | storage.for_statements.at(start_expression.index).end_index = loop_end_index; 333 | statements.push_back(parseForEndStatement(code, loop_start_index)); 334 | } else if (start_expression.type == FOR_SIMPLE_STATEMENT) { 335 | storage.for_simple_statements.at(start_expression.index).end_index = loop_end_index; 336 | statements.push_back(parseForSimpleEndStatement(code, loop_start_index)); 337 | } else { 338 | throw ParseException("Unexpected start type for loop", code); 339 | } 340 | } 341 | else if (isKeyword(code, "return")) { 342 | statements.push_back(parseReturnStatement(code)); 343 | } 344 | else { 345 | statements.push_back(parseNamedElement(code)); 346 | } 347 | code.first = end(statements.back()); 348 | } 349 | code = parseCharacter(code, '}'); 350 | 351 | const auto statements_first = storage.statements.size(); 352 | for (const auto statement : statements) { 353 | storage.statements.push_back(statement); 354 | } 355 | const auto statements_last = storage.statements.size(); 356 | 357 | auto dictionary = Dictionary{statements_first, statements_last, 0}; 358 | bindDictionaryNames(dictionary); 359 | return makeDictionary(CodeRange{first, code.begin()}, std::move(dictionary)); 360 | } 361 | 362 | Expression parseFunction(CodeRange code) { 363 | auto first = code.begin(); 364 | auto argument = parseArgument(code); 365 | code.first = end(argument); 366 | code = parseWhiteSpace(code); 367 | code = parseKeyword(code, "out"); 368 | auto body = parseExpression(code); 369 | code.first = end(body); 370 | return makeFunction( 371 | CodeRange{first, code.begin()}, 372 | {Expression{}, argument.index, body} 373 | ); 374 | } 375 | 376 | Expression parseFunctionDictionary(CodeRange code) { 377 | auto first = code.begin(); 378 | code = parseCharacter(code, '{'); 379 | code = parseWhiteSpace(code); 380 | 381 | const auto first_argument = Expression{ 382 | ARGUMENT, storage.arguments.size(), CodeRange{} 383 | }; 384 | auto last_argument = first_argument; 385 | 386 | while (!::startsWith(code, '}')) { 387 | throwIfEmpty(code); 388 | const auto argument = parseArgument(code); 389 | code.first = end(argument); 390 | ++last_argument.index; 391 | code = parseWhiteSpace(code); 392 | } 393 | code = parseCharacter(code, '}'); 394 | code = parseWhiteSpace(code); 395 | code = parseKeyword(code, "out"); 396 | auto body = parseExpression(code); 397 | code.first = end(body); 398 | return makeFunctionDictionary( 399 | CodeRange{first, code.begin()}, 400 | {Expression{}, first_argument.index, last_argument.index, body} 401 | ); 402 | } 403 | 404 | Expression parseFunctionTuple(CodeRange code) { 405 | auto first = code.begin(); 406 | code = parseCharacter(code, '('); 407 | code = parseWhiteSpace(code); 408 | 409 | const auto first_argument = Expression{ 410 | ARGUMENT, storage.arguments.size(), CodeRange{} 411 | }; 412 | auto last_argument = first_argument; 413 | 414 | while (!::startsWith(code, ')')) { 415 | throwIfEmpty(code); 416 | const auto name = parseArgument(code); 417 | code.first = end(name); 418 | ++last_argument.index; 419 | code = parseWhiteSpace(code); 420 | } 421 | code = parseCharacter(code, ')'); 422 | code = parseWhiteSpace(code); 423 | code = parseKeyword(code, "out"); 424 | auto body = parseExpression(code); 425 | code.first = end(body); 426 | return makeFunctionTuple( 427 | CodeRange{first, code.begin()}, 428 | {Expression{}, first_argument.index, last_argument.index, body} 429 | ); 430 | } 431 | 432 | Expression parseAnyFunction(CodeRange code) { 433 | code = parseKeyword(code, "in"); 434 | code = parseWhiteSpace(code); 435 | if (startsWith(code, '{')) {return parseFunctionDictionary(code);} 436 | if (startsWith(code, '(')) {return parseFunctionTuple(code);} 437 | return parseFunction(code); 438 | } 439 | 440 | Expression parseStack(CodeRange code) { 441 | auto first = code.begin(); 442 | code = parseCharacter(code, '['); 443 | code = parseWhiteSpace(code); 444 | auto items = std::vector{}; 445 | while (!::startsWith(code, ']')) { 446 | throwIfEmpty(code); 447 | auto item = parseExpression(code); 448 | code.first = end(item); 449 | code = parseWhiteSpace(code); 450 | items.push_back(item); 451 | } 452 | std::reverse(items.begin(), items.end()); 453 | auto stack = Expression{EMPTY_STACK, 0, CodeRange{}}; 454 | for (const auto& item : items) { 455 | stack = putStack(stack, item); 456 | } 457 | code = parseCharacter(code, ']'); 458 | stack.range = CodeRange{first, code.first}; 459 | return stack; 460 | } 461 | 462 | Expression parseTuple(CodeRange code) { 463 | auto first = code.begin(); 464 | code = parseCharacter(code, '('); 465 | code = parseWhiteSpace(code); 466 | auto expressions = std::vector{}; 467 | while (!::startsWith(code, ')')) { 468 | throwIfEmpty(code); 469 | auto expression = parseExpression(code); 470 | code.first = end(expression); 471 | expressions.push_back(expression); 472 | code = parseWhiteSpace(code); 473 | } 474 | const auto first_expression = storage.expressions.size(); 475 | for (const auto expression : expressions) { 476 | storage.expressions.push_back(expression); 477 | } 478 | const auto last_expression = storage.expressions.size(); 479 | code = parseCharacter(code, ')'); 480 | return makeTuple(CodeRange{first, code.first}, Tuple{first_expression, last_expression}); 481 | } 482 | 483 | Expression parseTable(CodeRange code) { 484 | auto first = code.begin(); 485 | code = parseCharacter(code, '<'); 486 | code = parseWhiteSpace(code); 487 | auto rows = std::vector{}; 488 | while (!::startsWith(code, '>')) { 489 | throwIfEmpty(code); 490 | code = parseCharacter(code, '('); 491 | code = parseWhiteSpace(code); 492 | const auto key = parseExpression(code); 493 | code.first = end(key); 494 | code = parseWhiteSpace(code); 495 | const auto value = parseExpression(code); 496 | code.first = end(value); 497 | code = parseWhiteSpace(code); 498 | code = parseCharacter(code, ')'); 499 | code = parseWhiteSpace(code); 500 | rows.push_back({key, value}); 501 | } 502 | code = parseCharacter(code, '>'); 503 | return makeTable(CodeRange{first, code.first}, Table{rows}); 504 | } 505 | 506 | Expression parseSubstitution(CodeRange code) { 507 | auto first = code.begin(); 508 | auto name = parseName(code); 509 | code.first = end(name); 510 | code = parseWhiteSpace(code); 511 | if (startsWith(code, '@')) { 512 | code = parseCharacter(code); 513 | code = parseWhiteSpace(code); 514 | auto child = parseExpression(code); 515 | code.first = end(child); 516 | return makeLookupChild(CodeRange{first, code.first}, {name.index, child}); 517 | } 518 | if (startsWith(code, '!') || startsWith(code, '?')) { 519 | code = parseCharacter(code); 520 | auto child = parseExpression(code); 521 | code.first = end(child); 522 | return makeFunctionApplication( 523 | CodeRange{first, code.first}, {BoundGlobalName{name.index}, child} 524 | ); 525 | } 526 | if (startsWith(code, ':')) { 527 | code = parseCharacter(code); 528 | auto value = parseExpression(code); 529 | code.first = end(value); 530 | return makeTypedExpression( 531 | CodeRange{first, code.first}, {BoundGlobalName{name.index}, value} 532 | ); 533 | } 534 | return makeLookupSymbol(CodeRange{first, end(name)}, {name.index}); 535 | } 536 | 537 | Expression parseNumber(CodeRange code) { 538 | auto first = code.begin(); 539 | code = parseOptionalCharacter(code, isSign); 540 | code = parseCharacter(code, isDigit); 541 | code = parseWhile(code, isDigit); 542 | code = parseOptionalCharacter(code, '.'); 543 | code = parseWhile(code, isDigit); 544 | const auto value = std::stod(rawString({first, code.first})); 545 | return makeNumber(CodeRange{first, code.first}, {value}); 546 | } 547 | 548 | CodeRange parseKeyWordContent(CodeRange code, std::string keyword) { 549 | return {code.begin(), parseKeyword(code, keyword).begin()}; 550 | } 551 | 552 | Expression parseYes(CodeRange code) { 553 | return Expression{YES, 0, parseKeyWordContent(code, "yes")}; 554 | } 555 | 556 | Expression parseNo(CodeRange code) { 557 | return Expression{NO, 0, parseKeyWordContent(code, "no")}; 558 | } 559 | 560 | Expression parseNegInf(CodeRange code) { 561 | return makeNumber( 562 | parseKeyWordContent(code, "-inf"), 563 | {-std::numeric_limits::infinity()} 564 | ); 565 | } 566 | 567 | Expression parseDynamicExpression(CodeRange code) { 568 | auto first = code.begin(); 569 | code = parseKeyword(code, "dynamic"); 570 | auto inner_expression = parseExpression(code); 571 | code.first = end(inner_expression); 572 | return makeDynamicExpression( 573 | CodeRange{first, code.begin()}, DynamicExpression{inner_expression} 574 | ); 575 | } 576 | 577 | Expression parseString(CodeRange code) { 578 | auto first = code.begin(); 579 | code = parseCharacter(code, '"'); 580 | auto characters = std::vector{}; 581 | for (; code.first->character != '"'; ++code.first) { 582 | auto character = makeCharacter( 583 | CodeRange{code.first, code.first + 1}, 584 | code.first->character 585 | ); 586 | characters.push_back(character); 587 | } 588 | std::reverse(characters.begin(), characters.end()); 589 | auto string = Expression{EMPTY_STRING, 0, {first, first + 1}}; 590 | for (const auto& character : characters) { 591 | string = putString(string, character); 592 | } 593 | code = parseCharacter(code, '"'); 594 | const auto last = code.begin(); 595 | string.range = CodeRange{first, last}; 596 | return string; 597 | } 598 | 599 | } // namespace 600 | 601 | Expression parseExpression(CodeRange code) { 602 | try { 603 | code = parseWhiteSpace(code); 604 | throwIfEmpty(code); 605 | const auto c = code.first->character; 606 | if (c == '[') {return parseStack(code);} 607 | if (c == '{') {return parseDictionary(code);} 608 | if (c == '(') {return parseTuple(code);} 609 | if (c == '<') {return parseTable(code);} 610 | if (c == '\'') {return parseCharacterExpression(code);} 611 | if (c == '\"') {return parseString(code);} 612 | if (isKeyword(code, "yes")) {return parseYes(code);} 613 | if (isKeyword(code, "no")) {return parseNo(code);} 614 | if (isKeyword(code, "-inf")) {return parseNegInf(code);} 615 | if (isKeyword(code, "if")) {return parseConditional(code);} 616 | if (isKeyword(code, "is")) {return parseIs(code);} 617 | if (isKeyword(code, "in")) {return parseAnyFunction(code);} 618 | if (isKeyword(code, "dynamic")) {return parseDynamicExpression(code);} 619 | if (isKeyword(code, "out")) {throwParseException(code);} 620 | if (isKeyword(code, "then")) {throwParseException(code);} 621 | if (isKeyword(code, "else")) {throwParseException(code);} 622 | if (isKeyword(code, "while")) {throwParseException(code);} 623 | if (isKeyword(code, "end")) {throwParseException(code);} 624 | if (isdigit(c) || c == '+' || c == '-') {return parseNumber(code);} 625 | if (isalpha(c) || c == '_') {return parseSubstitution(code);} 626 | throwParseException(code); 627 | return {}; 628 | } catch (const std::runtime_error& e) { 629 | std::cout << "Exception while parsing: " << e.what(); 630 | throw e; 631 | } 632 | } 633 | -------------------------------------------------------------------------------- /src/passes/parse.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct CodeRange; 4 | struct Expression; 5 | 6 | Expression parseExpression(CodeRange code); 7 | -------------------------------------------------------------------------------- /src/passes/serialize.cpp: -------------------------------------------------------------------------------- 1 | #include "serialize.h" 2 | 3 | #include 4 | #include 5 | 6 | #include "../factory.h" 7 | 8 | namespace { 9 | 10 | void serializeName(std::string& s, size_t name) { 11 | s.append(storage.names.at(name)); 12 | } 13 | 14 | void serializeArgument(std::string& s, Argument a) { 15 | if (a.type.type != ANY) { 16 | serialize(s, a.type); 17 | s.append(":"); 18 | serializeName(s, a.name); 19 | return; 20 | } 21 | serializeName(s, a.name); 22 | } 23 | 24 | void serializeDynamicExpression(std::string& s, const DynamicExpression& dynamic_expression) { 25 | s.append("dynamic "); 26 | serialize(s, dynamic_expression.expression); 27 | } 28 | 29 | void serializeTypedExpression(std::string& s, const TypedExpression& typed_expression) { 30 | serializeName(s, typed_expression.type_name.global_index); 31 | s.append(":"); 32 | serialize(s, typed_expression.value); 33 | } 34 | 35 | void serializeConditional(std::string& s, const Conditional& conditional) { 36 | s.append("if "); 37 | for (auto a = conditional.alternative_first; 38 | a.index <= conditional.alternative_last.index; 39 | ++a.index 40 | ) { 41 | const auto alternative = storage.alternatives.at(a.index); 42 | serialize(s, alternative.left); 43 | s.append(" then "); 44 | serialize(s, alternative.right); 45 | s.append(" "); 46 | } 47 | s.append("else "); 48 | serialize(s, conditional.expression_else); 49 | } 50 | 51 | void serializeIs(std::string& s, const IsExpression& is_expression) { 52 | s.append("is "); 53 | serialize(s, is_expression.input); 54 | s.append(" "); 55 | for (auto a = is_expression.alternative_first; 56 | a.index <= is_expression.alternative_last.index; 57 | ++a.index 58 | ) { 59 | const auto alternative = storage.alternatives.at(a.index); 60 | serialize(s, alternative.left); 61 | s.append(" then "); 62 | serialize(s, alternative.right); 63 | s.append(" "); 64 | } 65 | s.append("else "); 66 | serialize(s, is_expression.expression_else); 67 | } 68 | 69 | void serializeDefinition(std::string& s, const Definition& element) { 70 | serializeName(s, element.name.global_index); 71 | s.append("="); 72 | serialize(s, element.expression); 73 | s.append(" "); 74 | } 75 | 76 | void serializePutAssignment(std::string& s, const PutAssignment& element) { 77 | serializeName(s, element.name.global_index); 78 | s.append("+="); 79 | serialize(s, element.expression); 80 | s.append(" "); 81 | } 82 | 83 | void serializePutEachAssignment(std::string& s, const PutEachAssignment& element) { 84 | serializeName(s, element.name.global_index); 85 | s.append("++="); 86 | serialize(s, element.expression); 87 | s.append(" "); 88 | } 89 | 90 | void serializeDropAssignment(std::string& s, const DropAssignment& element) { 91 | serializeName(s, element.name.global_index); 92 | s.append("-- "); 93 | } 94 | 95 | void serializeWhileStatement(std::string& s, const WhileStatement& element) { 96 | s.append("while "); 97 | serialize(s, element.expression); 98 | s.append(" "); 99 | } 100 | 101 | void serializeForStatement(std::string& s, const ForStatement& element) { 102 | s.append("for "); 103 | serializeName(s, element.item_name.global_index); 104 | s.append(" in "); 105 | serializeName(s, element.container_name.global_index); 106 | s.append(" "); 107 | } 108 | 109 | void serializeForSimpleStatement(std::string& s, const ForSimpleStatement& element) { 110 | s.append("for "); 111 | serializeName(s, element.container_name.global_index); 112 | s.append(" "); 113 | } 114 | 115 | template 116 | void serializeEvaluatedDictionary(std::string& s, Serializer serializer, const EvaluatedDictionary& dictionary) { 117 | if (dictionary.definitions.empty()) { 118 | s.append("{}"); 119 | return; 120 | } 121 | s.append("{"); 122 | for (const auto& pair : dictionary.definitions) { 123 | serializeName(s, pair.name.global_index); 124 | s.append("="); 125 | serializer(s, pair.expression); 126 | s.append(" "); 127 | } 128 | s.back() = '}'; 129 | } 130 | 131 | template 132 | void serializeEvaluatedTuple(std::string& s, Serializer serializer, Expression t) { 133 | const auto evaluated_tuple = storage.evaluated_tuples.at(t.index); 134 | if (evaluated_tuple.first == evaluated_tuple.last) { 135 | s.append("()"); 136 | return; 137 | } 138 | s.append("("); 139 | for (size_t i = evaluated_tuple.first; i < evaluated_tuple.last; ++i) { 140 | const auto expression = storage.expressions.at(i); 141 | serializer(s, expression); 142 | s.append(" "); 143 | } 144 | s.back() = ')'; 145 | } 146 | 147 | void serializeLookupChild(std::string& s, const LookupChild& lookup_child) { 148 | serializeName(s, lookup_child.name); 149 | s.append("@"); 150 | serialize(s, lookup_child.child); 151 | } 152 | 153 | void serializeFunctionApplication(std::string& s, const FunctionApplication& function_application) { 154 | serializeName(s, function_application.name.global_index); 155 | s.append("!"); 156 | serialize(s, function_application.child); 157 | } 158 | 159 | void serializeLookupSymbol(std::string& s, const LookupSymbol& lookup_symbol) { 160 | serializeName(s, lookup_symbol.name.global_index); 161 | } 162 | 163 | void serializeDictionary(std::string& s, const Dictionary& dictionary) { 164 | s.append("{"); 165 | for (size_t i = dictionary.statement_first; i < dictionary.statement_last; ++i) { 166 | const auto statement = storage.statements.at(i); 167 | serialize(s, statement); 168 | } 169 | // TODO: handle by early return. 170 | if (dictionary.statement_first == dictionary.statement_last) { 171 | s.append("}"); 172 | } 173 | else { 174 | s.back() = '}'; 175 | } 176 | } 177 | 178 | void serializeTuple(std::string& s, Expression t) { 179 | const auto tuple_struct = storage.tuples.at(t.index); 180 | if (tuple_struct.first == tuple_struct.last) { 181 | s.append("()"); 182 | return; 183 | } 184 | s.append("("); 185 | for (size_t i = tuple_struct.first; i < tuple_struct.last; ++i) { 186 | const auto expression = storage.expressions.at(i); 187 | serialize(s, expression); 188 | s.append(" "); 189 | } 190 | s.back() = ')'; 191 | } 192 | 193 | void serializeStack(std::string& s, Expression expression) { 194 | s.append("["); 195 | while (expression.type != EMPTY_STACK) { 196 | if (expression.type != STACK) { 197 | throw std::runtime_error( 198 | std::string{"\n\nI have found a type error."} + 199 | "\nIt happens in serializeStack. " + 200 | "\nInstead of a stack I got a " + NAMES[expression.type] + 201 | ".\n" 202 | ); 203 | } 204 | const auto stack = storage.stacks.at(expression.index); 205 | serialize(s, stack.top); 206 | s.append(" "); 207 | expression = stack.rest; 208 | } 209 | s.back() = ']'; 210 | } 211 | 212 | void serializeCharacter(std::string& s, Character character) { 213 | s += '\''; 214 | s += character; 215 | s += '\''; 216 | } 217 | 218 | void serializeFunction(std::string& s, const Function& function) { 219 | s.append("in "); 220 | serializeArgument(s, storage.arguments.at(function.argument)); 221 | s.append(" out "); 222 | serialize(s, function.body); 223 | } 224 | 225 | void serializeFunctionDictionary(std::string& s, const FunctionDictionary& function_dictionary) { 226 | s.append("in "); 227 | s.append("{"); 228 | for (auto i = function_dictionary.first_argument; i < function_dictionary.last_argument; ++i) { 229 | serializeArgument(s, storage.arguments.at(i)); 230 | s.append(" "); 231 | } 232 | if (function_dictionary.first_argument == function_dictionary.last_argument) { 233 | s.append("}"); 234 | } 235 | else { 236 | s.back() = '}'; 237 | } 238 | s.append(" out "); 239 | serialize(s, function_dictionary.body); 240 | } 241 | 242 | void serializeFunctionTuple(std::string& s, const FunctionTuple& function_stack) { 243 | s.append("in "); 244 | s.append("("); 245 | for (auto i = function_stack.first_argument; i < function_stack.last_argument; ++i) { 246 | serializeArgument(s, storage.arguments.at(i)); 247 | s.append(" "); 248 | } 249 | if (function_stack.first_argument == function_stack.last_argument) { 250 | s.append(")"); 251 | } 252 | else { 253 | s.back() = ')'; 254 | } 255 | s.append(" out "); 256 | serialize(s, function_stack.body); 257 | } 258 | 259 | void serializeTable(std::string& s, Expression t) { 260 | const auto rows = storage.tables.at(t.index).rows; 261 | if (rows.empty()) { 262 | s.append("<>"); 263 | return; 264 | } 265 | s.append("<"); 266 | for (const auto& row : rows) { 267 | s.append("("); 268 | serialize(s, row.key); 269 | s.append(" "); 270 | serialize(s, row.value); 271 | s.append(") "); 272 | } 273 | s.back() = '>'; 274 | } 275 | 276 | void serializeTypesEvaluatedTable(std::string& s, Expression t) { 277 | const auto& rows = storage.evaluated_tables.at(t.index).rows; 278 | if (rows.empty()) { 279 | s.append("<>"); 280 | return; 281 | } 282 | const auto& row = rows.begin()->second; 283 | s.append("<("); 284 | serialize_types(s, row.key); 285 | s.append(" "); 286 | serialize_types(s, row.value); 287 | s.append(")>"); 288 | } 289 | 290 | template 291 | void serializeEvaluatedTable(std::string& s, const Table& table) { 292 | if (table.empty()) { 293 | s.append("<>"); 294 | return; 295 | } 296 | s.append("<"); 297 | for (const auto& row : table) { 298 | s.append("("); 299 | s.append(row.first); 300 | s.append(" "); 301 | serialize(s, row.second.value); 302 | s.append(") "); 303 | } 304 | s.back() = '>'; 305 | } 306 | 307 | void serializeTypesEvaluatedStack(std::string& s, Expression e) { 308 | s.append("["); 309 | serialize_types(s, storage.evaluated_stacks.at(e.index).top); 310 | s.append("]"); 311 | } 312 | 313 | void serializeEvaluatedStack(std::string& s, Expression expression) { 314 | s.append("["); 315 | while (expression.type != EMPTY_STACK) { 316 | if (expression.type != EVALUATED_STACK) { 317 | throw std::runtime_error{ 318 | "I found an error while serializing a stack. " 319 | "Instead of a stack I got a " + NAMES[expression.type] 320 | }; 321 | } 322 | const auto stack = storage.evaluated_stacks.at(expression.index); 323 | serialize(s, stack.top); 324 | s.append(" "); 325 | expression = stack.rest; 326 | } 327 | s.back() = ']'; 328 | } 329 | 330 | void serializeNumber(std::string& s, Number number) { 331 | if (number != number) { 332 | s.append("nan"); 333 | return; 334 | } 335 | std::stringstream stream; 336 | stream.precision(std::numeric_limits::digits10 + 1); 337 | stream << number; 338 | s.append(stream.str()); 339 | } 340 | 341 | void serializeString(std::string& s, Expression expression) { 342 | s.append("\""); 343 | while (expression.type != EMPTY_STRING) { 344 | if (expression.type != STRING) { 345 | throw std::runtime_error{ 346 | "I found an error while serializing a string. " 347 | "Instead of a string I got a " + NAMES[expression.type] 348 | }; 349 | } 350 | const auto string = storage.strings.at(expression.index); 351 | const auto top = string.top; 352 | const auto rest = string.rest; 353 | if (top.type != CHARACTER) { 354 | throw std::runtime_error{ 355 | "I found an error while serializing a string. " 356 | "Each item in the string should be a character, " 357 | "but I found a " + NAMES[top.type] 358 | }; 359 | } 360 | s.push_back(getCharacter(top)); 361 | expression = rest; 362 | } 363 | s.append("\""); 364 | } 365 | 366 | } // namespace 367 | 368 | void serialize_types(std::string& s, Expression expression) { 369 | switch (expression.type) { 370 | case EVALUATED_DICTIONARY: serializeEvaluatedDictionary(s, serialize_types, storage.evaluated_dictionaries.at(expression.index)); return; 371 | case EVALUATED_TUPLE: serializeEvaluatedTuple(s, serialize_types, expression); return; 372 | case EVALUATED_STACK: serializeTypesEvaluatedStack(s, expression); return; 373 | case EVALUATED_TABLE: serializeTypesEvaluatedTable(s, expression); return; 374 | // TODO: EVALUATED_TABLE_VIEW? 375 | default: s.append(NAMES[expression.type]); return; 376 | } 377 | } 378 | 379 | void serialize(std::string& s, Expression expression) { 380 | switch (expression.type) { 381 | case CHARACTER: serializeCharacter(s, getCharacter(expression)); return; 382 | case CONDITIONAL: serializeConditional(s, storage.conditionals.at(expression.index)); return; 383 | case IS: serializeIs(s, storage.is_expressions.at(expression.index)); return; 384 | case DICTIONARY: serializeDictionary(s, storage.dictionaries.at(expression.index)); return; 385 | case EVALUATED_DICTIONARY: serializeEvaluatedDictionary(s, serialize, storage.evaluated_dictionaries.at(expression.index)); return; 386 | case DEFINITION: serializeDefinition(s, storage.definitions.at(expression.index)); return; 387 | case PUT_ASSIGNMENT: serializePutAssignment(s, storage.put_assignments.at(expression.index)); return; 388 | case PUT_EACH_ASSIGNMENT: serializePutEachAssignment(s, storage.put_each_assignments.at(expression.index)); return; 389 | case DROP_ASSIGNMENT: serializeDropAssignment(s, storage.drop_assignments.at(expression.index)); return; 390 | case WHILE_STATEMENT: serializeWhileStatement(s, storage.while_statements.at(expression.index)); return; 391 | case FOR_STATEMENT: serializeForStatement(s, storage.for_statements.at(expression.index)); return; 392 | case FOR_SIMPLE_STATEMENT: serializeForSimpleStatement(s, storage.for_simple_statements.at(expression.index)); return; 393 | case FUNCTION: serializeFunction(s, storage.functions.at(expression.index)); return; 394 | case FUNCTION_DICTIONARY: serializeFunctionDictionary(s, storage.dictionary_functions.at(expression.index)); return; 395 | case FUNCTION_TUPLE: serializeFunctionTuple(s, storage.tuple_functions.at(expression.index)); return; 396 | case TABLE: serializeTable(s, expression); return; 397 | case EVALUATED_TABLE: serializeEvaluatedTable(s, storage.evaluated_tables.at(expression.index).rows); return; 398 | case EVALUATED_TABLE_VIEW: serializeEvaluatedTable(s, storage.evaluated_table_views.at(expression.index)); return; 399 | case TUPLE: serializeTuple(s, expression); return; 400 | case EVALUATED_TUPLE: serializeEvaluatedTuple(s, serialize, expression); return; 401 | case STACK: serializeStack(s, expression); return; 402 | case EVALUATED_STACK: serializeEvaluatedStack(s, expression); return; 403 | case LOOKUP_CHILD: serializeLookupChild(s, storage.child_lookups.at(expression.index)); return; 404 | case FUNCTION_APPLICATION: serializeFunctionApplication(s, storage.function_applications.at(expression.index)); return; 405 | case LOOKUP_SYMBOL: serializeLookupSymbol(s, storage.symbol_lookups.at(expression.index)); return; 406 | case NUMBER: serializeNumber(s, getNumber(expression)); return; 407 | case EMPTY_STRING: serializeString(s, expression); return; 408 | case STRING: serializeString(s, expression); return; 409 | case DYNAMIC_EXPRESSION: serializeDynamicExpression(s, storage.dynamic_expressions.at(expression.index)); return; 410 | case TYPED_EXPRESSION: serializeTypedExpression(s, storage.typed_expressions.at(expression.index)); return; 411 | case EMPTY_STACK: s.append("[]"); return; 412 | case YES: s.append("yes"); return; 413 | case NO: s.append("no"); return; 414 | case WHILE_END_STATEMENT: s.append("end "); return; 415 | case FOR_END_STATEMENT: s.append("end "); return; 416 | case FOR_SIMPLE_END_STATEMENT: s.append("end "); return; 417 | case RETURN_STATEMENT: s.append("return "); return; 418 | default: s.append(NAMES[expression.type]); return; 419 | } 420 | } 421 | -------------------------------------------------------------------------------- /src/passes/serialize.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | struct Expression; 6 | 7 | void serialize_types(std::string& s, Expression expression); 8 | void serialize(std::string& s, Expression expression); 9 | --------------------------------------------------------------------------------