authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2015-11-23 22:47:25-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2015-11-23 22:47:25-07:00
log3b4a2afb65d4ebb5292c230bada84408758e564c
treea07801709a0b1531f742e16e83e77d73ce873de7
parenta22bc8d20a1ad9158a28f29f6f866b0008b11e3e

semantic analysis checks for multiple definitions of functions


8 files changed, 389 insertions(+), 22 deletions(-)

CMakeLists.txt+1
...@@ -29,6 +29,7 @@ set(ZIG_SOURCES...@@ -29,6 +29,7 @@ set(ZIG_SOURCES
29 "${CMAKE_SOURCE_DIR}/src/parser.cpp"29 "${CMAKE_SOURCE_DIR}/src/parser.cpp"
30 "${CMAKE_SOURCE_DIR}/src/tokenizer.cpp"30 "${CMAKE_SOURCE_DIR}/src/tokenizer.cpp"
31 "${CMAKE_SOURCE_DIR}/src/util.cpp"31 "${CMAKE_SOURCE_DIR}/src/util.cpp"
32 "${CMAKE_SOURCE_DIR}/src/codegen.cpp"
32)33)
3334
34set(CONFIGURE_OUT_FILE "${CMAKE_BINARY_DIR}/config.h")35set(CONFIGURE_OUT_FILE "${CMAKE_BINARY_DIR}/config.h")
src/buffer.hpp+9
...@@ -149,5 +149,14 @@ static inline Buf *buf_dirname(Buf *buf) {...@@ -149,5 +149,14 @@ static inline Buf *buf_dirname(Buf *buf) {
149 return buf_create_from_mem("", 0);149 return buf_create_from_mem("", 0);
150}150}
151151
152static inline uint32_t buf_hash(Buf *buf) {
153 // FNV 32-bit hash
154 uint32_t h = 2166136261;
155 for (int i = 0; i < buf_len(buf); i += 1) {
156 h = h ^ ((uint8_t)buf->list.at(i));
157 h = h * 16777619;
158 }
159 return h;
160}
152161
153#endif162#endif
src/codegen.cpp created+87
...@@ -0,0 +1,87 @@
1#include "codegen.hpp"
2#include "hash_map.hpp"
3
4#include <stdio.h>
5
6struct CodeGen {
7 AstNode *root;
8 HashMap<Buf *, AstNode *, buf_hash, buf_eql_buf> fn_decls;
9 ZigList<ErrorMsg> errors;
10};
11
12CodeGen *create_codegen(AstNode *root) {
13 CodeGen *g = allocate<CodeGen>(1);
14 g->root = root;
15 g->fn_decls.init(32);
16 return g;
17}
18
19static void add_node_error(CodeGen *g, AstNode *node, Buf *msg) {
20 g->errors.add_one();
21 ErrorMsg *last_msg = &g->errors.last();
22 last_msg->line_start = node->line;
23 last_msg->column_start = node->column;
24 last_msg->line_end = -1;
25 last_msg->column_end = -1;
26 last_msg->msg = msg;
27}
28
29static void analyze_node(CodeGen *g, AstNode *node) {
30 switch (node->type) {
31 case NodeTypeRoot:
32 for (int i = 0; i < node->data.root.fn_decls.length; i += 1) {
33 AstNode *child = node->data.root.fn_decls.at(i);
34 analyze_node(g, child);
35 }
36 break;
37 case NodeTypeFnDecl:
38 {
39 auto entry = g->fn_decls.maybe_get(&node->data.fn_decl.name);
40 if (entry) {
41 add_node_error(g, node,
42 buf_sprintf("redefinition of '%s'", buf_ptr(&node->data.fn_decl.name)));
43 } else {
44 g->fn_decls.put(&node->data.fn_decl.name, node);
45 for (int i = 0; i < node->data.fn_decl.params.length; i += 1) {
46 AstNode *child = node->data.fn_decl.params.at(i);
47 analyze_node(g, child);
48 }
49 analyze_node(g, node->data.fn_decl.return_type);
50 analyze_node(g, node->data.fn_decl.body);
51 }
52 break;
53 }
54 case NodeTypeParamDecl:
55 analyze_node(g, node->data.param_decl.type);
56 break;
57 case NodeTypeType:
58 break;
59 case NodeTypePointerType:
60 break;
61 case NodeTypeBlock:
62 break;
63 case NodeTypeStatement:
64 break;
65 case NodeTypeExpressionStatement:
66 break;
67 case NodeTypeReturnStatement:
68 break;
69 case NodeTypeExpression:
70 break;
71 case NodeTypeFnCall:
72 break;
73 }
74}
75
76void semantic_analyze(CodeGen *g) {
77 // Pass 1.
78 analyze_node(g, g->root);
79}
80
81void code_gen(CodeGen *g) {
82
83}
84
85ZigList<ErrorMsg> *codegen_error_messages(CodeGen *g) {
86 return &g->errors;
87}
src/codegen.hpp created+25
...@@ -0,0 +1,25 @@
1#ifndef ZIG_CODEGEN_HPP
2#define ZIG_CODEGEN_HPP
3
4#include "parser.hpp"
5
6struct CodeGen;
7
8struct ErrorMsg {
9 int line_start;
10 int column_start;
11 int line_end;
12 int column_end;
13 Buf *msg;
14};
15
16
17CodeGen *create_codegen(AstNode *root);
18
19void semantic_analyze(CodeGen *g);
20
21void code_gen(CodeGen *g);
22
23ZigList<ErrorMsg> *codegen_error_messages(CodeGen *g);
24
25#endif
src/hash_map.hpp created+220
...@@ -0,0 +1,220 @@
1#ifndef ZIG_HASH_MAP_HPP
2#define ZIG_HASH_MAP_HPP
3
4#include "util.hpp"
5
6#include <stdint.h>
7
8template<typename K, typename V, uint32_t (*HashFunction)(K key), bool (*EqualFn)(K a, K b)>
9class HashMap {
10public:
11 void init(int capacity) {
12 init_capacity(capacity);
13 }
14 void deinit(void) {
15 free(_entries);
16 }
17
18 struct Entry {
19 bool used;
20 int distance_from_start_index;
21 K key;
22 V value;
23 };
24
25 void clear() {
26 for (int i = 0; i < _capacity; i += 1) {
27 _entries[i].used = false;
28 }
29 _size = 0;
30 _max_distance_from_start_index = 0;
31 _modification_count += 1;
32 }
33
34 int size() const {
35 return _size;
36 }
37
38 void put(const K &key, const V &value) {
39 _modification_count += 1;
40 internal_put(key, value);
41
42 // if we get too full (80%), double the capacity
43 if (_size * 5 >= _capacity * 4) {
44 Entry *old_entries = _entries;
45 int old_capacity = _capacity;
46 init_capacity(_capacity * 2);
47 // dump all of the old elements into the new table
48 for (int i = 0; i < old_capacity; i += 1) {
49 Entry *old_entry = &old_entries[i];
50 if (old_entry->used)
51 internal_put(old_entry->key, old_entry->value);
52 }
53 free(old_entries);
54 }
55 }
56
57 const V &get(const K &key) const {
58 Entry *entry = internal_get(key);
59 if (!entry)
60 zig_panic("key not found");
61 return entry->value;
62 }
63
64 Entry *maybe_get(const K &key) const {
65 return internal_get(key);
66 }
67
68 void remove(const K &key) {
69 _modification_count += 1;
70 int start_index = key_to_index(key);
71 for (int roll_over = 0; roll_over <= _max_distance_from_start_index; roll_over += 1) {
72 int index = (start_index + roll_over) % _capacity;
73 Entry *entry = &_entries[index];
74
75 if (!entry->used)
76 zig_panic("key not found");
77
78 if (!EqualFn(entry->key, key))
79 continue;
80
81 for (; roll_over < _capacity; roll_over += 1) {
82 int next_index = (start_index + roll_over + 1) % _capacity;
83 Entry *next_entry = &_entries[next_index];
84 if (!next_entry->used || next_entry->distance_from_start_index == 0) {
85 entry->used = false;
86 _size -= 1;
87 return;
88 }
89 *entry = *next_entry;
90 entry->distance_from_start_index -= 1;
91 entry = next_entry;
92 }
93 zig_panic("shifting everything in the table");
94 }
95 zig_panic("key not found");
96 }
97
98 class Iterator {
99 public:
100 Entry *next() {
101 if (_inital_modification_count != _table->_modification_count)
102 zig_panic("concurrent modification");
103 if (_count >= _table->size())
104 return NULL;
105 for (; _index < _table->_capacity; _index += 1) {
106 Entry *entry = &_table->_entries[_index];
107 if (entry->used) {
108 _index += 1;
109 _count += 1;
110 return entry;
111 }
112 }
113 zig_panic("no next item");
114 }
115 private:
116 const HashMap * _table;
117 // how many items have we returned
118 int _count = 0;
119 // iterator through the entry array
120 int _index = 0;
121 // used to detect concurrent modification
122 uint32_t _inital_modification_count;
123 Iterator(const HashMap * table) :
124 _table(table), _inital_modification_count(table->_modification_count) {
125 }
126 friend HashMap;
127 };
128
129 // you must not modify the underlying HashMap while this iterator is still in use
130 Iterator entry_iterator() const {
131 return Iterator(this);
132 }
133
134private:
135
136 Entry *_entries;
137 int _capacity;
138 int _size;
139 int _max_distance_from_start_index;
140 // this is used to detect bugs where a hashtable is edited while an iterator is running.
141 uint32_t _modification_count = 0;
142
143 void init_capacity(int capacity) {
144 _capacity = capacity;
145 _entries = allocate<Entry>(_capacity);
146 _size = 0;
147 _max_distance_from_start_index = 0;
148 for (int i = 0; i < _capacity; i += 1) {
149 _entries[i].used = false;
150 }
151 }
152
153 void internal_put(K key, V value) {
154 int start_index = key_to_index(key);
155 for (int roll_over = 0, distance_from_start_index = 0;
156 roll_over < _capacity; roll_over += 1, distance_from_start_index += 1)
157 {
158 int index = (start_index + roll_over) % _capacity;
159 Entry *entry = &_entries[index];
160
161 if (entry->used && !EqualFn(entry->key, key)) {
162 if (entry->distance_from_start_index < distance_from_start_index) {
163 // robin hood to the rescue
164 Entry tmp = *entry;
165 if (distance_from_start_index > _max_distance_from_start_index)
166 _max_distance_from_start_index = distance_from_start_index;
167 *entry = {
168 true,
169 distance_from_start_index,
170 key,
171 value,
172 };
173 key = tmp.key;
174 value = tmp.value;
175 distance_from_start_index = tmp.distance_from_start_index;
176 }
177 continue;
178 }
179
180 if (!entry->used) {
181 // adding an entry. otherwise overwriting old value with
182 // same key
183 _size += 1;
184 }
185
186 if (distance_from_start_index > _max_distance_from_start_index)
187 _max_distance_from_start_index = distance_from_start_index;
188 *entry = {
189 true,
190 distance_from_start_index,
191 key,
192 value,
193 };
194 return;
195 }
196 zig_panic("put into a full HashMap");
197 }
198
199
200 Entry *internal_get(const K &key) const {
201 int start_index = key_to_index(key);
202 for (int roll_over = 0; roll_over <= _max_distance_from_start_index; roll_over += 1) {
203 int index = (start_index + roll_over) % _capacity;
204 Entry *entry = &_entries[index];
205
206 if (!entry->used)
207 return NULL;
208
209 if (EqualFn(entry->key, key))
210 return entry;
211 }
212 return NULL;
213 }
214
215 int key_to_index(const K &key) const {
216 return (int)(HashFunction(key) % ((uint32_t)_capacity));
217 }
218};
219
220#endif
src/main.cpp+24-6
...@@ -12,6 +12,7 @@...@@ -12,6 +12,7 @@
12#include "parser.hpp"12#include "parser.hpp"
13#include "tokenizer.hpp"13#include "tokenizer.hpp"
14#include "error.hpp"14#include "error.hpp"
15#include "codegen.hpp"
1516
16#include <stdio.h>17#include <stdio.h>
17#include <string.h>18#include <string.h>
...@@ -75,24 +76,41 @@ static int build(const char *arg0, const char *in_file, const char *out_file, Zi...@@ -75,24 +76,41 @@ static int build(const char *arg0, const char *in_file, const char *out_file, Zi
75 cur_dir_path = buf_dirname(buf_create_from_str(in_file));76 cur_dir_path = buf_dirname(buf_create_from_str(in_file));
76 }77 }
7778
78 Buf *in_data = fetch_file(in_f);
79
80 fprintf(stderr, "Original source:\n");79 fprintf(stderr, "Original source:\n");
81 fprintf(stderr, "----------------\n");80 fprintf(stderr, "----------------\n");
81 Buf *in_data = fetch_file(in_f);
82 fprintf(stderr, "%s\n", buf_ptr(in_data));82 fprintf(stderr, "%s\n", buf_ptr(in_data));
8383
84 ZigList<Token> *tokens = tokenize(in_data, cur_dir_path);
85
86 fprintf(stderr, "\nTokens:\n");84 fprintf(stderr, "\nTokens:\n");
87 fprintf(stderr, "---------\n");85 fprintf(stderr, "---------\n");
86 ZigList<Token> *tokens = tokenize(in_data, cur_dir_path);
88 print_tokens(in_data, tokens);87 print_tokens(in_data, tokens);
8988
90 AstNode *root = ast_parse(in_data, tokens);
91 assert(root);
92 fprintf(stderr, "\nAST:\n");89 fprintf(stderr, "\nAST:\n");
93 fprintf(stderr, "------\n");90 fprintf(stderr, "------\n");
91 AstNode *root = ast_parse(in_data, tokens);
92 assert(root);
94 ast_print(root, 0);93 ast_print(root, 0);
9594
95 fprintf(stderr, "\nSemantic Analysis:\n");
96 fprintf(stderr, "--------------------\n");
97 CodeGen *codegen = create_codegen(root);
98 semantic_analyze(codegen);
99 ZigList<ErrorMsg> *errors = codegen_error_messages(codegen);
100 if (errors->length == 0) {
101 fprintf(stderr, "OK\n");
102 } else {
103 for (int i = 0; i < errors->length; i += 1) {
104 ErrorMsg *err = &errors->at(i);
105 fprintf(stderr, "Error: Line %d, column %d: %s\n", err->line_start, err->column_start,
106 buf_ptr(err->msg));
107 }
108 return 1;
109 }
110
111 fprintf(stderr, "\nCode Generation:\n");
112 fprintf(stderr, "------------------\n");
113 code_gen(codegen);
96114
97 return 0;115 return 0;
98}116}
src/parser.cpp+21-16
...@@ -133,9 +133,11 @@ struct ParseContext {...@@ -133,9 +133,11 @@ struct ParseContext {
133 ZigList<Token> *tokens;133 ZigList<Token> *tokens;
134};134};
135135
136static AstNode *ast_create_node(NodeType type) {136static AstNode *ast_create_node(NodeType type, Token *first_token) {
137 AstNode *node = allocate<AstNode>(1);137 AstNode *node = allocate<AstNode>(1);
138 node->type = type;138 node->type = type;
139 node->line = first_token->start_line;
140 node->column = first_token->start_column;
139 return node;141 return node;
140}142}
141143
...@@ -163,11 +165,11 @@ Type : token(Symbol) | PointerType;...@@ -163,11 +165,11 @@ Type : token(Symbol) | PointerType;
163PointerType : token(Star) token(Const) Type | token(Star) token(Mut) Type;165PointerType : token(Star) token(Const) Type | token(Star) token(Mut) Type;
164*/166*/
165static AstNode *ast_parse_type(ParseContext *pc, int token_index, int *new_token_index) {167static AstNode *ast_parse_type(ParseContext *pc, int token_index, int *new_token_index) {
166 AstNode *node = ast_create_node(NodeTypeType);
167
168 Token *token = &pc->tokens->at(token_index);168 Token *token = &pc->tokens->at(token_index);
169 token_index += 1;169 token_index += 1;
170170
171 AstNode *node = ast_create_node(NodeTypeType, token);
172
171 if (token->id == TokenIdSymbol) {173 if (token->id == TokenIdSymbol) {
172 node->data.type.type = AstNodeTypeTypePrimitive;174 node->data.type.type = AstNodeTypeTypePrimitive;
173 ast_buf_from_token(pc, token, &node->data.type.primitive_name);175 ast_buf_from_token(pc, token, &node->data.type.primitive_name);
...@@ -197,12 +199,13 @@ ParamDecl<node> : token(Symbol) token(Colon) Type {...@@ -197,12 +199,13 @@ ParamDecl<node> : token(Symbol) token(Colon) Type {
197};199};
198*/200*/
199static AstNode *ast_parse_param_decl(ParseContext *pc, int token_index, int *new_token_index) {201static AstNode *ast_parse_param_decl(ParseContext *pc, int token_index, int *new_token_index) {
200 AstNode *node = ast_create_node(NodeTypeParamDecl);
201
202 Token *param_name = &pc->tokens->at(token_index);202 Token *param_name = &pc->tokens->at(token_index);
203 token_index += 1;203 token_index += 1;
204 ast_expect_token(pc, param_name, TokenIdSymbol);204 ast_expect_token(pc, param_name, TokenIdSymbol);
205205
206 AstNode *node = ast_create_node(NodeTypeParamDecl, param_name);
207
208
206 ast_buf_from_token(pc, param_name, &node->data.param_decl.name);209 ast_buf_from_token(pc, param_name, &node->data.param_decl.name);
207210
208 Token *colon = &pc->tokens->at(token_index);211 Token *colon = &pc->tokens->at(token_index);
...@@ -280,12 +283,13 @@ static void ast_parse_fn_call_param_list(ParseContext *pc, int token_index, int...@@ -280,12 +283,13 @@ static void ast_parse_fn_call_param_list(ParseContext *pc, int token_index, int
280FnCall : token(Symbol) token(LParen) list(Expression, token(Comma)) token(RParen) ;283FnCall : token(Symbol) token(LParen) list(Expression, token(Comma)) token(RParen) ;
281*/284*/
282static AstNode *ast_parse_fn_call(ParseContext *pc, int token_index, int *new_token_index) {285static AstNode *ast_parse_fn_call(ParseContext *pc, int token_index, int *new_token_index) {
283 AstNode *node = ast_create_node(NodeTypeFnCall);
284
285 Token *fn_name = &pc->tokens->at(token_index);286 Token *fn_name = &pc->tokens->at(token_index);
286 token_index += 1;287 token_index += 1;
287 ast_expect_token(pc, fn_name, TokenIdSymbol);288 ast_expect_token(pc, fn_name, TokenIdSymbol);
288289
290 AstNode *node = ast_create_node(NodeTypeFnCall, fn_name);
291
292
289 ast_buf_from_token(pc, fn_name, &node->data.fn_call.name);293 ast_buf_from_token(pc, fn_name, &node->data.fn_call.name);
290294
291 ast_parse_fn_call_param_list(pc, token_index, &token_index, &node->data.fn_call.params);295 ast_parse_fn_call_param_list(pc, token_index, &token_index, &node->data.fn_call.params);
...@@ -295,9 +299,8 @@ static AstNode *ast_parse_fn_call(ParseContext *pc, int token_index, int *new_to...@@ -295,9 +299,8 @@ static AstNode *ast_parse_fn_call(ParseContext *pc, int token_index, int *new_to
295}299}
296300
297static AstNode *ast_parse_expression(ParseContext *pc, int token_index, int *new_token_index) {301static AstNode *ast_parse_expression(ParseContext *pc, int token_index, int *new_token_index) {
298 AstNode *node = ast_create_node(NodeTypeExpression);
299
300 Token *token = &pc->tokens->at(token_index);302 Token *token = &pc->tokens->at(token_index);
303 AstNode *node = ast_create_node(NodeTypeExpression, token);
301 if (token->id == TokenIdSymbol) {304 if (token->id == TokenIdSymbol) {
302 node->data.expression.type = AstNodeExpressionTypeFnCall;305 node->data.expression.type = AstNodeExpressionTypeFnCall;
303 node->data.expression.data.fn_call = ast_parse_fn_call(pc, token_index, &token_index);306 node->data.expression.data.fn_call = ast_parse_fn_call(pc, token_index, &token_index);
...@@ -329,9 +332,9 @@ Expression : token(Number) | token(String) | FnCall ;...@@ -329,9 +332,9 @@ Expression : token(Number) | token(String) | FnCall ;
329FnCall : token(Symbol) token(LParen) list(Expression, token(Comma)) token(RParen) ;332FnCall : token(Symbol) token(LParen) list(Expression, token(Comma)) token(RParen) ;
330*/333*/
331static AstNode *ast_parse_statement(ParseContext *pc, int token_index, int *new_token_index) {334static AstNode *ast_parse_statement(ParseContext *pc, int token_index, int *new_token_index) {
332 AstNode *node = ast_create_node(NodeTypeStatement);
333
334 Token *token = &pc->tokens->at(token_index);335 Token *token = &pc->tokens->at(token_index);
336 AstNode *node = ast_create_node(NodeTypeStatement, token);
337
335 if (token->id == TokenIdKeywordReturn) {338 if (token->id == TokenIdKeywordReturn) {
336 token_index += 1;339 token_index += 1;
337 node->data.statement.type = AstNodeStatementTypeReturn;340 node->data.statement.type = AstNodeStatementTypeReturn;
...@@ -362,12 +365,13 @@ static AstNode *ast_parse_statement(ParseContext *pc, int token_index, int *new_...@@ -362,12 +365,13 @@ static AstNode *ast_parse_statement(ParseContext *pc, int token_index, int *new_
362Block : token(LBrace) many(Statement) token(RBrace);365Block : token(LBrace) many(Statement) token(RBrace);
363*/366*/
364static AstNode *ast_parse_block(ParseContext *pc, int token_index, int *new_token_index) {367static AstNode *ast_parse_block(ParseContext *pc, int token_index, int *new_token_index) {
365 AstNode *node = ast_create_node(NodeTypeBlock);
366
367 Token *l_brace = &pc->tokens->at(token_index);368 Token *l_brace = &pc->tokens->at(token_index);
368 token_index += 1;369 token_index += 1;
369 ast_expect_token(pc, l_brace, TokenIdLBrace);370 ast_expect_token(pc, l_brace, TokenIdLBrace);
370371
372 AstNode *node = ast_create_node(NodeTypeBlock, l_brace);
373
374
371 for (;;) {375 for (;;) {
372 Token *token = &pc->tokens->at(token_index);376 Token *token = &pc->tokens->at(token_index);
373 if (token->id == TokenIdRBrace) {377 if (token->id == TokenIdRBrace) {
...@@ -386,12 +390,13 @@ static AstNode *ast_parse_block(ParseContext *pc, int token_index, int *new_toke...@@ -386,12 +390,13 @@ static AstNode *ast_parse_block(ParseContext *pc, int token_index, int *new_toke
386FnDecl : token(Fn) token(Symbol) ParamDeclList option(token(Arrow) Type) Block;390FnDecl : token(Fn) token(Symbol) ParamDeclList option(token(Arrow) Type) Block;
387*/391*/
388static AstNode *ast_parse_fn_decl(ParseContext *pc, int token_index, int *new_token_index) {392static AstNode *ast_parse_fn_decl(ParseContext *pc, int token_index, int *new_token_index) {
389 AstNode *node = ast_create_node(NodeTypeFnDecl);
390
391 Token *fn_token = &pc->tokens->at(token_index);393 Token *fn_token = &pc->tokens->at(token_index);
392 token_index += 1;394 token_index += 1;
393 ast_expect_token(pc, fn_token, TokenIdKeywordFn);395 ast_expect_token(pc, fn_token, TokenIdKeywordFn);
394396
397 AstNode *node = ast_create_node(NodeTypeFnDecl, fn_token);
398
399
395 Token *fn_name = &pc->tokens->at(token_index);400 Token *fn_name = &pc->tokens->at(token_index);
396 token_index += 1;401 token_index += 1;
397 ast_expect_token(pc, fn_name, TokenIdSymbol);402 ast_expect_token(pc, fn_name, TokenIdSymbol);
...@@ -437,7 +442,7 @@ static void ast_parse_fn_decl_list(ParseContext *pc, int token_index, ZigList<As...@@ -437,7 +442,7 @@ static void ast_parse_fn_decl_list(ParseContext *pc, int token_index, ZigList<As
437AstNode *ast_parse(Buf *buf, ZigList<Token> *tokens) {442AstNode *ast_parse(Buf *buf, ZigList<Token> *tokens) {
438 ParseContext pc = {0};443 ParseContext pc = {0};
439 pc.buf = buf;444 pc.buf = buf;
440 pc.root = ast_create_node(NodeTypeRoot);445 pc.root = ast_create_node(NodeTypeRoot, &tokens->at(0));
441 pc.tokens = tokens;446 pc.tokens = tokens;
442447
443 int new_token_index;448 int new_token_index;
src/parser.hpp+2
...@@ -97,6 +97,8 @@ struct AstNodeFnCall {...@@ -97,6 +97,8 @@ struct AstNodeFnCall {
97struct AstNode {97struct AstNode {
98 enum NodeType type;98 enum NodeType type;
99 AstNode *parent;99 AstNode *parent;
100 int line;
101 int column;
100 union {102 union {
101 AstNodeRoot root;103 AstNodeRoot root;
102 AstNodeFnDecl fn_decl;104 AstNodeFnDecl fn_decl;