mirror of
				https://github.com/ton-blockchain/ton
				synced 2025-03-09 15:40:10 +00:00 
			
		
		
		
	[Tolk] Implement AST: intermediate representation of tolk files
Now, the whole .tolk file can be loaded as AST tree and then converted to Expr/Op. This gives a great ability to implement AST transformations. In the future, more and more code analysis will be moved out of legacy to AST-level.
This commit is contained in:
		
							parent
							
								
									6c30e5a7eb
								
							
						
					
					
						commit
						80001d1756
					
				
					 23 changed files with 3798 additions and 2233 deletions
				
			
		|  | @ -5,8 +5,10 @@ set(TOLK_SOURCE | |||
|         lexer.cpp | ||||
|         symtable.cpp | ||||
|         compiler-state.cpp | ||||
|         ast.cpp | ||||
|         ast-from-tokens.cpp | ||||
|         ast-to-legacy.cpp | ||||
|         unify-types.cpp | ||||
|         parse-tolk.cpp | ||||
|         abscode.cpp | ||||
|         gen-abscode.cpp | ||||
|         analyzer.cpp | ||||
|  |  | |||
|  | @ -25,8 +25,8 @@ namespace tolk { | |||
|  *  | ||||
|  */ | ||||
| 
 | ||||
| TmpVar::TmpVar(var_idx_t _idx, int _cls, TypeExpr* _type, SymDef* sym, SrcLocation loc) | ||||
|     : v_type(_type), idx(_idx), cls(_cls), coord(0), where(loc) { | ||||
| TmpVar::TmpVar(var_idx_t _idx, bool _is_tmp_unnamed, TypeExpr* _type, SymDef* sym, SrcLocation loc) | ||||
|     : v_type(_type), idx(_idx), is_tmp_unnamed(_is_tmp_unnamed), coord(0), where(loc) { | ||||
|   if (sym) { | ||||
|     name = sym->sym_idx; | ||||
|     sym->value->idx = _idx; | ||||
|  | @ -59,9 +59,9 @@ void TmpVar::dump(std::ostream& os) const { | |||
| } | ||||
| 
 | ||||
| void TmpVar::show(std::ostream& os, int omit_idx) const { | ||||
|   if (cls & _Named) { | ||||
|   if (!is_tmp_unnamed) { | ||||
|     os << G.symbols.get_name(name); | ||||
|     if (omit_idx && (omit_idx >= 2 || (cls & _UniqueName))) { | ||||
|     if (omit_idx >= 2) { | ||||
|       return; | ||||
|     } | ||||
|   } | ||||
|  | @ -474,8 +474,8 @@ void CodeBlob::print(std::ostream& os, int flags) const { | |||
|   os << "-------- END ---------\n\n"; | ||||
| } | ||||
| 
 | ||||
| var_idx_t CodeBlob::create_var(int cls, TypeExpr* var_type, SymDef* sym, SrcLocation location) { | ||||
|   vars.emplace_back(var_cnt, cls, var_type, sym, location); | ||||
| var_idx_t CodeBlob::create_var(bool is_tmp_unnamed, TypeExpr* var_type, SymDef* sym, SrcLocation location) { | ||||
|   vars.emplace_back(var_cnt, is_tmp_unnamed, var_type, sym, location); | ||||
|   if (sym) { | ||||
|     sym->value->idx = var_cnt; | ||||
|   } | ||||
|  | @ -492,7 +492,7 @@ bool CodeBlob::import_params(FormalArgList arg_list) { | |||
|     SymDef* arg_sym; | ||||
|     SrcLocation arg_loc; | ||||
|     std::tie(arg_type, arg_sym, arg_loc) = par; | ||||
|     list.push_back(create_var(arg_sym ? (TmpVar::_In | TmpVar::_Named) : TmpVar::_In, arg_type, arg_sym, arg_loc)); | ||||
|     list.push_back(create_var(arg_sym == nullptr, arg_type, arg_sym, arg_loc)); | ||||
|   } | ||||
|   emplace_back(loc, Op::_Import, list); | ||||
|   in_var_cnt = var_cnt; | ||||
|  |  | |||
|  | @ -46,7 +46,7 @@ int CodeBlob::split_vars(bool strict) { | |||
|     if (k != 1) { | ||||
|       var.coord = ~((n << 8) + k); | ||||
|       for (int i = 0; i < k; i++) { | ||||
|         auto v = create_var(vars[j].cls, comp_types[i], 0, vars[j].where); | ||||
|         auto v = create_var(vars[j].is_tmp_unnamed, comp_types[i], 0, vars[j].where); | ||||
|         tolk_assert(v == n + i); | ||||
|         tolk_assert(vars[v].idx == v); | ||||
|         vars[v].name = vars[j].name; | ||||
|  |  | |||
							
								
								
									
										877
									
								
								tolk/ast-from-tokens.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										877
									
								
								tolk/ast-from-tokens.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,877 @@ | |||
| /*
 | ||||
|     This file is part of TON Blockchain Library. | ||||
| 
 | ||||
|     TON Blockchain Library is free software: you can redistribute it and/or modify | ||||
|     it under the terms of the GNU Lesser General Public License as published by | ||||
|     the Free Software Foundation, either version 2 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     TON Blockchain Library is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU Lesser General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU Lesser General Public License | ||||
|     along with TON Blockchain Library.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
| */ | ||||
| #include "ast-from-tokens.h" | ||||
| #include "ast.h" | ||||
| #include "platform-utils.h" | ||||
| #include "type-expr.h" | ||||
| 
 | ||||
| /*
 | ||||
|  *   Here we construct AST for a tolk file. | ||||
|  *   While constructing, no global state is modified. | ||||
|  *   Historically, in FunC, there was no AST: while lexing, symbols were registered, types were inferred, and so on. | ||||
|  * There was no way to perform any more or less semantic analysis. | ||||
|  *   Implementing AST gives a giant advance for future modifications and stability. | ||||
|  */ | ||||
| 
 | ||||
| namespace tolk { | ||||
| 
 | ||||
| // given a token, determine whether it's <, or >, or similar
 | ||||
| static bool is_comparison_binary_op(TokenType tok) { | ||||
|   return tok == tok_lt || tok == tok_gt || tok == tok_leq || tok == tok_geq || tok == tok_eq || tok == tok_neq || tok == tok_spaceship; | ||||
| } | ||||
| 
 | ||||
| // same as above, but to detect bitwise operators: & | ^
 | ||||
| // (in Tolk, they are used as logical ones due to absence of a boolean type and && || operators)
 | ||||
| static bool is_bitwise_binary_op(TokenType tok) { | ||||
|   return tok == tok_bitwise_and || tok == tok_bitwise_or || tok == tok_bitwise_xor; | ||||
| } | ||||
| 
 | ||||
| // same as above, but to detect addition/subtraction
 | ||||
| static bool is_add_or_sub_binary_op(TokenType tok) { | ||||
|   return tok == tok_plus || tok == tok_minus; | ||||
| } | ||||
| 
 | ||||
| // fire an error for a case "flags & 0xFF != 0" (equivalent to "flags & 1", probably unexpected)
 | ||||
| // it would better be a warning, but we decided to make it a strict error
 | ||||
| GNU_ATTRIBUTE_NORETURN GNU_ATTRIBUTE_COLD | ||||
| static void fire_error_lower_precedence(SrcLocation loc, std::string_view op_lower, std::string_view op_higher) { | ||||
|   std::string name_lower = static_cast<std::string>(op_lower); | ||||
|   std::string name_higher = static_cast<std::string>(op_higher); | ||||
|   throw ParseError(loc, name_lower + " has lower precedence than " + name_higher + | ||||
|                                  ", probably this code won't work as you expected.  " | ||||
|                                  "Use parenthesis: either (... " + name_lower + " ...) to evaluate it first, or (... " + name_higher + " ...) to suppress this error."); | ||||
| } | ||||
| 
 | ||||
| // fire an error for a case "arg1 & arg2 | arg3"
 | ||||
| GNU_ATTRIBUTE_NORETURN GNU_ATTRIBUTE_COLD | ||||
| static void fire_error_mix_bitwise_and_or(SrcLocation loc, std::string_view op1, std::string_view op2) { | ||||
|   std::string name1 = static_cast<std::string>(op1); | ||||
|   std::string name2 = static_cast<std::string>(op2); | ||||
|   throw ParseError(loc, "mixing " + name1 + " with " + name2 + " without parenthesis" | ||||
|                                  ", probably this code won't work as you expected.  " | ||||
|                                  "Use parenthesis to emphasize operator precedence."); | ||||
| } | ||||
| 
 | ||||
| // diagnose when bitwise operators are used in a probably wrong way due to tricky precedence
 | ||||
| // example: "flags & 0xFF != 0" is equivalent to "flags & 1", most likely it's unexpected
 | ||||
| // the only way to suppress this error for the programmer is to use parenthesis
 | ||||
| // (how do we detect presence of parenthesis? simple: (0!=1) is ast_parenthesized_expr{ast_binary_operator},
 | ||||
| //  that's why if rhs->type == ast_binary_operator, it's not surrounded by parenthesis)
 | ||||
| static void diagnose_bitwise_precedence(SrcLocation loc, std::string_view operator_name, AnyV lhs, AnyV rhs) { | ||||
|   // handle "flags & 0xFF != 0" (rhs = "0xFF != 0")
 | ||||
|   if (rhs->type == ast_binary_operator && is_comparison_binary_op(rhs->as<ast_binary_operator>()->tok)) { | ||||
|     fire_error_lower_precedence(loc, operator_name, rhs->as<ast_binary_operator>()->operator_name); | ||||
|   } | ||||
| 
 | ||||
|   // handle "0 != flags & 0xFF" (lhs = "0 != flags")
 | ||||
|   if (lhs->type == ast_binary_operator && is_comparison_binary_op(lhs->as<ast_binary_operator>()->tok)) { | ||||
|     fire_error_lower_precedence(loc, operator_name, lhs->as<ast_binary_operator>()->operator_name); | ||||
|   } | ||||
| 
 | ||||
|   // handle "arg1 & arg2 | arg3" (lhs = "arg1 & arg2")
 | ||||
|   if (lhs->type == ast_binary_operator && is_bitwise_binary_op(lhs->as<ast_binary_operator>()->tok) && lhs->as<ast_binary_operator>()->operator_name != operator_name) { | ||||
|     fire_error_mix_bitwise_and_or(loc, lhs->as<ast_binary_operator>()->operator_name, operator_name); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| // diagnose "a << 8 + 1" (equivalent to "a << 9", probably unexpected)
 | ||||
| static void diagnose_addition_in_bitshift(SrcLocation loc, std::string_view bitshift_operator_name, AnyV rhs) { | ||||
|   if (rhs->type == ast_binary_operator && is_add_or_sub_binary_op(rhs->as<ast_binary_operator>()->tok)) { | ||||
|     fire_error_lower_precedence(loc, bitshift_operator_name, rhs->as<ast_binary_operator>()->operator_name); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * | ||||
|  *   PARSE SOURCE | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| // TE ::= TA | TA -> TE
 | ||||
| // TA ::= int | ... | cont | var | _ | () | ( TE { , TE } ) | [ TE { , TE } ]
 | ||||
| TypeExpr* parse_type(Lexer& lex, V<ast_forall_list> forall_list); | ||||
| 
 | ||||
| TypeExpr* parse_type1(Lexer& lex, V<ast_forall_list> forall_list) { | ||||
|   switch (lex.tok()) { | ||||
|     case tok_int: | ||||
|       lex.next(); | ||||
|       return TypeExpr::new_atomic(TypeExpr::_Int); | ||||
|     case tok_cell: | ||||
|       lex.next(); | ||||
|       return TypeExpr::new_atomic(TypeExpr::_Cell); | ||||
|     case tok_slice: | ||||
|       lex.next(); | ||||
|       return TypeExpr::new_atomic(TypeExpr::_Slice); | ||||
|     case tok_builder: | ||||
|       lex.next(); | ||||
|       return TypeExpr::new_atomic(TypeExpr::_Builder); | ||||
|     case tok_cont: | ||||
|       lex.next(); | ||||
|       return TypeExpr::new_atomic(TypeExpr::_Cont); | ||||
|     case tok_tuple: | ||||
|       lex.next(); | ||||
|       return TypeExpr::new_atomic(TypeExpr::_Tuple); | ||||
|     case tok_var: | ||||
|     case tok_underscore: | ||||
|       lex.next(); | ||||
|       return TypeExpr::new_hole(); | ||||
|     case tok_identifier: { | ||||
|       if (int idx = forall_list ? forall_list->lookup_idx(lex.cur_str()) : -1; idx != -1) { | ||||
|         lex.next(); | ||||
|         return forall_list->get_item(idx)->created_type; | ||||
|       } | ||||
|       lex.error("Is not a type identifier"); | ||||
|     } | ||||
|     default: | ||||
|       break; | ||||
|   } | ||||
|   TokenType c; | ||||
|   if (lex.tok() == tok_opbracket) { | ||||
|     lex.next(); | ||||
|     c = tok_clbracket; | ||||
|   } else { | ||||
|     lex.expect(tok_oppar, "<type>"); | ||||
|     c = tok_clpar; | ||||
|   } | ||||
|   if (lex.tok() == c) { | ||||
|     lex.next(); | ||||
|     return c == tok_clpar ? TypeExpr::new_unit() : TypeExpr::new_tuple({}); | ||||
|   } | ||||
|   auto t1 = parse_type(lex, forall_list); | ||||
|   if (lex.tok() == tok_clpar) { | ||||
|     lex.expect(c, c == tok_clpar ? "')'" : "']'"); | ||||
|     return t1; | ||||
|   } | ||||
|   std::vector<TypeExpr*> tlist{1, t1}; | ||||
|   while (lex.tok() == tok_comma) { | ||||
|     lex.next(); | ||||
|     tlist.push_back(parse_type(lex, forall_list)); | ||||
|   } | ||||
|   lex.expect(c, c == tok_clpar ? "')'" : "']'"); | ||||
|   return c == tok_clpar ? TypeExpr::new_tensor(std::move(tlist)) : TypeExpr::new_tuple(std::move(tlist)); | ||||
| } | ||||
| 
 | ||||
| TypeExpr* parse_type(Lexer& lex, V<ast_forall_list> forall_list) { | ||||
|   TypeExpr* res = parse_type1(lex, forall_list); | ||||
|   if (lex.tok() == tok_mapsto) { | ||||
|     lex.next(); | ||||
|     TypeExpr* to = parse_type(lex, forall_list); | ||||
|     return TypeExpr::new_map(res, to); | ||||
|   } | ||||
|   return res; | ||||
| } | ||||
| 
 | ||||
| AnyV parse_argument(Lexer& lex, V<ast_forall_list> forall_list) { | ||||
|   TypeExpr* arg_type = nullptr; | ||||
|   SrcLocation loc = lex.cur_location(); | ||||
|   if (lex.tok() == tok_underscore) { | ||||
|     lex.next(); | ||||
|     if (lex.tok() == tok_comma || lex.tok() == tok_clpar) { | ||||
|       return createV<ast_argument>(loc, "", TypeExpr::new_hole()); | ||||
|     } | ||||
|     arg_type = TypeExpr::new_hole(); | ||||
|     loc = lex.cur_location(); | ||||
|   } else if (lex.tok() != tok_identifier) { // int, cell, [X], etc.
 | ||||
|     arg_type = parse_type(lex, forall_list); | ||||
|   } else if (lex.tok() == tok_identifier) { | ||||
|     if (forall_list && forall_list->lookup_idx(lex.cur_str()) != -1) { | ||||
|       arg_type = parse_type(lex, forall_list); | ||||
|     } else { | ||||
|       arg_type = TypeExpr::new_hole(); | ||||
|     } | ||||
|   } else { | ||||
|     lex.error("Is not a type identifier"); | ||||
|   } | ||||
|   if (lex.tok() == tok_underscore || lex.tok() == tok_comma || lex.tok() == tok_clpar) { | ||||
|     if (lex.tok() == tok_underscore) { | ||||
|       loc = lex.cur_location(); | ||||
|       lex.next(); | ||||
|     } | ||||
|     return createV<ast_argument>(loc, "", arg_type); | ||||
|   } | ||||
|   lex.check(tok_identifier, "parameter name"); | ||||
|   loc = lex.cur_location(); | ||||
|   std::string_view arg_name = lex.cur_str(); | ||||
|   lex.next(); | ||||
|   return createV<ast_argument>(loc, arg_name, arg_type); | ||||
| } | ||||
| 
 | ||||
| AnyV parse_global_var_declaration(Lexer& lex) { | ||||
|   TypeExpr* declared_type = nullptr; | ||||
|   SrcLocation loc = lex.cur_location(); | ||||
|   if (lex.tok() == tok_underscore) { | ||||
|     lex.next(); | ||||
|     declared_type = TypeExpr::new_hole(); | ||||
|     loc = lex.cur_location(); | ||||
|   } else if (lex.tok() != tok_identifier) { | ||||
|     declared_type = parse_type(lex, nullptr); | ||||
|   } | ||||
|   lex.check(tok_identifier, "global variable name"); | ||||
|   std::string_view var_name = lex.cur_str(); | ||||
|   lex.next(); | ||||
|   return createV<ast_global_var_declaration>(loc, var_name, declared_type); | ||||
| } | ||||
| 
 | ||||
| AnyV parse_expr(Lexer& lex); | ||||
| 
 | ||||
| AnyV parse_constant_declaration(Lexer& lex) { | ||||
|   TypeExpr *declared_type = nullptr; | ||||
|   if (lex.tok() == tok_int) { | ||||
|     declared_type = TypeExpr::new_atomic(TypeExpr::_Int); | ||||
|     lex.next(); | ||||
|   } else if (lex.tok() == tok_slice) { | ||||
|     declared_type = TypeExpr::new_atomic(TypeExpr::_Slice); | ||||
|     lex.next(); | ||||
|   } | ||||
|   lex.check(tok_identifier, "constant name"); | ||||
|   SrcLocation loc = lex.cur_location(); | ||||
|   std::string_view const_name = lex.cur_str(); | ||||
|   lex.next(); | ||||
|   lex.expect(tok_assign, "'='"); | ||||
|   AnyV init_value = parse_expr(lex); | ||||
|   return createV<ast_constant_declaration>(loc, const_name, declared_type, init_value); | ||||
| } | ||||
| 
 | ||||
| AnyV parse_argument_list(Lexer& lex, V<ast_forall_list> forall_list) { | ||||
|   SrcLocation loc = lex.cur_location(); | ||||
|   std::vector<AnyV> args; | ||||
|   lex.expect(tok_oppar, "argument list"); | ||||
|   if (lex.tok() != tok_clpar) { | ||||
|     args.push_back(parse_argument(lex, forall_list)); | ||||
|     while (lex.tok() == tok_comma) { | ||||
|       lex.next(); | ||||
|       args.push_back(parse_argument(lex, forall_list)); | ||||
|     } | ||||
|   } | ||||
|   lex.expect(tok_clpar, "')'"); | ||||
|   return createV<ast_argument_list>(loc, std::move(args)); | ||||
| } | ||||
| 
 | ||||
| AnyV parse_constant_declaration_list(Lexer& lex) { | ||||
|   std::vector<AnyV> consts; | ||||
|   SrcLocation loc = lex.cur_location(); | ||||
|   lex.expect(tok_const, "'const'"); | ||||
|   while (true) { | ||||
|     consts.push_back(parse_constant_declaration(lex)); | ||||
|     if (lex.tok() != tok_comma) { | ||||
|       break; | ||||
|     } | ||||
|     lex.expect(tok_comma, "','"); | ||||
|   } | ||||
|   lex.expect(tok_semicolon, "';'"); | ||||
|   return createV<ast_constant_declaration_list>(loc, std::move(consts)); | ||||
| } | ||||
| 
 | ||||
| AnyV parse_global_var_declaration_list(Lexer& lex) { | ||||
|   std::vector<AnyV> globals; | ||||
|   SrcLocation loc = lex.cur_location(); | ||||
|   lex.expect(tok_global, "'global'"); | ||||
|   while (true) { | ||||
|     globals.push_back(parse_global_var_declaration(lex)); | ||||
|     if (lex.tok() != tok_comma) { | ||||
|       break; | ||||
|     } | ||||
|     lex.expect(tok_comma, "','"); | ||||
|   } | ||||
|   lex.expect(tok_semicolon, "';'"); | ||||
|   return createV<ast_global_var_declaration_list>(loc, std::move(globals)); | ||||
| } | ||||
| 
 | ||||
| // parse ( E { , E } ) | () | [ E { , E } ] | [] | id | num | _
 | ||||
| AnyV parse_expr100(Lexer& lex) { | ||||
|   SrcLocation loc = lex.cur_location(); | ||||
|   if (lex.tok() == tok_oppar) { | ||||
|     lex.next(); | ||||
|     if (lex.tok() == tok_clpar) { | ||||
|       lex.next(); | ||||
|       return createV<ast_tensor>(loc, {}); | ||||
|     } | ||||
|     AnyV res = parse_expr(lex); | ||||
|     if (lex.tok() == tok_clpar) { | ||||
|       lex.next(); | ||||
|       return createV<ast_parenthesized_expr>(loc, res); | ||||
|     } | ||||
|     std::vector<AnyV> items; | ||||
|     bool is_type_expression = res->type == ast_type_expression; // to differ `(a,b)` and `(int,slice)`
 | ||||
|     items.emplace_back(res); | ||||
|     while (lex.tok() == tok_comma) { | ||||
|       lex.next(); | ||||
|       AnyV item = parse_expr(lex); | ||||
|       if (is_type_expression != (item->type == ast_type_expression)) { | ||||
|         lex.error("mixing type and non-type expressions inside the same tuple"); | ||||
|       } | ||||
|       items.emplace_back(item); | ||||
|     } | ||||
|     lex.expect(tok_clpar, "')'"); | ||||
|     if (is_type_expression) { | ||||
|       std::vector<TypeExpr*> types; | ||||
|       types.reserve(items.size()); | ||||
|       for (AnyV item : items) { | ||||
|         types.emplace_back(item->as<ast_type_expression>()->declared_type); | ||||
|       } | ||||
|       return createV<ast_type_expression>(loc, TypeExpr::new_tensor(std::move(types))); | ||||
|     } | ||||
|     return createV<ast_tensor>(loc, std::move(items)); | ||||
|   } | ||||
|   if (lex.tok() == tok_opbracket) { | ||||
|     lex.next(); | ||||
|     if (lex.tok() == tok_clbracket) { | ||||
|       lex.next(); | ||||
|       return createV<ast_tensor_square>(loc, {}); | ||||
|     } | ||||
|     AnyV res = parse_expr(lex); | ||||
|     std::vector<AnyV> items; | ||||
|     bool is_type_expression = res->type == ast_type_expression; // to differ `(a,b)` and `(int,slice)`
 | ||||
|     items.emplace_back(res); | ||||
|     while (lex.tok() == tok_comma) { | ||||
|       lex.next(); | ||||
|       AnyV item = parse_expr(lex); | ||||
|       if (is_type_expression != (item->type == ast_type_expression)) { | ||||
|         lex.error("mixing type and non-type expressions inside the same tuple"); | ||||
|       } | ||||
|       items.emplace_back(item); | ||||
|     } | ||||
|     lex.expect(tok_clbracket, "']'"); | ||||
|     if (is_type_expression) { | ||||
|       std::vector<TypeExpr*> types; | ||||
|       types.reserve(items.size()); | ||||
|       for (AnyV item : items) { | ||||
|         types.emplace_back(item->as<ast_type_expression>()->declared_type); | ||||
|       } | ||||
|       return createV<ast_type_expression>(loc, TypeExpr::new_tuple(TypeExpr::new_tensor(std::move(types)))); | ||||
|     } | ||||
|     return createV<ast_tensor_square>(loc, std::move(items)); | ||||
|   } | ||||
|   TokenType t = lex.tok(); | ||||
|   if (t == tok_int_const) { | ||||
|     std::string_view int_val = lex.cur_str(); | ||||
|     lex.next(); | ||||
|     return createV<ast_int_const>(loc, int_val); | ||||
|   } | ||||
|   if (t == tok_string_const) { | ||||
|     std::string_view str_val = lex.cur_str(); | ||||
|     lex.next(); | ||||
|     char modifier = 0; | ||||
|     if (lex.tok() == tok_string_modifier) { | ||||
|       modifier = lex.cur_str()[0]; | ||||
|       lex.next(); | ||||
|     } | ||||
|     return createV<ast_string_const>(loc, str_val, modifier); | ||||
|   } | ||||
|   if (t == tok_underscore) { | ||||
|     lex.next(); | ||||
|     return createV<ast_underscore>(loc); | ||||
|   } | ||||
|   if (t == tok_var) { | ||||
|     lex.next(); | ||||
|     return createV<ast_type_expression>(loc, TypeExpr::new_hole()); | ||||
|   } | ||||
|   if (t == tok_int || t == tok_cell || t == tok_slice || t == tok_builder || t == tok_cont || t == tok_tuple) { | ||||
|     lex.next(); | ||||
|     return createV<ast_type_expression>(loc, TypeExpr::new_atomic(t)); | ||||
|   } | ||||
|   if (t == tok_true || t == tok_false) { | ||||
|     lex.next(); | ||||
|     return createV<ast_bool_const>(loc, t == tok_true); | ||||
|   } | ||||
|   if (t == tok_nil) { | ||||
|     lex.next(); | ||||
|     return createV<ast_nil_tuple>(loc); | ||||
|   } | ||||
|   if (t == tok_identifier) { | ||||
|     std::string_view str_val = lex.cur_str(); | ||||
|     lex.next(); | ||||
|     return createV<ast_identifier>(loc, str_val); | ||||
|   } | ||||
|   lex.expect(tok_identifier, "identifier"); | ||||
|   return nullptr; | ||||
| } | ||||
| 
 | ||||
| // parse E { E }
 | ||||
| AnyV parse_expr90(Lexer& lex) { | ||||
|   AnyV res = parse_expr100(lex); | ||||
|   while (lex.tok() == tok_oppar || lex.tok() == tok_opbracket || (lex.tok() == tok_identifier && lex.cur_str()[0] != '.' && lex.cur_str()[0] != '~')) { | ||||
|     if (const auto* v_type_expr = res->try_as<ast_type_expression>()) { | ||||
|       AnyV dest = parse_expr100(lex); | ||||
|       return createV<ast_variable_declaration>(v_type_expr->loc, v_type_expr->declared_type, dest); | ||||
|     } else { | ||||
|       AnyV arg = parse_expr100(lex); | ||||
|       return createV<ast_function_call>(res->loc, res, arg); | ||||
|     } | ||||
|   } | ||||
|   return res; | ||||
| } | ||||
| 
 | ||||
| // parse E { .method E | ~method E }
 | ||||
| AnyV parse_expr80(Lexer& lex) { | ||||
|   AnyV lhs = parse_expr90(lex); | ||||
|   while (lex.tok() == tok_identifier && (lex.cur_str()[0] == '.' || lex.cur_str()[0] == '~')) { | ||||
|     std::string_view method_name = lex.cur_str(); | ||||
|     SrcLocation loc = lex.cur_location(); | ||||
|     lex.next(); | ||||
|     const ASTNodeBase *arg = parse_expr100(lex); | ||||
|     lhs = createV<ast_dot_tilde_call>(loc, method_name, lhs, arg); | ||||
|   } | ||||
|   return lhs; | ||||
| } | ||||
| 
 | ||||
| // parse [ ~ | - | + ] E
 | ||||
| AnyV parse_expr75(Lexer& lex) { | ||||
|   TokenType t = lex.tok(); | ||||
|   if (t == tok_bitwise_not || t == tok_minus || t == tok_plus) { | ||||
|     SrcLocation loc = lex.cur_location(); | ||||
|     std::string_view operator_name = lex.cur_str(); | ||||
|     lex.next(); | ||||
|     AnyV rhs = parse_expr75(lex); | ||||
|     return createV<ast_unary_operator>(loc, operator_name, t, rhs); | ||||
|   } else { | ||||
|     return parse_expr80(lex); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| // parse E { (* | / | % | /% | ^/ | ~/ | ^% | ~% ) E }
 | ||||
| AnyV parse_expr30(Lexer& lex) { | ||||
|   AnyV lhs = parse_expr75(lex); | ||||
|   TokenType t = lex.tok(); | ||||
|   while (t == tok_mul || t == tok_div || t == tok_mod || t == tok_divmod || t == tok_divC || | ||||
|          t == tok_divR || t == tok_modC || t == tok_modR) { | ||||
|     SrcLocation loc = lex.cur_location(); | ||||
|     std::string_view operator_name = lex.cur_str(); | ||||
|     lex.next(); | ||||
|     AnyV rhs = parse_expr75(lex); | ||||
|     lhs = createV<ast_binary_operator>(loc, operator_name, t, lhs, rhs); | ||||
|     t = lex.tok(); | ||||
|   } | ||||
|   return lhs; | ||||
| } | ||||
| 
 | ||||
| // parse E { (+ | -) E }
 | ||||
| AnyV parse_expr20(Lexer& lex) { | ||||
|   AnyV lhs = parse_expr30(lex); | ||||
|   TokenType t = lex.tok(); | ||||
|   while (t == tok_minus || t == tok_plus) { | ||||
|     SrcLocation loc = lex.cur_location(); | ||||
|     std::string_view operator_name = lex.cur_str(); | ||||
|     lex.next(); | ||||
|     AnyV rhs = parse_expr30(lex); | ||||
|     lhs = createV<ast_binary_operator>(loc, operator_name, t, lhs, rhs); | ||||
|     t = lex.tok(); | ||||
|   } | ||||
|   return lhs; | ||||
| } | ||||
| 
 | ||||
| // parse E { ( << | >> | ~>> | ^>> ) E }
 | ||||
| AnyV parse_expr17(Lexer& lex) { | ||||
|   AnyV lhs = parse_expr20(lex); | ||||
|   TokenType t = lex.tok(); | ||||
|   while (t == tok_lshift || t == tok_rshift || t == tok_rshiftC || t == tok_rshiftR) { | ||||
|     SrcLocation loc = lex.cur_location(); | ||||
|     std::string_view operator_name = lex.cur_str(); | ||||
|     lex.next(); | ||||
|     AnyV rhs = parse_expr20(lex); | ||||
|     diagnose_addition_in_bitshift(loc, operator_name, rhs); | ||||
|     lhs = createV<ast_binary_operator>(loc, operator_name, t, lhs, rhs); | ||||
|     t = lex.tok(); | ||||
|   } | ||||
|   return lhs; | ||||
| } | ||||
| 
 | ||||
| // parse E [ (== | < | > | <= | >= | != | <=> ) E ]
 | ||||
| AnyV parse_expr15(Lexer& lex) { | ||||
|   AnyV lhs = parse_expr17(lex); | ||||
|   TokenType t = lex.tok(); | ||||
|   if (t == tok_eq || t == tok_lt || t == tok_gt || t == tok_leq || t == tok_geq || t == tok_neq || t == tok_spaceship) { | ||||
|     SrcLocation loc = lex.cur_location(); | ||||
|     std::string_view operator_name = lex.cur_str(); | ||||
|     lex.next(); | ||||
|     AnyV rhs = parse_expr17(lex); | ||||
|     lhs = createV<ast_binary_operator>(loc, operator_name, t, lhs, rhs); | ||||
|   } | ||||
|   return lhs; | ||||
| } | ||||
| 
 | ||||
| // parse E { ( & | `|` | ^ ) E }
 | ||||
| AnyV parse_expr14(Lexer& lex) { | ||||
|   AnyV lhs = parse_expr15(lex); | ||||
|   TokenType t = lex.tok(); | ||||
|   while (t == tok_bitwise_and || t == tok_bitwise_or || t == tok_bitwise_xor) { | ||||
|     SrcLocation loc = lex.cur_location(); | ||||
|     std::string_view operator_name = lex.cur_str(); | ||||
|     lex.next(); | ||||
|     AnyV rhs = parse_expr15(lex); | ||||
|     diagnose_bitwise_precedence(loc, operator_name, lhs, rhs); | ||||
|     lhs = createV<ast_binary_operator>(loc, operator_name, t, lhs, rhs); | ||||
|     t = lex.tok(); | ||||
|   } | ||||
|   return lhs; | ||||
| } | ||||
| 
 | ||||
| // parse E [ ? E : E ]
 | ||||
| AnyV parse_expr13(Lexer& lex) { | ||||
|   AnyV res = parse_expr14(lex); | ||||
|   if (lex.tok() == tok_question) { | ||||
|     SrcLocation loc = lex.cur_location(); | ||||
|     lex.next(); | ||||
|     AnyV when_true = parse_expr(lex); | ||||
|     lex.expect(tok_colon, "':'"); | ||||
|     AnyV when_false = parse_expr13(lex); | ||||
|     return createV<ast_ternary_operator>(loc, res, when_true, when_false); | ||||
|   } | ||||
|   return res; | ||||
| } | ||||
| 
 | ||||
| // parse LE1 (= | += | -= | ... ) E2
 | ||||
| AnyV parse_expr10(Lexer& lex) { | ||||
|   AnyV lhs = parse_expr13(lex); | ||||
|   TokenType t = lex.tok(); | ||||
|   if (t == tok_set_plus || t == tok_set_minus || t == tok_set_mul || t == tok_set_div || t == tok_set_divR || t == tok_set_divC || | ||||
|       t == tok_set_mod || t == tok_set_modC || t == tok_set_modR || t == tok_set_lshift || t == tok_set_rshift || t == tok_set_rshiftC || | ||||
|       t == tok_set_rshiftR || t == tok_set_bitwise_and || t == tok_set_bitwise_or || t == tok_set_bitwise_xor || | ||||
|       t == tok_assign) { | ||||
|     SrcLocation loc = lex.cur_location(); | ||||
|     std::string_view operator_name = lex.cur_str(); | ||||
|     lex.next(); | ||||
|     AnyV rhs = parse_expr10(lex); | ||||
|     return createV<ast_binary_operator>(loc, operator_name, t, lhs, rhs); | ||||
|   } | ||||
|   return lhs; | ||||
| } | ||||
| 
 | ||||
| AnyV parse_expr(Lexer& lex) { | ||||
|   return parse_expr10(lex); | ||||
| } | ||||
| 
 | ||||
| AnyV parse_return_stmt(Lexer& lex) { | ||||
|   SrcLocation loc = lex.cur_location(); | ||||
|   lex.expect(tok_return, "'return'"); | ||||
|   AnyV child = parse_expr(lex); | ||||
|   lex.expect(tok_semicolon, "';'"); | ||||
|   return createV<ast_return_statement>(loc, child); | ||||
| } | ||||
| 
 | ||||
| AnyV parse_statement(Lexer& lex); | ||||
| 
 | ||||
| V<ast_sequence> parse_sequence(Lexer& lex) { | ||||
|   SrcLocation loc = lex.cur_location(); | ||||
|   lex.expect(tok_opbrace, "'{'"); | ||||
|   std::vector<AnyV> items; | ||||
|   while (lex.tok() != tok_clbrace) { | ||||
|     items.push_back(parse_statement(lex)); | ||||
|   } | ||||
|   SrcLocation loc_end = lex.cur_location(); | ||||
|   lex.expect(tok_clbrace, "'}'"); | ||||
|   return createV<ast_sequence>(loc, loc_end, items); | ||||
| } | ||||
| 
 | ||||
| AnyV parse_repeat_statement(Lexer& lex) { | ||||
|   SrcLocation loc = lex.cur_location(); | ||||
|   lex.expect(tok_repeat, "'repeat'"); | ||||
|   AnyV cond = parse_expr(lex); | ||||
|   V<ast_sequence> body = parse_sequence(lex); | ||||
|   return createV<ast_repeat_statement>(loc, cond, body); | ||||
| } | ||||
| 
 | ||||
| AnyV parse_while_statement(Lexer& lex) { | ||||
|   SrcLocation loc = lex.cur_location(); | ||||
|   lex.expect(tok_while, "'while'"); | ||||
|   AnyV cond = parse_expr(lex); | ||||
|   V<ast_sequence> body = parse_sequence(lex); | ||||
|   return createV<ast_while_statement>(loc, cond, body); | ||||
| } | ||||
| 
 | ||||
| ASTNodeBase* parse_do_until_statement(Lexer& lex) { | ||||
|   SrcLocation loc = lex.cur_location(); | ||||
|   lex.expect(tok_do, "'do'"); | ||||
|   V<ast_sequence> body = parse_sequence(lex); | ||||
|   lex.expect(tok_until, "'until'"); | ||||
|   AnyV cond = parse_expr(lex); | ||||
|   return createV<ast_do_until_statement>(loc, body, cond); | ||||
| } | ||||
| 
 | ||||
| AnyV parse_try_catch_statement(Lexer& lex) { | ||||
|   SrcLocation loc = lex.cur_location(); | ||||
|   lex.expect(tok_try, "'try'"); | ||||
|   V<ast_sequence> try_body = parse_sequence(lex); | ||||
|   lex.expect(tok_catch, "'catch'"); | ||||
|   AnyV catch_expr = parse_expr(lex); | ||||
|   V<ast_sequence> catch_body = parse_sequence(lex); | ||||
|   return createV<ast_try_catch_statement>(loc, try_body, catch_expr, catch_body); | ||||
| } | ||||
| 
 | ||||
| AnyV parse_if_statement(Lexer& lex, bool is_ifnot) { | ||||
|   SrcLocation loc = lex.cur_location(); | ||||
|   lex.next(); | ||||
|   AnyV cond = parse_expr(lex); | ||||
|   V<ast_sequence> if_body = parse_sequence(lex); | ||||
|   V<ast_sequence> else_body = nullptr; | ||||
|   if (lex.tok() == tok_else) { | ||||
|     lex.next(); | ||||
|     else_body = parse_sequence(lex); | ||||
|   } else if (lex.tok() == tok_elseif) { | ||||
|     AnyV v_inner_if = parse_if_statement(lex, false); | ||||
|     else_body = createV<ast_sequence>(v_inner_if->loc, lex.cur_location(), {v_inner_if}); | ||||
|   } else if (lex.tok() == tok_elseifnot) { | ||||
|     AnyV v_inner_if = parse_if_statement(lex, true); | ||||
|     else_body = createV<ast_sequence>(v_inner_if->loc, lex.cur_location(), {v_inner_if}); | ||||
|   } else { | ||||
|     else_body = createV<ast_sequence>(lex.cur_location(), lex.cur_location(), {}); | ||||
|   } | ||||
|   return createV<ast_if_statement>(loc, is_ifnot, cond, if_body, else_body); | ||||
| } | ||||
| 
 | ||||
| AnyV parse_statement(Lexer& lex) { | ||||
|   switch (lex.tok()) { | ||||
|     case tok_return: | ||||
|       return parse_return_stmt(lex); | ||||
|     case tok_opbrace: | ||||
|       return parse_sequence(lex); | ||||
|     case tok_repeat: | ||||
|       return parse_repeat_statement(lex); | ||||
|     case tok_if: | ||||
|       return parse_if_statement(lex, false); | ||||
|     case tok_ifnot: | ||||
|       return parse_if_statement(lex, true); | ||||
|     case tok_do: | ||||
|       return parse_do_until_statement(lex); | ||||
|     case tok_while: | ||||
|       return parse_while_statement(lex); | ||||
|     case tok_try: | ||||
|       return parse_try_catch_statement(lex); | ||||
|     case tok_semicolon: { | ||||
|       lex.next(); | ||||
|       return createV<ast_empty>; | ||||
|     } | ||||
|     default: { | ||||
|       AnyV expr = parse_expr(lex); | ||||
|       lex.expect(tok_semicolon, "';'"); | ||||
|       return expr; | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| AnyV parse_func_body(Lexer& lex) { | ||||
|   return parse_sequence(lex); | ||||
| } | ||||
| 
 | ||||
| AnyV parse_asm_func_body(Lexer& lex, V<ast_argument_list> arg_list) { | ||||
|   SrcLocation loc = lex.cur_location(); | ||||
|   lex.expect(tok_asm, "'asm'"); | ||||
|   size_t n_args = arg_list->size(); | ||||
|   if (n_args > 16) { | ||||
|     throw ParseError{loc, "assembler built-in function can have at most 16 arguments"}; | ||||
|   } | ||||
|   std::vector<int> arg_order, ret_order; | ||||
|   if (lex.tok() == tok_oppar) { | ||||
|     lex.next(); | ||||
|     while (lex.tok() == tok_identifier || lex.tok() == tok_int_const) { | ||||
|       int arg_idx = arg_list->lookup_idx(lex.cur_str()); | ||||
|       if (arg_idx == -1) { | ||||
|         lex.error("argument name expected"); | ||||
|       } | ||||
|       arg_order.push_back(arg_idx); | ||||
|       lex.next(); | ||||
|     } | ||||
|     if (lex.tok() == tok_mapsto) { | ||||
|       lex.next(); | ||||
|       while (lex.tok() == tok_int_const) { | ||||
|         int ret_idx = std::atoi(static_cast<std::string>(lex.cur_str()).c_str()); | ||||
|         ret_order.push_back(ret_idx); | ||||
|         lex.next(); | ||||
|       } | ||||
|     } | ||||
|     lex.expect(tok_clpar, "')'"); | ||||
|   } | ||||
|   std::vector<AnyV> asm_commands; | ||||
|   lex.check(tok_string_const, "\"ASM COMMAND\""); | ||||
|   while (lex.tok() == tok_string_const) { | ||||
|     std::string_view asm_command = lex.cur_str(); | ||||
|     asm_commands.push_back(createV<ast_string_const>(lex.cur_location(), asm_command, 0)); | ||||
|     lex.next(); | ||||
|   } | ||||
|   lex.expect(tok_semicolon, "';'"); | ||||
|   return createV<ast_asm_body>(loc, std::move(arg_order), std::move(ret_order), std::move(asm_commands)); | ||||
| } | ||||
| 
 | ||||
| AnyV parse_forall(Lexer& lex) { | ||||
|   SrcLocation loc = lex.cur_location(); | ||||
|   std::vector<AnyV> forall_items; | ||||
|   lex.expect(tok_forall, "'forall'"); | ||||
|   int idx = 0; | ||||
|   while (true) { | ||||
|     lex.check(tok_identifier, "T expected"); | ||||
|     std::string_view nameT = lex.cur_str(); | ||||
|     TypeExpr* type = TypeExpr::new_var(idx++); | ||||
|     forall_items.emplace_back(createV<ast_forall_item>(lex.cur_location(), type, static_cast<std::string>(nameT))); | ||||
|     lex.next(); | ||||
|     if (lex.tok() != tok_comma) { | ||||
|       break; | ||||
|     } | ||||
|     lex.next(); | ||||
|   } | ||||
|   lex.expect(tok_mapsto, "'->'"); | ||||
|   return createV<ast_forall_list>{loc, std::move(forall_items)}; | ||||
| } | ||||
| 
 | ||||
| AnyV parse_function_declaration(Lexer& lex) { | ||||
|   SrcLocation loc = lex.cur_location(); | ||||
|   V<ast_forall_list> forall_list = nullptr; | ||||
|   bool is_get_method = false; | ||||
|   bool is_builtin = false; | ||||
|   bool marked_as_inline = false; | ||||
|   bool marked_as_inline_ref = false; | ||||
|   if (lex.tok() == tok_forall) { | ||||
|     forall_list = parse_forall(lex)->as<ast_forall_list>(); | ||||
|   } else if (lex.tok() == tok_get) { | ||||
|     is_get_method = true; | ||||
|     lex.next(); | ||||
|   } | ||||
|   TypeExpr* ret_type = parse_type(lex, forall_list); | ||||
|   lex.check(tok_identifier, "function name identifier expected"); | ||||
|   std::string func_name = static_cast<std::string>(lex.cur_str()); | ||||
|   lex.next(); | ||||
|   V<ast_argument_list> arg_list = parse_argument_list(lex, forall_list)->as<ast_argument_list>(); | ||||
|   bool marked_as_pure = false; | ||||
|   if (lex.tok() == tok_impure) { | ||||
|     static bool warning_shown = false; | ||||
|     if (!warning_shown) { | ||||
|       lex.cur_location().show_warning("`impure` specifier is deprecated. All functions are impure by default, use `pure` to mark a function as pure"); | ||||
|       warning_shown = true; | ||||
|     } | ||||
|     lex.next(); | ||||
|   } else if (lex.tok() == tok_pure) { | ||||
|     marked_as_pure = true; | ||||
|     lex.next(); | ||||
|   } | ||||
|   if (lex.tok() == tok_inline) { | ||||
|     marked_as_inline = true; | ||||
|     lex.next(); | ||||
|   } else if (lex.tok() == tok_inlineref) { | ||||
|     marked_as_inline_ref = true; | ||||
|     lex.next(); | ||||
|   } | ||||
|   V<ast_int_const> method_id = nullptr; | ||||
|   if (lex.tok() == tok_method_id) { | ||||
|     if (is_get_method) { | ||||
|       lex.error("both `get` and `method_id` are not allowed"); | ||||
|     } | ||||
|     lex.next(); | ||||
|     if (lex.tok() == tok_oppar) {  // method_id(N)
 | ||||
|       lex.next(); | ||||
|       lex.check(tok_int_const, "number"); | ||||
|       std::string_view int_val = lex.cur_str(); | ||||
|       method_id = createV<ast_int_const>(lex.cur_location(), int_val); | ||||
|       lex.next(); | ||||
|       lex.expect(tok_clpar, "')'"); | ||||
|     } else { | ||||
|       static bool warning_shown = false; | ||||
|       if (!warning_shown) { | ||||
|         lex.cur_location().show_warning("`method_id` specifier is deprecated, use `get` keyword.\nExample: `get int seqno() { ... }`"); | ||||
|         warning_shown = true; | ||||
|       } | ||||
|       is_get_method = true; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   AnyV body = nullptr; | ||||
| 
 | ||||
|   if (lex.tok() == tok_builtin) { | ||||
|     is_builtin = true; | ||||
|     body = createV<ast_empty>; | ||||
|     lex.next(); | ||||
|     lex.expect(tok_semicolon, "';'"); | ||||
|   } else if (lex.tok() == tok_semicolon) { | ||||
|     // todo this is just a prototype, remove this "feature" in the future
 | ||||
|     lex.next(); | ||||
|     body = createV<ast_empty>; | ||||
|   } else if (lex.tok() == tok_opbrace) { | ||||
|     body = parse_func_body(lex); | ||||
|   } else if (lex.tok() == tok_asm) { | ||||
|     body = parse_asm_func_body(lex, arg_list); | ||||
|   } else { | ||||
|     lex.expect(tok_opbrace, "function body block"); | ||||
|   } | ||||
| 
 | ||||
|   auto f_declaration = createV<ast_function_declaration>(loc, func_name, arg_list, body); | ||||
|   f_declaration->ret_type = ret_type; | ||||
|   f_declaration->forall_list = forall_list; | ||||
|   f_declaration->marked_as_pure = marked_as_pure; | ||||
|   f_declaration->marked_as_get_method = is_get_method; | ||||
|   f_declaration->marked_as_builtin = is_builtin; | ||||
|   f_declaration->marked_as_inline = marked_as_inline; | ||||
|   f_declaration->marked_as_inline_ref = marked_as_inline_ref; | ||||
|   f_declaration->method_id = method_id; | ||||
|   return f_declaration; | ||||
| } | ||||
| 
 | ||||
| AnyV parse_pragma(Lexer& lex) { | ||||
|   SrcLocation loc = lex.cur_location(); | ||||
|   lex.next_special(tok_pragma_name, "pragma name"); | ||||
|   std::string_view pragma_name = lex.cur_str(); | ||||
|   if (pragma_name == "version") { | ||||
|     lex.next(); | ||||
|     TokenType cmp_tok = lex.tok(); | ||||
|     bool valid = cmp_tok == tok_gt || cmp_tok == tok_geq || cmp_tok == tok_lt || cmp_tok == tok_leq || cmp_tok == tok_eq || cmp_tok == tok_bitwise_xor; | ||||
|     if (!valid) { | ||||
|       lex.error("invalid comparison operator"); | ||||
|     } | ||||
|     lex.next_special(tok_semver, "semver"); | ||||
|     std::string_view semver = lex.cur_str(); | ||||
|     lex.next(); | ||||
|     lex.expect(tok_semicolon, "';'"); | ||||
|     return createV<ast_pragma_version>(loc, cmp_tok, semver); | ||||
|   } | ||||
|   lex.next(); | ||||
|   lex.expect(tok_semicolon, "';'"); | ||||
|   return createV<ast_pragma_no_arg>(loc, pragma_name); | ||||
| } | ||||
| 
 | ||||
| AnyV parse_include_statement(Lexer& lex) { | ||||
|   SrcLocation loc = lex.cur_location(); | ||||
|   lex.expect(tok_include, "#include"); | ||||
|   lex.check(tok_string_const, "source file name"); | ||||
|   std::string_view rel_filename = lex.cur_str(); | ||||
|   if (rel_filename.empty()) { | ||||
|     lex.error("imported file name is an empty string"); | ||||
|   } | ||||
|   lex.next(); | ||||
|   lex.expect(tok_semicolon, "';'"); | ||||
|   return createV<ast_include_statement>(loc, rel_filename); | ||||
| } | ||||
| 
 | ||||
| // the main (exported) function
 | ||||
| AnyV parse_src_file_to_ast(SrcFile* file) { | ||||
|   file->was_parsed = true; | ||||
| 
 | ||||
|   std::vector<AnyV> toplevel_declarations; | ||||
|   Lexer lex(file); | ||||
|   while (!lex.is_eof()) { | ||||
|     if (lex.tok() == tok_pragma) { | ||||
|       toplevel_declarations.push_back(parse_pragma(lex)); | ||||
|     } else if (lex.tok() == tok_include) { | ||||
|       toplevel_declarations.push_back(parse_include_statement(lex)); | ||||
|     } else if (lex.tok() == tok_global) { | ||||
|       toplevel_declarations.push_back(parse_global_var_declaration_list(lex)); | ||||
|     } else if (lex.tok() == tok_const) { | ||||
|       toplevel_declarations.push_back(parse_constant_declaration_list(lex)); | ||||
|     } else { | ||||
|       toplevel_declarations.push_back(parse_function_declaration(lex)); | ||||
|     } | ||||
|   } | ||||
|   return createV<ast_tolk_file>(file, std::move(toplevel_declarations)); | ||||
| } | ||||
| 
 | ||||
| }  // namespace tolk
 | ||||
							
								
								
									
										27
									
								
								tolk/ast-from-tokens.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								tolk/ast-from-tokens.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,27 @@ | |||
| /*
 | ||||
|     This file is part of TON Blockchain Library. | ||||
| 
 | ||||
|     TON Blockchain Library is free software: you can redistribute it and/or modify | ||||
|     it under the terms of the GNU Lesser General Public License as published by | ||||
|     the Free Software Foundation, either version 2 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     TON Blockchain Library is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU Lesser General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU Lesser General Public License | ||||
|     along with TON Blockchain Library.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
| */ | ||||
| #pragma once | ||||
| 
 | ||||
| #include "src-file.h" | ||||
| 
 | ||||
| namespace tolk { | ||||
| 
 | ||||
| struct ASTNodeBase; | ||||
| 
 | ||||
| const ASTNodeBase* parse_src_file_to_ast(SrcFile* file); | ||||
| 
 | ||||
| } // namespace tolk
 | ||||
							
								
								
									
										155
									
								
								tolk/ast-replacer.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										155
									
								
								tolk/ast-replacer.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,155 @@ | |||
| /*
 | ||||
|     This file is part of TON Blockchain Library. | ||||
| 
 | ||||
|     TON Blockchain Library is free software: you can redistribute it and/or modify | ||||
|     it under the terms of the GNU Lesser General Public License as published by | ||||
|     the Free Software Foundation, either version 2 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     TON Blockchain Library is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU Lesser General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU Lesser General Public License | ||||
|     along with TON Blockchain Library.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
| */ | ||||
| #pragma once | ||||
| 
 | ||||
| #include "ast.h" | ||||
| #include "platform-utils.h" | ||||
| 
 | ||||
| /*
 | ||||
|  *   A module of implementing traversing a vertex tree and replacing any vertex to another. | ||||
|  *   For example, to replace "beginCell()" call to "begin_cell()" in a function body (in V<ast_function>) | ||||
|  * regardless of the place this call is performed, you need to iterate over all the function AST, | ||||
|  * to find ast_function_call(beginCell), create ast_function_call(begin_cell) instead and to replace | ||||
|  * a pointer inside its parent. | ||||
|  *   Inheriting from ASTVisitor makes this task quite simple, without any boilerplate. | ||||
|  * | ||||
|  *   If you need just to traverse a vertex tree without replacing vertices, | ||||
|  * consider another api: ast-visitor.h. | ||||
|  */ | ||||
| 
 | ||||
| namespace tolk { | ||||
| 
 | ||||
| class ASTReplacer { | ||||
| protected: | ||||
|   GNU_ATTRIBUTE_ALWAYS_INLINE static AnyV replace_children(const ASTNodeLeaf* v) { | ||||
|     return v; | ||||
|   } | ||||
| 
 | ||||
|   GNU_ATTRIBUTE_ALWAYS_INLINE AnyV replace_children(const ASTNodeUnary* v) { | ||||
|     auto* v_mutable = const_cast<ASTNodeUnary*>(v); | ||||
|     v_mutable->child = replace(v_mutable->child); | ||||
|     return v_mutable; | ||||
|   } | ||||
| 
 | ||||
|   GNU_ATTRIBUTE_ALWAYS_INLINE AnyV replace_children(const ASTNodeBinary* v) { | ||||
|     auto* v_mutable = const_cast<ASTNodeBinary*>(v); | ||||
|     v_mutable->lhs = replace(v->lhs); | ||||
|     v_mutable->rhs = replace(v->rhs); | ||||
|     return v_mutable; | ||||
|   } | ||||
| 
 | ||||
|   GNU_ATTRIBUTE_ALWAYS_INLINE AnyV replace_children(const ASTNodeVararg* v) { | ||||
|     auto* v_mutable = const_cast<ASTNodeVararg*>(v); | ||||
|     for (AnyV& child : v_mutable->children) { | ||||
|       child = replace(child); | ||||
|     } | ||||
|     return v_mutable; | ||||
|   } | ||||
| 
 | ||||
| public: | ||||
|   virtual ~ASTReplacer() = default; | ||||
| 
 | ||||
|   virtual AnyV replace(AnyV v) = 0; | ||||
| }; | ||||
| 
 | ||||
| class ASTReplacerInFunctionBody : public ASTReplacer { | ||||
| protected: | ||||
|   using parent = ASTReplacerInFunctionBody; | ||||
| 
 | ||||
|   virtual AnyV replace(V<ast_empty> v)                   { return replace_children(v); } | ||||
|   virtual AnyV replace(V<ast_identifier> v)              { return replace_children(v); } | ||||
|   virtual AnyV replace(V<ast_int_const> v)               { return replace_children(v); } | ||||
|   virtual AnyV replace(V<ast_string_const> v)            { return replace_children(v); } | ||||
|   virtual AnyV replace(V<ast_bool_const> v)              { return replace_children(v); } | ||||
|   virtual AnyV replace(V<ast_nil_tuple> v)               { return replace_children(v); } | ||||
|   virtual AnyV replace(V<ast_function_call> v)           { return replace_children(v); } | ||||
|   virtual AnyV replace(V<ast_parenthesized_expr> v)      { return replace_children(v); } | ||||
|   virtual AnyV replace(V<ast_underscore> v)              { return replace_children(v); } | ||||
|   virtual AnyV replace(V<ast_type_expression> v)         { return replace_children(v); } | ||||
|   virtual AnyV replace(V<ast_variable_declaration> v)    { return replace_children(v); } | ||||
|   virtual AnyV replace(V<ast_tensor> v)                  { return replace_children(v); } | ||||
|   virtual AnyV replace(V<ast_tensor_square> v)           { return replace_children(v); } | ||||
|   virtual AnyV replace(V<ast_dot_tilde_call> v)          { return replace_children(v); } | ||||
|   virtual AnyV replace(V<ast_unary_operator> v)          { return replace_children(v); } | ||||
|   virtual AnyV replace(V<ast_binary_operator> v)         { return replace_children(v); } | ||||
|   virtual AnyV replace(V<ast_ternary_operator> v)        { return replace_children(v); } | ||||
|   virtual AnyV replace(V<ast_return_statement> v)        { return replace_children(v); } | ||||
|   virtual AnyV replace(V<ast_sequence> v)                { return replace_children(v); } | ||||
|   virtual AnyV replace(V<ast_repeat_statement> v)        { return replace_children(v); } | ||||
|   virtual AnyV replace(V<ast_while_statement> v)         { return replace_children(v); } | ||||
|   virtual AnyV replace(V<ast_do_until_statement> v)      { return replace_children(v); } | ||||
|   virtual AnyV replace(V<ast_try_catch_statement> v)     { return replace_children(v); } | ||||
|   virtual AnyV replace(V<ast_if_statement> v)            { return replace_children(v); } | ||||
|   virtual AnyV replace(V<ast_asm_body> v)                { return replace_children(v); } | ||||
| 
 | ||||
|   AnyV replace(AnyV v) final { | ||||
|     switch (v->type) { | ||||
|       case ast_empty:                           return replace(v->as<ast_empty>()); | ||||
|       case ast_identifier:                      return replace(v->as<ast_identifier>()); | ||||
|       case ast_int_const:                       return replace(v->as<ast_int_const>()); | ||||
|       case ast_string_const:                    return replace(v->as<ast_string_const>()); | ||||
|       case ast_bool_const:                      return replace(v->as<ast_bool_const>()); | ||||
|       case ast_nil_tuple:                       return replace(v->as<ast_nil_tuple>()); | ||||
|       case ast_function_call:                   return replace(v->as<ast_function_call>()); | ||||
|       case ast_parenthesized_expr:              return replace(v->as<ast_parenthesized_expr>()); | ||||
|       case ast_underscore:                      return replace(v->as<ast_underscore>()); | ||||
|       case ast_type_expression:                 return replace(v->as<ast_type_expression>()); | ||||
|       case ast_variable_declaration:            return replace(v->as<ast_variable_declaration>()); | ||||
|       case ast_tensor:                          return replace(v->as<ast_tensor>()); | ||||
|       case ast_tensor_square:                   return replace(v->as<ast_tensor_square>()); | ||||
|       case ast_dot_tilde_call:                  return replace(v->as<ast_dot_tilde_call>()); | ||||
|       case ast_unary_operator:                  return replace(v->as<ast_unary_operator>()); | ||||
|       case ast_binary_operator:                 return replace(v->as<ast_binary_operator>()); | ||||
|       case ast_ternary_operator:                return replace(v->as<ast_ternary_operator>()); | ||||
|       case ast_return_statement:                return replace(v->as<ast_return_statement>()); | ||||
|       case ast_sequence:                        return replace(v->as<ast_sequence>()); | ||||
|       case ast_repeat_statement:                return replace(v->as<ast_repeat_statement>()); | ||||
|       case ast_while_statement:                 return replace(v->as<ast_while_statement>()); | ||||
|       case ast_do_until_statement:              return replace(v->as<ast_do_until_statement>()); | ||||
|       case ast_try_catch_statement:             return replace(v->as<ast_try_catch_statement>()); | ||||
|       case ast_if_statement:                    return replace(v->as<ast_if_statement>()); | ||||
|       case ast_asm_body:                        return replace(v->as<ast_asm_body>()); | ||||
|       default: | ||||
|         throw UnexpectedASTNodeType(v, "ASTReplacerInFunctionBody::visit"); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
| public: | ||||
|   void start_replacing_in_function(V<ast_function_declaration> v) { | ||||
|     replace(v->get_body()); | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| class ASTReplacerAllFunctionsInFile : public ASTReplacerInFunctionBody { | ||||
| protected: | ||||
|   using parent = ASTReplacerAllFunctionsInFile; | ||||
| 
 | ||||
|   virtual bool should_enter_function(V<ast_function_declaration> v) = 0; | ||||
| 
 | ||||
| public: | ||||
|   void start_replacing_in_file(V<ast_tolk_file> v_file) { | ||||
|     for (AnyV v : v_file->get_toplevel_declarations()) { | ||||
|       if (auto v_function = v->try_as<ast_function_declaration>()) { | ||||
|         if (should_enter_function(v_function)) { | ||||
|           replace(v_function->get_body()); | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| } // namespace tolk
 | ||||
							
								
								
									
										233
									
								
								tolk/ast-stringifier.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										233
									
								
								tolk/ast-stringifier.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,233 @@ | |||
| /*
 | ||||
|     This file is part of TON Blockchain Library. | ||||
| 
 | ||||
|     TON Blockchain Library is free software: you can redistribute it and/or modify | ||||
|     it under the terms of the GNU Lesser General Public License as published by | ||||
|     the Free Software Foundation, either version 2 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     TON Blockchain Library is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU Lesser General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU Lesser General Public License | ||||
|     along with TON Blockchain Library.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
| */ | ||||
| #pragma once | ||||
| 
 | ||||
| #ifdef TOLK_DEBUG | ||||
| 
 | ||||
| #include "ast.h" | ||||
| #include "ast-visitor.h" | ||||
| #include <sstream> | ||||
| 
 | ||||
| /*
 | ||||
|  *   ASTStringifier is used to print out the whole vertex tree in a human-readable format. | ||||
|  *   To stringify any vertex, call v->debug_print(), which uses this class. | ||||
|  */ | ||||
| 
 | ||||
| namespace tolk { | ||||
| 
 | ||||
| class ASTStringifier final : public ASTVisitor { | ||||
|   constexpr static std::pair<ASTNodeType, const char*> name_pairs[] = { | ||||
|     {ast_empty, "ast_empty"}, | ||||
|     {ast_identifier, "ast_identifier"}, | ||||
|     {ast_int_const, "ast_int_const"}, | ||||
|     {ast_string_const, "ast_string_const"}, | ||||
|     {ast_bool_const, "ast_bool_const"}, | ||||
|     {ast_nil_tuple, "ast_nil_tuple"}, | ||||
|     {ast_function_call, "ast_function_call"}, | ||||
|     {ast_parenthesized_expr, "ast_parenthesized_expr"}, | ||||
|     {ast_global_var_declaration, "ast_global_var_declaration"}, | ||||
|     {ast_global_var_declaration_list, "ast_global_var_declaration_list"}, | ||||
|     {ast_constant_declaration, "ast_constant_declaration"}, | ||||
|     {ast_constant_declaration_list, "ast_constant_declaration_list"}, | ||||
|     {ast_underscore, "ast_underscore"}, | ||||
|     {ast_type_expression, "ast_type_expression"}, | ||||
|     {ast_variable_declaration, "ast_variable_declaration"}, | ||||
|     {ast_tensor, "ast_tensor"}, | ||||
|     {ast_tensor_square, "ast_tensor_square"}, | ||||
|     {ast_dot_tilde_call, "ast_dot_tilde_call"}, | ||||
|     {ast_unary_operator, "ast_unary_operator"}, | ||||
|     {ast_binary_operator, "ast_binary_operator"}, | ||||
|     {ast_ternary_operator, "ast_ternary_operator"}, | ||||
|     {ast_return_statement, "ast_return_statement"}, | ||||
|     {ast_sequence, "ast_sequence"}, | ||||
|     {ast_repeat_statement, "ast_repeat_statement"}, | ||||
|     {ast_while_statement, "ast_while_statement"}, | ||||
|     {ast_do_until_statement, "ast_do_until_statement"}, | ||||
|     {ast_try_catch_statement, "ast_try_catch_statement"}, | ||||
|     {ast_if_statement, "ast_if_statement"}, | ||||
|     {ast_forall_item, "ast_forall_item"}, | ||||
|     {ast_forall_list, "ast_forall_list"}, | ||||
|     {ast_argument, "ast_argument"}, | ||||
|     {ast_argument_list, "ast_argument_list"}, | ||||
|     {ast_asm_body, "ast_asm_body"}, | ||||
|     {ast_function_declaration, "ast_function_declaration"}, | ||||
|     {ast_pragma_no_arg, "ast_pragma_no_arg"}, | ||||
|     {ast_pragma_version, "ast_pragma_version"}, | ||||
|     {ast_include_statement, "ast_include_statement"}, | ||||
|     {ast_tolk_file, "ast_tolk_file"}, | ||||
|   }; | ||||
| 
 | ||||
|   template<ASTNodeType node_type> | ||||
|   constexpr static const char* ast_node_type_to_string() { | ||||
|     static_assert(std::size(name_pairs) == ast_tolk_file + 1, "name_pairs needs to be updated"); | ||||
|     return name_pairs[node_type].second; | ||||
|   } | ||||
| 
 | ||||
|   int depth = 0; | ||||
|   std::string out; | ||||
|   bool colored = false; | ||||
| 
 | ||||
|   template<ASTNodeType node_type> | ||||
|   void handle_vertex(V<node_type> v) { | ||||
|     out += std::string(depth * 2, ' '); | ||||
|     out += ast_node_type_to_string<node_type>(); | ||||
|     if (std::string postfix = specific_str(v); !postfix.empty()) { | ||||
|       out += colored ? "  \x1b[34m" : " // "; | ||||
|       out += postfix; | ||||
|       out += colored ? "\x1b[0m" : ""; | ||||
|     } | ||||
|     out += '\n'; | ||||
|     depth++; | ||||
|     visit_children(v); | ||||
|     depth--; | ||||
|   } | ||||
| 
 | ||||
|   static std::string specific_str(AnyV node) { | ||||
|     switch (node->type) { | ||||
|       case ast_identifier: | ||||
|         return static_cast<std::string>(node->as<ast_identifier>()->name); | ||||
|       case ast_int_const: | ||||
|         return static_cast<std::string>(node->as<ast_int_const>()->int_val); | ||||
|       case ast_string_const: | ||||
|         if (char modifier = node->as<ast_string_const>()->modifier) { | ||||
|           return "\"" + static_cast<std::string>(node->as<ast_string_const>()->str_val) + "\"" + std::string(1, modifier); | ||||
|         } else { | ||||
|           return "\"" + static_cast<std::string>(node->as<ast_string_const>()->str_val) + "\""; | ||||
|         } | ||||
|       case ast_global_var_declaration: | ||||
|         return static_cast<std::string>(node->as<ast_global_var_declaration>()->var_name); | ||||
|       case ast_constant_declaration: | ||||
|         return static_cast<std::string>(node->as<ast_constant_declaration>()->const_name); | ||||
|       case ast_type_expression: { | ||||
|         std::ostringstream os; | ||||
|         os << node->as<ast_type_expression>()->declared_type; | ||||
|         return os.str(); | ||||
|       } | ||||
|       case ast_variable_declaration: { | ||||
|         std::ostringstream os; | ||||
|         os << node->as<ast_variable_declaration>()->declared_type; | ||||
|         return os.str(); | ||||
|       } | ||||
|       case ast_dot_tilde_call: | ||||
|         return static_cast<std::string>(node->as<ast_dot_tilde_call>()->method_name); | ||||
|       case ast_unary_operator: | ||||
|         return static_cast<std::string>(node->as<ast_unary_operator>()->operator_name); | ||||
|       case ast_binary_operator: | ||||
|         return static_cast<std::string>(node->as<ast_binary_operator>()->operator_name); | ||||
|       case ast_sequence: | ||||
|         return "↓" + std::to_string(node->as<ast_sequence>()->get_items().size()); | ||||
|       case ast_if_statement: | ||||
|         return node->as<ast_if_statement>()->is_ifnot ? "ifnot" : ""; | ||||
|       case ast_argument: { | ||||
|         std::ostringstream os; | ||||
|         os << node->as<ast_argument>()->arg_type; | ||||
|         return static_cast<std::string>(node->as<ast_argument>()->arg_name) + ": " + os.str(); | ||||
|       } | ||||
|       case ast_function_declaration: { | ||||
|         std::string arg_names; | ||||
|         for (int i = 0; i < node->as<ast_function_declaration>()->get_num_args(); i++) { | ||||
|           if (!arg_names.empty()) | ||||
|             arg_names += ","; | ||||
|           arg_names += node->as<ast_function_declaration>()->get_arg(i)->arg_name; | ||||
|         } | ||||
|         return "fun " + node->as<ast_function_declaration>()->name + "(" + arg_names + ")"; | ||||
|       } | ||||
|       case ast_pragma_no_arg: | ||||
|         return static_cast<std::string>(node->as<ast_pragma_no_arg>()->pragma_name); | ||||
|       case ast_pragma_version: | ||||
|         return static_cast<std::string>(node->as<ast_pragma_version>()->semver); | ||||
|       case ast_include_statement: | ||||
|         return static_cast<std::string>(node->as<ast_include_statement>()->file_name); | ||||
|       case ast_tolk_file: | ||||
|         return node->as<ast_tolk_file>()->file->rel_filename; | ||||
|       default: | ||||
|         return {}; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
| public: | ||||
|   explicit ASTStringifier(bool colored) : colored(colored) { | ||||
|   } | ||||
| 
 | ||||
|   std::string to_string_with_children(AnyV v) { | ||||
|     out.clear(); | ||||
|     visit(v); | ||||
|     return std::move(out); | ||||
|   } | ||||
| 
 | ||||
|   static std::string to_string_without_children(AnyV v) { | ||||
|     std::string result = ast_node_type_to_string(v->type); | ||||
|     if (std::string postfix = specific_str(v); !postfix.empty()) { | ||||
|       result += ' '; | ||||
|       result += specific_str(v); | ||||
|     } | ||||
|     return result; | ||||
|   } | ||||
| 
 | ||||
|   static const char* ast_node_type_to_string(ASTNodeType node_type) { | ||||
|     return name_pairs[node_type].second; | ||||
|   } | ||||
| 
 | ||||
|   void visit(AnyV v) override { | ||||
|     switch (v->type) { | ||||
|       case ast_empty:                         return handle_vertex(v->as<ast_empty>()); | ||||
|       case ast_identifier:                    return handle_vertex(v->as<ast_identifier>()); | ||||
|       case ast_int_const:                     return handle_vertex(v->as<ast_int_const>()); | ||||
|       case ast_string_const:                  return handle_vertex(v->as<ast_string_const>()); | ||||
|       case ast_bool_const:                    return handle_vertex(v->as<ast_bool_const>()); | ||||
|       case ast_nil_tuple:                     return handle_vertex(v->as<ast_nil_tuple>()); | ||||
|       case ast_function_call:                 return handle_vertex(v->as<ast_function_call>()); | ||||
|       case ast_parenthesized_expr:            return handle_vertex(v->as<ast_parenthesized_expr>()); | ||||
|       case ast_global_var_declaration:        return handle_vertex(v->as<ast_global_var_declaration>()); | ||||
|       case ast_global_var_declaration_list:   return handle_vertex(v->as<ast_global_var_declaration_list>()); | ||||
|       case ast_constant_declaration:          return handle_vertex(v->as<ast_constant_declaration>()); | ||||
|       case ast_constant_declaration_list:     return handle_vertex(v->as<ast_constant_declaration_list>()); | ||||
|       case ast_underscore:                    return handle_vertex(v->as<ast_underscore>()); | ||||
|       case ast_type_expression:               return handle_vertex(v->as<ast_type_expression>()); | ||||
|       case ast_variable_declaration:          return handle_vertex(v->as<ast_variable_declaration>()); | ||||
|       case ast_tensor:                        return handle_vertex(v->as<ast_tensor>()); | ||||
|       case ast_tensor_square:                 return handle_vertex(v->as<ast_tensor_square>()); | ||||
|       case ast_dot_tilde_call:                return handle_vertex(v->as<ast_dot_tilde_call>()); | ||||
|       case ast_unary_operator:                return handle_vertex(v->as<ast_unary_operator>()); | ||||
|       case ast_binary_operator:               return handle_vertex(v->as<ast_binary_operator>()); | ||||
|       case ast_ternary_operator:              return handle_vertex(v->as<ast_ternary_operator>()); | ||||
|       case ast_return_statement:              return handle_vertex(v->as<ast_return_statement>()); | ||||
|       case ast_sequence:                      return handle_vertex(v->as<ast_sequence>()); | ||||
|       case ast_repeat_statement:              return handle_vertex(v->as<ast_repeat_statement>()); | ||||
|       case ast_while_statement:               return handle_vertex(v->as<ast_while_statement>()); | ||||
|       case ast_do_until_statement:            return handle_vertex(v->as<ast_do_until_statement>()); | ||||
|       case ast_try_catch_statement:           return handle_vertex(v->as<ast_try_catch_statement>()); | ||||
|       case ast_if_statement:                  return handle_vertex(v->as<ast_if_statement>()); | ||||
|       case ast_forall_item:                   return handle_vertex(v->as<ast_forall_item>()); | ||||
|       case ast_forall_list:                   return handle_vertex(v->as<ast_forall_list>()); | ||||
|       case ast_argument:                      return handle_vertex(v->as<ast_argument>()); | ||||
|       case ast_argument_list:                 return handle_vertex(v->as<ast_argument_list>()); | ||||
|       case ast_asm_body:                      return handle_vertex(v->as<ast_asm_body>()); | ||||
|       case ast_function_declaration:          return handle_vertex(v->as<ast_function_declaration>()); | ||||
|       case ast_pragma_no_arg:                 return handle_vertex(v->as<ast_pragma_no_arg>()); | ||||
|       case ast_pragma_version:                return handle_vertex(v->as<ast_pragma_version>()); | ||||
|       case ast_include_statement:             return handle_vertex(v->as<ast_include_statement>()); | ||||
|       case ast_tolk_file:                     return handle_vertex(v->as<ast_tolk_file>()); | ||||
|       default: | ||||
|         throw UnexpectedASTNodeType(v, "ASTStringifier::visit"); | ||||
|     } | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| } // namespace tolk
 | ||||
| 
 | ||||
| #endif // TOLK_DEBUG
 | ||||
							
								
								
									
										1438
									
								
								tolk/ast-to-legacy.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1438
									
								
								tolk/ast-to-legacy.cpp
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										28
									
								
								tolk/ast-to-legacy.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								tolk/ast-to-legacy.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,28 @@ | |||
| /*
 | ||||
|     This file is part of TON Blockchain Library. | ||||
| 
 | ||||
|     TON Blockchain Library is free software: you can redistribute it and/or modify | ||||
|     it under the terms of the GNU Lesser General Public License as published by | ||||
|     the Free Software Foundation, either version 2 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     TON Blockchain Library is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU Lesser General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU Lesser General Public License | ||||
|     along with TON Blockchain Library.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
| */ | ||||
| #pragma once | ||||
| 
 | ||||
| #include "ast.h" | ||||
| 
 | ||||
| namespace tolk { | ||||
| 
 | ||||
| struct SrcFile; | ||||
| 
 | ||||
| void process_file_ast(AnyV file_ast); | ||||
| 
 | ||||
| } // namespace tolk
 | ||||
| 
 | ||||
							
								
								
									
										199
									
								
								tolk/ast-visitor.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										199
									
								
								tolk/ast-visitor.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,199 @@ | |||
| /*
 | ||||
|     This file is part of TON Blockchain Library. | ||||
| 
 | ||||
|     TON Blockchain Library is free software: you can redistribute it and/or modify | ||||
|     it under the terms of the GNU Lesser General Public License as published by | ||||
|     the Free Software Foundation, either version 2 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     TON Blockchain Library is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU Lesser General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU Lesser General Public License | ||||
|     along with TON Blockchain Library.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
| */ | ||||
| #pragma once | ||||
| 
 | ||||
| #include "ast.h" | ||||
| #include "platform-utils.h" | ||||
| 
 | ||||
| /*
 | ||||
|  *   A module implementing base functionality of read-only traversing a vertex tree. | ||||
|  *   Since a vertex in general doesn't store a vector of children, iterating is possible only for concrete node_type. | ||||
|  * E.g., for ast_if_statement, visit nodes cond, if-body and else-body. For ast_string_const, nothing. And so on. | ||||
|  *   Visitors below are helpers to inherit from and handle specific vertex types. | ||||
|  * | ||||
|  *   Note, that absence of "children" in ASTNodeBase is not a drawback. Instead, it encourages you to think | ||||
|  * about types and match the type system. | ||||
|  * | ||||
|  *   The visitor is read-only, it does not modify visited nodes (except if you purposely call mutating methods). | ||||
|  *   For example, if you want to replace "beginCell()" call with "begin_cell", a visitor isn't enough for you. | ||||
|  *   To replace vertices, consider another API: ast-replacer.h. | ||||
|  */ | ||||
| 
 | ||||
| namespace tolk { | ||||
| 
 | ||||
| class ASTVisitor { | ||||
| protected: | ||||
|   GNU_ATTRIBUTE_ALWAYS_INLINE static void visit_children(const ASTNodeLeaf* v) { | ||||
|     static_cast<void>(v); | ||||
|   } | ||||
| 
 | ||||
|   GNU_ATTRIBUTE_ALWAYS_INLINE void visit_children(const ASTNodeUnary* v) { | ||||
|     visit(v->child); | ||||
|   } | ||||
| 
 | ||||
|   GNU_ATTRIBUTE_ALWAYS_INLINE void visit_children(const ASTNodeBinary* v) { | ||||
|     visit(v->lhs); | ||||
|     visit(v->rhs); | ||||
|   } | ||||
| 
 | ||||
|   GNU_ATTRIBUTE_ALWAYS_INLINE void visit_children(const ASTNodeVararg* v) { | ||||
|     for (AnyV child : v->children) { | ||||
|       visit(child); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   virtual void visit(AnyV v) = 0; | ||||
| 
 | ||||
| public: | ||||
|   virtual ~ASTVisitor() = default; | ||||
| }; | ||||
| 
 | ||||
| class ASTVisitorFunctionBody : public ASTVisitor { | ||||
| protected: | ||||
|   using parent = ASTVisitorFunctionBody; | ||||
| 
 | ||||
|   virtual void visit(V<ast_empty> v)                     { return visit_children(v); } | ||||
|   virtual void visit(V<ast_identifier> v)                { return visit_children(v); } | ||||
|   virtual void visit(V<ast_int_const> v)                 { return visit_children(v); } | ||||
|   virtual void visit(V<ast_string_const> v)              { return visit_children(v); } | ||||
|   virtual void visit(V<ast_bool_const> v)                { return visit_children(v); } | ||||
|   virtual void visit(V<ast_nil_tuple> v)                 { return visit_children(v); } | ||||
|   virtual void visit(V<ast_function_call> v)             { return visit_children(v); } | ||||
|   virtual void visit(V<ast_parenthesized_expr> v)        { return visit_children(v); } | ||||
|   virtual void visit(V<ast_underscore> v)                { return visit_children(v); } | ||||
|   virtual void visit(V<ast_type_expression> v)           { return visit_children(v); } | ||||
|   virtual void visit(V<ast_variable_declaration> v)      { return visit_children(v); } | ||||
|   virtual void visit(V<ast_tensor> v)                    { return visit_children(v); } | ||||
|   virtual void visit(V<ast_tensor_square> v)             { return visit_children(v); } | ||||
|   virtual void visit(V<ast_dot_tilde_call> v)            { return visit_children(v); } | ||||
|   virtual void visit(V<ast_unary_operator> v)            { return visit_children(v); } | ||||
|   virtual void visit(V<ast_binary_operator> v)           { return visit_children(v); } | ||||
|   virtual void visit(V<ast_ternary_operator> v)          { return visit_children(v); } | ||||
|   virtual void visit(V<ast_return_statement> v)          { return visit_children(v); } | ||||
|   virtual void visit(V<ast_sequence> v)                  { return visit_children(v); } | ||||
|   virtual void visit(V<ast_repeat_statement> v)          { return visit_children(v); } | ||||
|   virtual void visit(V<ast_while_statement> v)           { return visit_children(v); } | ||||
|   virtual void visit(V<ast_do_until_statement> v)        { return visit_children(v); } | ||||
|   virtual void visit(V<ast_try_catch_statement> v)       { return visit_children(v); } | ||||
|   virtual void visit(V<ast_if_statement> v)              { return visit_children(v); } | ||||
|   virtual void visit(V<ast_asm_body> v)                  { return visit_children(v); } | ||||
| 
 | ||||
|   void visit(AnyV v) final { | ||||
|     switch (v->type) { | ||||
|       case ast_empty:                           return visit(v->as<ast_empty>()); | ||||
|       case ast_identifier:                      return visit(v->as<ast_identifier>()); | ||||
|       case ast_int_const:                       return visit(v->as<ast_int_const>()); | ||||
|       case ast_string_const:                    return visit(v->as<ast_string_const>()); | ||||
|       case ast_bool_const:                      return visit(v->as<ast_bool_const>()); | ||||
|       case ast_nil_tuple:                       return visit(v->as<ast_nil_tuple>()); | ||||
|       case ast_function_call:                   return visit(v->as<ast_function_call>()); | ||||
|       case ast_parenthesized_expr:              return visit(v->as<ast_parenthesized_expr>()); | ||||
|       case ast_underscore:                      return visit(v->as<ast_underscore>()); | ||||
|       case ast_type_expression:                 return visit(v->as<ast_type_expression>()); | ||||
|       case ast_variable_declaration:            return visit(v->as<ast_variable_declaration>()); | ||||
|       case ast_tensor:                          return visit(v->as<ast_tensor>()); | ||||
|       case ast_tensor_square:                   return visit(v->as<ast_tensor_square>()); | ||||
|       case ast_dot_tilde_call:                  return visit(v->as<ast_dot_tilde_call>()); | ||||
|       case ast_unary_operator:                  return visit(v->as<ast_unary_operator>()); | ||||
|       case ast_binary_operator:                 return visit(v->as<ast_binary_operator>()); | ||||
|       case ast_ternary_operator:                return visit(v->as<ast_ternary_operator>()); | ||||
|       case ast_return_statement:                return visit(v->as<ast_return_statement>()); | ||||
|       case ast_sequence:                        return visit(v->as<ast_sequence>()); | ||||
|       case ast_repeat_statement:                return visit(v->as<ast_repeat_statement>()); | ||||
|       case ast_while_statement:                 return visit(v->as<ast_while_statement>()); | ||||
|       case ast_do_until_statement:              return visit(v->as<ast_do_until_statement>()); | ||||
|       case ast_try_catch_statement:             return visit(v->as<ast_try_catch_statement>()); | ||||
|       case ast_if_statement:                    return visit(v->as<ast_if_statement>()); | ||||
|       case ast_asm_body:                        return visit(v->as<ast_asm_body>()); | ||||
|       default: | ||||
|         throw UnexpectedASTNodeType(v, "ASTVisitorFunctionBody::visit"); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
| public: | ||||
|   void start_visiting_function(V<ast_function_declaration> v_function) { | ||||
|     visit(v_function->get_body()); | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| class ASTVisitorAllFunctionsInFile : public ASTVisitorFunctionBody { | ||||
| protected: | ||||
|   using parent = ASTVisitorAllFunctionsInFile; | ||||
| 
 | ||||
|   virtual bool should_enter_function(V<ast_function_declaration> v) = 0; | ||||
| 
 | ||||
| public: | ||||
|   void start_visiting_file(V<ast_tolk_file> v_file) { | ||||
|     for (AnyV v : v_file->get_toplevel_declarations()) { | ||||
|       if (auto v_func = v->try_as<ast_function_declaration>()) { | ||||
|         if (should_enter_function(v_func)) { | ||||
|           visit(v_func->get_body()); | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| class ASTVisitorToplevelDeclarations : public ASTVisitor { | ||||
| protected: | ||||
|   using parent = ASTVisitorToplevelDeclarations; | ||||
| 
 | ||||
|   virtual void on_pragma_no_arg(V<ast_pragma_no_arg> v) = 0; | ||||
|   virtual void on_pragma_version(V<ast_pragma_version> v) = 0; | ||||
|   virtual void on_include_statement(V<ast_include_statement> v) = 0; | ||||
|   virtual void on_constant_declaration(V<ast_constant_declaration> v) = 0; | ||||
|   virtual void on_global_var_declaration(V<ast_global_var_declaration> v) = 0; | ||||
|   virtual void on_function_declaration(V<ast_function_declaration> v) = 0; | ||||
| 
 | ||||
|   void visit(AnyV v) final { | ||||
|    switch (v->type) { | ||||
|      case ast_pragma_no_arg: | ||||
|        on_pragma_no_arg(v->as<ast_pragma_no_arg>()); | ||||
|        break; | ||||
|      case ast_pragma_version: | ||||
|        on_pragma_version(v->as<ast_pragma_version>()); | ||||
|        break; | ||||
|      case ast_include_statement: | ||||
|        on_include_statement(v->as<ast_include_statement>()); | ||||
|        break; | ||||
|      case ast_constant_declaration_list: | ||||
|        for (const auto& v_decl : v->as<ast_constant_declaration_list>()->get_declarations()) { | ||||
|          on_constant_declaration(v_decl->as<ast_constant_declaration>()); | ||||
|        } | ||||
|        break; | ||||
|      case ast_global_var_declaration_list: | ||||
|        for (const auto& v_decl : v->as<ast_global_var_declaration_list>()->get_declarations()) { | ||||
|          on_global_var_declaration(v_decl->as<ast_global_var_declaration>()); | ||||
|        } | ||||
|        break; | ||||
|      case ast_function_declaration: | ||||
|        on_function_declaration(v->as<ast_function_declaration>()); | ||||
|        break; | ||||
|      default: | ||||
|        throw UnexpectedASTNodeType(v, "ASTVisitorToplevelDeclarations::visit"); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
| public: | ||||
|   void start_visiting_file(V<ast_tolk_file> v_file) { | ||||
|     for (AnyV v : v_file->get_toplevel_declarations()) { | ||||
|       visit(v); | ||||
|     } | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| } // namespace tolk
 | ||||
							
								
								
									
										70
									
								
								tolk/ast.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								tolk/ast.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,70 @@ | |||
| /*
 | ||||
|     This file is part of TON Blockchain Library. | ||||
| 
 | ||||
|     TON Blockchain Library is free software: you can redistribute it and/or modify | ||||
|     it under the terms of the GNU Lesser General Public License as published by | ||||
|     the Free Software Foundation, either version 2 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     TON Blockchain Library is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU Lesser General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU Lesser General Public License | ||||
|     along with TON Blockchain Library.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
| */ | ||||
| #include "ast.h" | ||||
| #include "ast-stringifier.h" | ||||
| #include <iostream> | ||||
| 
 | ||||
| namespace tolk { | ||||
| 
 | ||||
| static_assert(sizeof(ASTNodeBase) == 12); | ||||
| 
 | ||||
| #ifdef TOLK_DEBUG | ||||
| 
 | ||||
| std::string ASTNodeBase::to_debug_string(bool colored) const { | ||||
|   ASTStringifier s(colored); | ||||
|   return s.to_string_with_children(this); | ||||
| } | ||||
| 
 | ||||
| void ASTNodeBase::debug_print() const { | ||||
|   std::cerr << to_debug_string(true) << std::endl; | ||||
| } | ||||
| 
 | ||||
| #endif  // TOLK_DEBUG
 | ||||
| 
 | ||||
| UnexpectedASTNodeType::UnexpectedASTNodeType(AnyV v_unexpected, const char* place_where): v_unexpected(v_unexpected) { | ||||
|   message = "Unexpected ASTNodeType "; | ||||
| #ifdef TOLK_DEBUG | ||||
|   message += ASTStringifier::ast_node_type_to_string(v_unexpected->type); | ||||
|   message += " "; | ||||
| #endif | ||||
|   message += "in "; | ||||
|   message += place_where; | ||||
| } | ||||
| 
 | ||||
| void ASTNodeBase::error(const std::string& err_msg) const { | ||||
|   throw ParseError(loc, err_msg); | ||||
| } | ||||
| 
 | ||||
| int Vertex<ast_forall_list>::lookup_idx(std::string_view nameT) const { | ||||
|   for (size_t idx = 0; idx < children.size(); ++idx) { | ||||
|     if (children[idx] && children[idx]->as<ast_forall_item>()->nameT == nameT) { | ||||
|       return static_cast<int>(idx); | ||||
|     } | ||||
|   } | ||||
|   return -1; | ||||
| } | ||||
| 
 | ||||
| int Vertex<ast_argument_list>::lookup_idx(std::string_view arg_name) const { | ||||
|   for (size_t idx = 0; idx < children.size(); ++idx) { | ||||
|     if (children[idx] && children[idx]->as<ast_argument>()->arg_name == arg_name) { | ||||
|       return static_cast<int>(idx); | ||||
|     } | ||||
|   } | ||||
|   return -1; | ||||
| } | ||||
| 
 | ||||
| } // namespace tolk
 | ||||
							
								
								
									
										567
									
								
								tolk/ast.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										567
									
								
								tolk/ast.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,567 @@ | |||
| /*
 | ||||
|     This file is part of TON Blockchain Library. | ||||
| 
 | ||||
|     TON Blockchain Library is free software: you can redistribute it and/or modify | ||||
|     it under the terms of the GNU Lesser General Public License as published by | ||||
|     the Free Software Foundation, either version 2 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     TON Blockchain Library is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU Lesser General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU Lesser General Public License | ||||
|     along with TON Blockchain Library.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
| */ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <string> | ||||
| #include "platform-utils.h" | ||||
| #include "src-file.h" | ||||
| #include "type-expr.h" | ||||
| #include "lexer.h" | ||||
| 
 | ||||
| /*
 | ||||
|  *   Here we introduce AST representation of Tolk source code. | ||||
|  *   Historically, in FunC, there was no AST: while lexing, symbols were registered, types were inferred, and so on. | ||||
|  * There was no way to perform any more or less semantic analysis. | ||||
|  *   In Tolk, I've implemented parsing .tolk files into AST at first, and then converting this AST | ||||
|  * into legacy representation (see ast-to-legacy.cpp). | ||||
|  *   In the future, more and more code analysis will be moved out of legacy to AST-level. | ||||
|  * | ||||
|  *   From the user's point of view, all AST vertices are constant. All API is based on constancy. | ||||
|  * Even though fields of vertex structs are public, they can't be modified, since vertices are accepted by const ref. | ||||
|  *   Generally, there are two ways of accepting a vertex: | ||||
|  *   * AnyV (= const ASTNodeBase*) | ||||
|  *     the only you can do with this vertex is to see v->type (ASTNodeType) and to cast via v->as<node_type>() | ||||
|  *   * V<node_type> (= const Vertex<node_type>*) | ||||
|  *     a specific type of vertex, you can use its fields and methods | ||||
|  *   There is one way of creating a vertex: | ||||
|  *   * createV<node_type>(...constructor_args)   (= new Vertex<node_type>(...)) | ||||
|  *     vertices are currently created on a heap, without any custom memory arena, just allocated and never deleted | ||||
|  * | ||||
|  *   Having AnyV and knowing its node_type, a call | ||||
|  *     v->as<node_type>() | ||||
|  *   will return a typed vertex. | ||||
|  *   There is also a shorthand v->try_as<node_type>() which returns V<node_type> or nullptr if types don't match: | ||||
|  *     if (auto v_int = v->try_as<ast_int_const>()) | ||||
|  *   Note, that there casts are NOT DYNAMIC. ASTNode is not a virtual base, it has no vtable. | ||||
|  *   So, as<...>() is just a compile-time casting, without any runtime overhead. | ||||
|  * | ||||
|  *   Note, that ASTNodeBase doesn't store any vector of children. That's why there is no way to loop over | ||||
|  * a random (unknown) vertex. Only a concrete Vertex<node_type> stores its children (if any). | ||||
|  *   Hence, to iterate over a custom vertex (e.g., a function body), one should inherit some kind of ASTVisitor. | ||||
|  *   Besides read-only visiting, there is a "visit and replace" pattern. | ||||
|  *   See ast-visitor.h and ast-replacer.h. | ||||
|  */ | ||||
| 
 | ||||
| namespace tolk { | ||||
| 
 | ||||
| enum ASTNodeType { | ||||
|   ast_empty, | ||||
|   ast_identifier, | ||||
|   ast_int_const, | ||||
|   ast_string_const, | ||||
|   ast_bool_const, | ||||
|   ast_nil_tuple, | ||||
|   ast_function_call, | ||||
|   ast_parenthesized_expr, | ||||
|   ast_global_var_declaration, | ||||
|   ast_global_var_declaration_list, | ||||
|   ast_constant_declaration, | ||||
|   ast_constant_declaration_list, | ||||
|   ast_underscore, | ||||
|   ast_type_expression, | ||||
|   ast_variable_declaration, | ||||
|   ast_tensor, | ||||
|   ast_tensor_square, | ||||
|   ast_dot_tilde_call, | ||||
|   ast_unary_operator, | ||||
|   ast_binary_operator, | ||||
|   ast_ternary_operator, | ||||
|   ast_return_statement, | ||||
|   ast_sequence, | ||||
|   ast_repeat_statement, | ||||
|   ast_while_statement, | ||||
|   ast_do_until_statement, | ||||
|   ast_try_catch_statement, | ||||
|   ast_if_statement, | ||||
|   ast_forall_item, | ||||
|   ast_forall_list, | ||||
|   ast_argument, | ||||
|   ast_argument_list, | ||||
|   ast_asm_body, | ||||
|   ast_function_declaration, | ||||
|   ast_pragma_no_arg, | ||||
|   ast_pragma_version, | ||||
|   ast_include_statement, | ||||
|   ast_tolk_file, | ||||
| }; | ||||
| 
 | ||||
| struct ASTNodeBase; | ||||
| 
 | ||||
| using AnyV = const ASTNodeBase*; | ||||
| 
 | ||||
| template<ASTNodeType node_type> | ||||
| struct Vertex; | ||||
| 
 | ||||
| template<ASTNodeType node_type> | ||||
| using V = const Vertex<node_type>*; | ||||
| 
 | ||||
| #define createV new Vertex | ||||
| 
 | ||||
| struct UnexpectedASTNodeType final : std::exception { | ||||
|   AnyV v_unexpected; | ||||
|   std::string message; | ||||
| 
 | ||||
|   explicit UnexpectedASTNodeType(AnyV v_unexpected, const char* place_where); | ||||
| 
 | ||||
|   const char* what() const noexcept override { | ||||
|     return message.c_str(); | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| // ---------------------------------------------------------
 | ||||
| 
 | ||||
| struct ASTNodeBase { | ||||
|   const ASTNodeType type; | ||||
|   const SrcLocation loc; | ||||
| 
 | ||||
|   ASTNodeBase(ASTNodeType type, SrcLocation loc) : type(type), loc(loc) {} | ||||
| 
 | ||||
|   template<ASTNodeType node_type> | ||||
|   V<node_type> as() const { | ||||
| #ifdef TOLK_DEBUG | ||||
|     if (type != node_type) { | ||||
|       throw Fatal("v->as<...> to wrong node_type"); | ||||
|     } | ||||
| #endif | ||||
|     return static_cast<V<node_type>>(this); | ||||
|   } | ||||
| 
 | ||||
|   template<ASTNodeType node_type> | ||||
|   V<node_type> try_as() const { | ||||
|     return type == node_type ? static_cast<V<node_type>>(this) : nullptr; | ||||
|   } | ||||
| 
 | ||||
|   #ifdef TOLK_DEBUG | ||||
|   std::string to_debug_string() const { return to_debug_string(false); } | ||||
|   std::string to_debug_string(bool colored) const; | ||||
|   void debug_print() const; | ||||
| #endif | ||||
| 
 | ||||
|   GNU_ATTRIBUTE_NORETURN GNU_ATTRIBUTE_COLD | ||||
|   void error(const std::string& err_msg) const; | ||||
| }; | ||||
| 
 | ||||
| struct ASTNodeLeaf : ASTNodeBase { | ||||
|   friend class ASTVisitor; | ||||
|   friend class ASTReplacer; | ||||
| 
 | ||||
| protected: | ||||
|   ASTNodeLeaf(ASTNodeType type, SrcLocation loc) | ||||
|     : ASTNodeBase(type, loc) {} | ||||
| }; | ||||
| 
 | ||||
| struct ASTNodeUnary : ASTNodeBase { | ||||
|   friend class ASTVisitor; | ||||
|   friend class ASTReplacer; | ||||
| 
 | ||||
| protected: | ||||
|   AnyV child; | ||||
| 
 | ||||
|   ASTNodeUnary(ASTNodeType type, SrcLocation loc, AnyV child) | ||||
|     : ASTNodeBase(type, loc), child(child) {} | ||||
| }; | ||||
| 
 | ||||
| struct ASTNodeBinary : ASTNodeBase { | ||||
|   friend class ASTVisitor; | ||||
|   friend class ASTReplacer; | ||||
| 
 | ||||
| protected: | ||||
|   AnyV lhs; | ||||
|   AnyV rhs; | ||||
| 
 | ||||
|   ASTNodeBinary(ASTNodeType type, SrcLocation loc, AnyV lhs, AnyV rhs) | ||||
|     : ASTNodeBase(type, loc), lhs(lhs), rhs(rhs) {} | ||||
| }; | ||||
| 
 | ||||
| struct ASTNodeVararg : ASTNodeBase { | ||||
|   friend class ASTVisitor; | ||||
|   friend class ASTReplacer; | ||||
| 
 | ||||
| protected: | ||||
|   std::vector<AnyV> children; | ||||
| 
 | ||||
|   ASTNodeVararg(ASTNodeType type, SrcLocation loc, std::vector<AnyV> children) | ||||
|     : ASTNodeBase(type, loc), children(std::move(children)) {} | ||||
| 
 | ||||
| public: | ||||
|   int size() const { return static_cast<int>(children.size()); } | ||||
|   bool empty() const { return children.empty(); } | ||||
| }; | ||||
| 
 | ||||
| // ---------------------------------------------------------
 | ||||
| 
 | ||||
| template<> | ||||
| struct Vertex<ast_empty> final : ASTNodeLeaf { | ||||
|   Vertex() | ||||
|     : ASTNodeLeaf(ast_empty, SrcLocation()) {} | ||||
| }; | ||||
| 
 | ||||
| template<> | ||||
| struct Vertex<ast_identifier> final : ASTNodeLeaf { | ||||
|   std::string_view name; | ||||
| 
 | ||||
|   Vertex(SrcLocation loc, std::string_view name) | ||||
|     : ASTNodeLeaf(ast_identifier, loc), name(name) {} | ||||
| }; | ||||
| 
 | ||||
| template<> | ||||
| struct Vertex<ast_int_const> final : ASTNodeLeaf { | ||||
|   std::string_view int_val; | ||||
| 
 | ||||
|   Vertex(SrcLocation loc, std::string_view int_val) | ||||
|     : ASTNodeLeaf(ast_int_const, loc), int_val(int_val) {} | ||||
| }; | ||||
| 
 | ||||
| template<> | ||||
| struct Vertex<ast_string_const> final : ASTNodeLeaf { | ||||
|   std::string_view str_val; | ||||
|   char modifier; | ||||
| 
 | ||||
|   Vertex(SrcLocation loc, std::string_view str_val, char modifier) | ||||
|     : ASTNodeLeaf(ast_string_const, loc), str_val(str_val), modifier(modifier) {} | ||||
| }; | ||||
| 
 | ||||
| template<> | ||||
| struct Vertex<ast_bool_const> final : ASTNodeLeaf { | ||||
|   bool bool_val; | ||||
| 
 | ||||
|   Vertex(SrcLocation loc, bool bool_val) | ||||
|     : ASTNodeLeaf(ast_bool_const, loc), bool_val(bool_val) {} | ||||
| }; | ||||
| 
 | ||||
| template<> | ||||
| struct Vertex<ast_nil_tuple> final : ASTNodeLeaf { | ||||
|   explicit Vertex(SrcLocation loc) | ||||
|     : ASTNodeLeaf(ast_nil_tuple, loc) {} | ||||
| }; | ||||
| 
 | ||||
| template<> | ||||
| struct Vertex<ast_function_call> final : ASTNodeBinary { | ||||
|   // even for f(1,2,3), f (lhs) is called with a single arg (tensor "(1,2,3)") (rhs)
 | ||||
|   AnyV get_called_f() const { return lhs; } | ||||
|   AnyV get_called_arg() const { return rhs; } | ||||
| 
 | ||||
|   Vertex(SrcLocation loc, AnyV lhs_f, AnyV arg) | ||||
|     : ASTNodeBinary(ast_function_call, loc, lhs_f, arg) {} | ||||
| }; | ||||
| 
 | ||||
| template<> | ||||
| struct Vertex<ast_parenthesized_expr> final : ASTNodeUnary { | ||||
|   AnyV get_expr() const { return child; } | ||||
| 
 | ||||
|   Vertex(SrcLocation loc, AnyV expr) | ||||
|     : ASTNodeUnary(ast_parenthesized_expr, loc, expr) {} | ||||
| }; | ||||
| 
 | ||||
| template<> | ||||
| struct Vertex<ast_global_var_declaration> final : ASTNodeLeaf { | ||||
|   std::string_view var_name; | ||||
|   TypeExpr* declared_type;      // may be nullptr
 | ||||
| 
 | ||||
|   Vertex(SrcLocation loc, std::string_view var_name, TypeExpr* declared_type) | ||||
|     : ASTNodeLeaf(ast_global_var_declaration, loc), var_name(var_name), declared_type(declared_type) {} | ||||
| }; | ||||
| 
 | ||||
| template<> | ||||
| struct Vertex<ast_global_var_declaration_list> final : ASTNodeVararg { | ||||
|   const std::vector<AnyV>& get_declarations() const { return children; } | ||||
| 
 | ||||
|   Vertex(SrcLocation loc, std::vector<AnyV> declarations) | ||||
|     : ASTNodeVararg(ast_global_var_declaration_list, loc, std::move(declarations)) {} | ||||
| }; | ||||
| 
 | ||||
| template<> | ||||
| struct Vertex<ast_constant_declaration> final : ASTNodeUnary { | ||||
|   std::string_view const_name; | ||||
|   TypeExpr* declared_type;      // may be nullptr
 | ||||
| 
 | ||||
|   AnyV get_init_value() const { return child; } | ||||
| 
 | ||||
|   Vertex(SrcLocation loc, std::string_view const_name, TypeExpr* declared_type, AnyV init_value) | ||||
|     : ASTNodeUnary(ast_constant_declaration, loc, init_value), const_name(const_name), declared_type(declared_type) {} | ||||
| }; | ||||
| 
 | ||||
| template<> | ||||
| struct Vertex<ast_constant_declaration_list> final : ASTNodeVararg { | ||||
|   const std::vector<AnyV>& get_declarations() const { return children; } | ||||
| 
 | ||||
|   Vertex(SrcLocation loc, std::vector<AnyV> declarations) | ||||
|     : ASTNodeVararg(ast_constant_declaration_list, loc, std::move(declarations)) {} | ||||
| }; | ||||
| 
 | ||||
| template<> | ||||
| struct Vertex<ast_underscore> final : ASTNodeLeaf { | ||||
|   explicit Vertex(SrcLocation loc) | ||||
|     : ASTNodeLeaf(ast_underscore, loc) {} | ||||
| }; | ||||
| 
 | ||||
| template<> | ||||
| struct Vertex<ast_type_expression> final : ASTNodeLeaf { | ||||
|   TypeExpr* declared_type; | ||||
| 
 | ||||
|   Vertex(SrcLocation loc, TypeExpr* declared_type) | ||||
|     : ASTNodeLeaf(ast_type_expression, loc), declared_type(declared_type) {} | ||||
| }; | ||||
| 
 | ||||
| template<> | ||||
| struct Vertex<ast_variable_declaration> final : ASTNodeUnary { | ||||
|   TypeExpr* declared_type; | ||||
| 
 | ||||
|   AnyV get_variable_or_list() const { return child; }  // identifier, tuple, tensor
 | ||||
| 
 | ||||
|   Vertex(SrcLocation loc, TypeExpr* declared_type, AnyV dest) | ||||
|     : ASTNodeUnary(ast_variable_declaration, loc, dest), declared_type(declared_type) {} | ||||
| }; | ||||
| 
 | ||||
| template<> | ||||
| struct Vertex<ast_tensor> final : ASTNodeVararg { | ||||
|   const std::vector<AnyV>& get_items() const { return children; } | ||||
|   AnyV get_item(int i) const { return children.at(i); } | ||||
| 
 | ||||
|   Vertex(SrcLocation loc, std::vector<AnyV> items) | ||||
|     : ASTNodeVararg(ast_tensor, loc, std::move(items)) {} | ||||
| }; | ||||
| 
 | ||||
| template<> | ||||
| struct Vertex<ast_tensor_square> final : ASTNodeVararg { | ||||
|   const std::vector<AnyV>& get_items() const { return children; } | ||||
|   AnyV get_item(int i) const { return children.at(i); } | ||||
| 
 | ||||
|   Vertex(SrcLocation loc, std::vector<AnyV> items) | ||||
|     : ASTNodeVararg(ast_tensor_square, loc, std::move(items)) {} | ||||
| }; | ||||
| 
 | ||||
| template<> | ||||
| struct Vertex<ast_dot_tilde_call> final : ASTNodeBinary { | ||||
|   std::string_view method_name;      // starts with . or ~
 | ||||
| 
 | ||||
|   AnyV get_lhs() const { return lhs; } | ||||
|   AnyV get_arg() const { return rhs; } | ||||
| 
 | ||||
|   Vertex(SrcLocation loc, std::string_view method_name, AnyV lhs, AnyV rhs) | ||||
|     : ASTNodeBinary(ast_dot_tilde_call, loc, lhs, rhs), method_name(method_name) {} | ||||
| }; | ||||
| 
 | ||||
| template<> | ||||
| struct Vertex<ast_unary_operator> final : ASTNodeUnary { | ||||
|   std::string_view operator_name; | ||||
|   TokenType tok; | ||||
| 
 | ||||
|   AnyV get_rhs() const { return child; } | ||||
| 
 | ||||
|   Vertex(SrcLocation loc, std::string_view operator_name, TokenType tok, AnyV rhs) | ||||
|     : ASTNodeUnary(ast_unary_operator, loc, rhs), operator_name(operator_name), tok(tok) {} | ||||
| }; | ||||
| 
 | ||||
| template<> | ||||
| struct Vertex<ast_binary_operator> final : ASTNodeBinary { | ||||
|   std::string_view operator_name; | ||||
|   TokenType tok; | ||||
| 
 | ||||
|   AnyV get_lhs() const { return lhs; } | ||||
|   AnyV get_rhs() const { return rhs; } | ||||
| 
 | ||||
|   Vertex(SrcLocation loc, std::string_view operator_name, TokenType tok, AnyV lhs, AnyV rhs) | ||||
|     : ASTNodeBinary(ast_binary_operator, loc, lhs, rhs), operator_name(operator_name), tok(tok) {} | ||||
| }; | ||||
| 
 | ||||
| template<> | ||||
| struct Vertex<ast_ternary_operator> final : ASTNodeVararg { | ||||
|   AnyV get_cond() const { return children.at(0); } | ||||
|   AnyV get_when_true() const { return children.at(1); } | ||||
|   AnyV get_when_false() const { return children.at(2); } | ||||
| 
 | ||||
|   Vertex(SrcLocation loc, AnyV cond, AnyV when_true, AnyV when_false) | ||||
|     : ASTNodeVararg(ast_ternary_operator, loc, {cond, when_true, when_false}) {} | ||||
| }; | ||||
| 
 | ||||
| template<> | ||||
| struct Vertex<ast_return_statement> : ASTNodeUnary { | ||||
|   AnyV get_return_value() const { return child; } | ||||
| 
 | ||||
|   Vertex(SrcLocation loc, AnyV child) | ||||
|     : ASTNodeUnary(ast_return_statement, loc, child) {} | ||||
| }; | ||||
| 
 | ||||
| template<> | ||||
| struct Vertex<ast_sequence> final : ASTNodeVararg { | ||||
|   SrcLocation loc_end; | ||||
| 
 | ||||
|   const std::vector<AnyV>& get_items() const { return children; } | ||||
|   AnyV get_item(int i) const { return children.at(i); } | ||||
| 
 | ||||
|   Vertex(SrcLocation loc, SrcLocation loc_end, std::vector<AnyV> items) | ||||
|     : ASTNodeVararg(ast_sequence, loc, std::move(items)), loc_end(loc_end) {} | ||||
| }; | ||||
| 
 | ||||
| template<> | ||||
| struct Vertex<ast_repeat_statement> final : ASTNodeBinary { | ||||
|   AnyV get_cond() const { return lhs; } | ||||
|   auto get_body() const { return rhs->as<ast_sequence>(); } | ||||
| 
 | ||||
|   Vertex(SrcLocation loc, AnyV cond, V<ast_sequence> body) | ||||
|     : ASTNodeBinary(ast_repeat_statement, loc, cond, body) {} | ||||
| }; | ||||
| 
 | ||||
| template<> | ||||
| struct Vertex<ast_while_statement> final : ASTNodeBinary { | ||||
|   AnyV get_cond() const { return lhs; } | ||||
|   auto get_body() const { return rhs->as<ast_sequence>(); } | ||||
| 
 | ||||
|   Vertex(SrcLocation loc, AnyV cond, V<ast_sequence> body) | ||||
|     : ASTNodeBinary(ast_while_statement, loc, cond, body) {} | ||||
| }; | ||||
| 
 | ||||
| template<> | ||||
| struct Vertex<ast_do_until_statement> final : ASTNodeBinary { | ||||
|   auto get_body() const { return lhs->as<ast_sequence>(); } | ||||
|   AnyV get_cond() const { return rhs; } | ||||
| 
 | ||||
|   Vertex(SrcLocation loc, V<ast_sequence> body, AnyV cond) | ||||
|     : ASTNodeBinary(ast_do_until_statement, loc, body, cond) {} | ||||
| }; | ||||
| 
 | ||||
| template<> | ||||
| struct Vertex<ast_try_catch_statement> final : ASTNodeVararg { | ||||
|   auto get_try_body() const { return children.at(0)->as<ast_sequence>(); } | ||||
|   AnyV get_catch_expr() const { return children.at(1); }    // it's a tensor
 | ||||
|   auto get_catch_body() const { return children.at(2)->as<ast_sequence>(); } | ||||
| 
 | ||||
|   Vertex(SrcLocation loc, V<ast_sequence> try_body, AnyV catch_expr, V<ast_sequence> catch_body) | ||||
|     : ASTNodeVararg(ast_try_catch_statement, loc, {try_body, catch_expr, catch_body}) {} | ||||
| }; | ||||
| 
 | ||||
| template<> | ||||
| struct Vertex<ast_if_statement> final : ASTNodeVararg { | ||||
|   bool is_ifnot; | ||||
| 
 | ||||
|   AnyV get_cond() const { return children.at(0); } | ||||
|   auto get_if_body() const { return children.at(1)->as<ast_sequence>(); } | ||||
|   auto get_else_body() const { return children.at(2)->as<ast_sequence>(); }    // always exists (when else omitted, it's empty)
 | ||||
| 
 | ||||
|   Vertex(SrcLocation loc, bool is_ifnot, AnyV cond, V<ast_sequence> if_body, V<ast_sequence> else_body) | ||||
|     : ASTNodeVararg(ast_if_statement, loc, {cond, if_body, else_body}), is_ifnot(is_ifnot) {} | ||||
| }; | ||||
| 
 | ||||
| template<> | ||||
| struct Vertex<ast_forall_item> final : ASTNodeLeaf { | ||||
|   TypeExpr* created_type;   // used to keep same pointer, since TypeExpr::new_var(i) always allocates
 | ||||
|   std::string nameT; | ||||
| 
 | ||||
|   Vertex(SrcLocation loc, TypeExpr* created_type, std::string nameT) | ||||
|     : ASTNodeLeaf(ast_forall_item, loc), created_type(created_type), nameT(std::move(nameT)) {} | ||||
| }; | ||||
| 
 | ||||
| template<> | ||||
| struct Vertex<ast_forall_list> final : ASTNodeVararg { | ||||
|   std::vector<AnyV> get_items() const { return children; } | ||||
|   auto get_item(int i) const { return children.at(i)->as<ast_forall_item>(); } | ||||
| 
 | ||||
|   Vertex(SrcLocation loc, std::vector<AnyV> forall_items) | ||||
|     : ASTNodeVararg(ast_forall_list, loc, std::move(forall_items)) {} | ||||
| 
 | ||||
|   int lookup_idx(std::string_view nameT) const; | ||||
| }; | ||||
| 
 | ||||
| template<> | ||||
| struct Vertex<ast_argument> final : ASTNodeLeaf { | ||||
|   std::string_view arg_name; | ||||
|   TypeExpr* arg_type; | ||||
| 
 | ||||
|   Vertex(SrcLocation loc, std::string_view arg_name, TypeExpr* arg_type) | ||||
|     : ASTNodeLeaf(ast_argument, loc), arg_name(arg_name), arg_type(arg_type) {} | ||||
| }; | ||||
| 
 | ||||
| template<> | ||||
| struct Vertex<ast_asm_body> final : ASTNodeVararg { | ||||
|   std::vector<int> arg_order; | ||||
|   std::vector<int> ret_order; | ||||
| 
 | ||||
|   const std::vector<AnyV>& get_asm_commands() const { return children; }    // ast_string_const[]
 | ||||
| 
 | ||||
|   Vertex(SrcLocation loc, std::vector<int> arg_order, std::vector<int> ret_order, std::vector<AnyV> asm_commands) | ||||
|     : ASTNodeVararg(ast_asm_body, loc, std::move(asm_commands)), arg_order(std::move(arg_order)), ret_order(std::move(ret_order)) {} | ||||
| }; | ||||
| 
 | ||||
| template<> | ||||
| struct Vertex<ast_argument_list> final : ASTNodeVararg { | ||||
|   const std::vector<AnyV>& get_args() const { return children; } | ||||
|   auto get_arg(int i) const { return children.at(i)->as<ast_argument>(); } | ||||
| 
 | ||||
|   Vertex(SrcLocation loc, std::vector<AnyV> args) | ||||
|     : ASTNodeVararg(ast_argument_list, loc, std::move(args)) {} | ||||
| 
 | ||||
|   int lookup_idx(std::string_view arg_name) const; | ||||
| }; | ||||
| 
 | ||||
| template<> | ||||
| struct Vertex<ast_function_declaration> final : ASTNodeBinary { | ||||
|   int get_num_args() const { return lhs->as<ast_argument_list>()->size(); } | ||||
|   auto get_arg_list() const { return lhs->as<ast_argument_list>(); } | ||||
|   auto get_arg(int i) const { return lhs->as<ast_argument_list>()->get_arg(i); } | ||||
|   AnyV get_body() const { return rhs; }   // ast_sequence / ast_asm_body / ast_empty
 | ||||
| 
 | ||||
|   std::string name; | ||||
|   TypeExpr* ret_type = nullptr; | ||||
|   V<ast_forall_list> forall_list = nullptr; | ||||
|   bool marked_as_pure = false; | ||||
|   bool marked_as_builtin = false; | ||||
|   bool marked_as_get_method = false; | ||||
|   bool marked_as_inline = false; | ||||
|   bool marked_as_inline_ref = false; | ||||
|   V<ast_int_const> method_id = nullptr; | ||||
| 
 | ||||
|   Vertex(SrcLocation loc, std::string name, V<ast_argument_list> args, AnyV body) | ||||
|     : ASTNodeBinary(ast_function_declaration, loc, args, body), name(std::move(name)) {} | ||||
| }; | ||||
| 
 | ||||
| template<> | ||||
| struct Vertex<ast_pragma_no_arg> final : ASTNodeLeaf { | ||||
|   std::string_view pragma_name; | ||||
| 
 | ||||
|   Vertex(SrcLocation loc, std::string_view pragma_name) | ||||
|     : ASTNodeLeaf(ast_pragma_no_arg, loc), pragma_name(pragma_name) {} | ||||
| }; | ||||
| 
 | ||||
| template<> | ||||
| struct Vertex<ast_pragma_version> final : ASTNodeLeaf { | ||||
|   TokenType cmp_tok; | ||||
|   std::string_view semver; | ||||
| 
 | ||||
|   Vertex(SrcLocation loc, TokenType cmp_tok, std::string_view semver) | ||||
|     : ASTNodeLeaf(ast_pragma_version, loc), cmp_tok(cmp_tok), semver(semver) {} | ||||
| }; | ||||
| 
 | ||||
| template<> | ||||
| struct Vertex<ast_include_statement> final : ASTNodeLeaf { | ||||
|   std::string_view file_name; | ||||
| 
 | ||||
|   Vertex(SrcLocation loc, std::string_view file_name) | ||||
|     : ASTNodeLeaf(ast_include_statement, loc), file_name(file_name) {} | ||||
| }; | ||||
| 
 | ||||
| template<> | ||||
| struct Vertex<ast_tolk_file> final : ASTNodeVararg { | ||||
|   const SrcFile* const file; | ||||
| 
 | ||||
|   const std::vector<AnyV>& get_toplevel_declarations() const { return children; } | ||||
| 
 | ||||
|   Vertex(const SrcFile* file, std::vector<AnyV> toplevel_declarations) | ||||
|     : ASTNodeVararg(ast_tolk_file, SrcLocation(file), std::move(toplevel_declarations)), file(file) {} | ||||
| }; | ||||
| 
 | ||||
| } // namespace tolk
 | ||||
|  | @ -72,22 +72,6 @@ SymDef* define_builtin_func(const std::string& name, TypeExpr* func_type, const | |||
|   return define_builtin_func_impl(name, new SymValAsmFunc{func_type, make_simple_compile(macro), arg_order, ret_order, !impure}); | ||||
| } | ||||
| 
 | ||||
| SymDef* force_autoapply(SymDef* def) { | ||||
|   if (def) { | ||||
|     auto val = dynamic_cast<SymVal*>(def->value); | ||||
|     if (val) { | ||||
|       val->auto_apply = true; | ||||
|     } | ||||
|   } | ||||
|   return def; | ||||
| } | ||||
| 
 | ||||
| template <typename... Args> | ||||
| SymDef* define_builtin_const(std::string name, TypeExpr* const_type, Args&&... args) { | ||||
|   return force_autoapply( | ||||
|       define_builtin_func(name, TypeExpr::new_map(TypeExpr::new_unit(), const_type), std::forward<Args>(args)...)); | ||||
| } | ||||
| 
 | ||||
| bool SymValAsmFunc::compile(AsmOpList& dest, std::vector<VarDescr>& out, std::vector<VarDescr>& in, | ||||
|                             SrcLocation where) const { | ||||
|   if (simple_compile) { | ||||
|  | @ -1219,11 +1203,10 @@ void define_builtins() { | |||
|   define_builtin_func("_<=_", arith_bin_op, std::bind(compile_cmp_int, _1, _2, 6)); | ||||
|   define_builtin_func("_>=_", arith_bin_op, std::bind(compile_cmp_int, _1, _2, 3)); | ||||
|   define_builtin_func("_<=>_", arith_bin_op, std::bind(compile_cmp_int, _1, _2, 7)); | ||||
|   define_builtin_const("true", Int, /* AsmOp::Const("TRUE") */ std::bind(compile_bool_const, _1, _2, true)); | ||||
|   define_builtin_const("false", Int, /* AsmOp::Const("FALSE") */ std::bind(compile_bool_const, _1, _2, false)); | ||||
|   define_builtin_func("true", TypeExpr::new_map(TypeExpr::new_unit(), Int), /* AsmOp::Const("TRUE") */ std::bind(compile_bool_const, _1, _2, true)); | ||||
|   define_builtin_func("false", TypeExpr::new_map(TypeExpr::new_unit(), Int), /* AsmOp::Const("FALSE") */ std::bind(compile_bool_const, _1, _2, false)); | ||||
|   // define_builtin_func("null", Null, AsmOp::Const("PUSHNULL"));
 | ||||
|   define_builtin_const("nil", Tuple, AsmOp::Const("PUSHNULL")); | ||||
|   define_builtin_const("Nil", Tuple, AsmOp::Const("NIL")); | ||||
|   define_builtin_func("nil", TypeExpr::new_map(TypeExpr::new_unit(), Tuple), AsmOp::Const("PUSHNULL")); | ||||
|   define_builtin_func("null?", TypeExpr::new_forall({X}, TypeExpr::new_map(X, Int)), compile_is_null); | ||||
|   define_builtin_func("throw", impure_un_op, compile_throw, true); | ||||
|   define_builtin_func("throw_if", impure_bin_op, std::bind(compile_cond_throw, _1, _2, true), true); | ||||
|  |  | |||
|  | @ -41,19 +41,7 @@ Expr::Expr(ExprCls c, sym_idx_t name_idx, std::initializer_list<Expr*> _arglist) | |||
|   } | ||||
| } | ||||
| 
 | ||||
| void Expr::chk_rvalue(const Lexer& lex) const { | ||||
|   if (!is_rvalue()) { | ||||
|     lex.error_at("rvalue expected before `", "`"); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void Expr::chk_lvalue(const Lexer& lex) const { | ||||
|   if (!is_lvalue()) { | ||||
|     lex.error_at("lvalue expected before `", "`"); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| bool Expr::deduce_type(const Lexer& lex) { | ||||
| bool Expr::deduce_type() { | ||||
|   if (e_type) { | ||||
|     return true; | ||||
|   } | ||||
|  | @ -77,7 +65,7 @@ bool Expr::deduce_type(const Lexer& lex) { | |||
|         std::ostringstream os; | ||||
|         os << "cannot apply function " << sym->name() << " : " << sym_val->get_type() << " to arguments of type " | ||||
|            << fun_type->args[0] << ": " << ue; | ||||
|         lex.error(os.str()); | ||||
|         throw ParseError(here, os.str()); | ||||
|       } | ||||
|       e_type = fun_type->args[1]; | ||||
|       TypeExpr::remove_indirect(e_type); | ||||
|  | @ -92,7 +80,7 @@ bool Expr::deduce_type(const Lexer& lex) { | |||
|         std::ostringstream os; | ||||
|         os << "cannot apply expression of type " << args[0]->e_type << " to an expression of type " << args[1]->e_type | ||||
|            << ": " << ue; | ||||
|         lex.error(os.str()); | ||||
|         throw ParseError(here, os.str()); | ||||
|       } | ||||
|       e_type = fun_type->args[1]; | ||||
|       TypeExpr::remove_indirect(e_type); | ||||
|  | @ -107,7 +95,7 @@ bool Expr::deduce_type(const Lexer& lex) { | |||
|         std::ostringstream os; | ||||
|         os << "cannot assign an expression of type " << args[1]->e_type << " to a variable or pattern of type " | ||||
|            << args[0]->e_type << ": " << ue; | ||||
|         lex.error(os.str()); | ||||
|         throw ParseError(here, os.str()); | ||||
|       } | ||||
|       e_type = args[0]->e_type; | ||||
|       TypeExpr::remove_indirect(e_type); | ||||
|  | @ -124,7 +112,7 @@ bool Expr::deduce_type(const Lexer& lex) { | |||
|         os << "cannot implicitly assign an expression of type " << args[1]->e_type | ||||
|            << " to a variable or pattern of type " << rhs_type << " in modifying method `" << G.symbols.get_name(val) | ||||
|            << "` : " << ue; | ||||
|         lex.error(os.str()); | ||||
|         throw ParseError(here, os.str()); | ||||
|       } | ||||
|       e_type = rhs_type->args[1]; | ||||
|       TypeExpr::remove_indirect(e_type); | ||||
|  | @ -139,7 +127,7 @@ bool Expr::deduce_type(const Lexer& lex) { | |||
|       } catch (UnifyError& ue) { | ||||
|         std::ostringstream os; | ||||
|         os << "condition in a conditional expression has non-integer type " << args[0]->e_type << ": " << ue; | ||||
|         lex.error(os.str()); | ||||
|         throw ParseError(here, os.str()); | ||||
|       } | ||||
|       try { | ||||
|         unify(args[1]->e_type, args[2]->e_type); | ||||
|  | @ -147,7 +135,7 @@ bool Expr::deduce_type(const Lexer& lex) { | |||
|         std::ostringstream os; | ||||
|         os << "the two variants in a conditional expression have different types " << args[1]->e_type << " and " | ||||
|            << args[2]->e_type << " : " << ue; | ||||
|         lex.error(os.str()); | ||||
|         throw ParseError(here, os.str()); | ||||
|       } | ||||
|       e_type = args[1]->e_type; | ||||
|       TypeExpr::remove_indirect(e_type); | ||||
|  | @ -170,13 +158,13 @@ int Expr::define_new_vars(CodeBlob& code) { | |||
|     } | ||||
|     case _Var: | ||||
|       if (val < 0) { | ||||
|         val = code.create_var(TmpVar::_Named, e_type, sym, here); | ||||
|         val = code.create_var(false, e_type, sym, here); | ||||
|         return 1; | ||||
|       } | ||||
|       break; | ||||
|     case _Hole: | ||||
|       if (val < 0) { | ||||
|         val = code.create_var(TmpVar::_Tmp, e_type, nullptr, here); | ||||
|         val = code.create_var(true, e_type, nullptr, here); | ||||
|       } | ||||
|       break; | ||||
|   } | ||||
|  | @ -279,7 +267,7 @@ std::vector<var_idx_t> pre_compile_tensor(const std::vector<Expr *>& args, CodeB | |||
|     res_lists[i] = args[i]->pre_compile(code, lval_globs); | ||||
|     for (size_t j = 0; j < res_lists[i].size(); ++j) { | ||||
|       TmpVar& var = code.vars.at(res_lists[i][j]); | ||||
|       if (!lval_globs && (var.cls & TmpVar::_Named)) { | ||||
|       if (!lval_globs && !var.is_tmp_unnamed) { | ||||
|         var.on_modification.push_back([&modified_vars, i, j, cur_ops = code.cur_ops, done = false](SrcLocation here) mutable { | ||||
|           if (!done) { | ||||
|             done = true; | ||||
|  |  | |||
|  | @ -361,19 +361,21 @@ struct ChunkIdentifierOrKeyword final : ChunkLexerBase { | |||
|         if (str == "asm") return tok_asm; | ||||
|         if (str == "get") return tok_get; | ||||
|         if (str == "try") return tok_try; | ||||
|         if (str == "nil") return tok_nil; | ||||
|         break; | ||||
|       case 4: | ||||
|         if (str == "else") return tok_else; | ||||
|         if (str == "true") return tok_true; | ||||
|         if (str == "pure") return tok_pure; | ||||
|         if (str == "then") return tok_then; | ||||
|         if (str == "cell") return tok_cell; | ||||
|         if (str == "cont") return tok_cont; | ||||
|         if (str == "type") return tok_type; // todo unused token?
 | ||||
|         break; | ||||
|       case 5: | ||||
|         if (str == "slice") return tok_slice; | ||||
|         if (str == "tuple") return tok_tuple; | ||||
|         if (str == "const") return tok_const; | ||||
|         if (str == "false") return tok_false; | ||||
|         if (str == "while") return tok_while; | ||||
|         if (str == "until") return tok_until; | ||||
|         if (str == "catch") return tok_catch; | ||||
|  | @ -427,7 +429,7 @@ struct ChunkIdentifierOrKeyword final : ChunkLexerBase { | |||
|     if (TokenType kw_tok = maybe_keyword(str_val)) { | ||||
|       lex->add_token(kw_tok, str_val); | ||||
|     } else { | ||||
|       G.symbols.lookup_add(static_cast<std::string>(str_val)); | ||||
|       G.symbols.lookup_add(str_val); | ||||
|       lex->add_token(tok_identifier, str_val); | ||||
|     } | ||||
|     return true; | ||||
|  | @ -453,7 +455,7 @@ struct ChunkIdentifierInBackticks final : ChunkLexerBase { | |||
| 
 | ||||
|     std::string_view str_val(str_begin + 1, lex->c_str() - str_begin - 1); | ||||
|     lex->skip_chars(1); | ||||
|     G.symbols.lookup_add(static_cast<std::string>(str_val)); | ||||
|     G.symbols.lookup_add(str_val); | ||||
|     lex->add_token(tok_identifier, str_val); | ||||
|     return true; | ||||
|   } | ||||
|  | @ -610,21 +612,12 @@ void Lexer::next_special(TokenType parse_next_as, const char* str_expected) { | |||
|   cur_token = tokens_circularbuf[++cur_token_idx & 7]; | ||||
| } | ||||
| 
 | ||||
| int Lexer::cur_sym_idx() const { | ||||
|   assert(tok() == tok_identifier); | ||||
|   return G.symbols.lookup_add(cur_str_std_string()); | ||||
| } | ||||
| 
 | ||||
| void Lexer::error(const std::string& err_msg) const { | ||||
|   throw ParseError(cur_location(), err_msg); | ||||
| } | ||||
| 
 | ||||
| void Lexer::error_at(const std::string& prefix, const std::string& suffix) const { | ||||
|   throw ParseError(cur_location(), prefix + cur_str_std_string() + suffix); | ||||
| } | ||||
| 
 | ||||
| void Lexer::on_expect_call_failed(const char* str_expected) const { | ||||
|   throw ParseError(cur_location(), std::string(str_expected) + " expected instead of `" + cur_str_std_string() + "`"); | ||||
|   throw ParseError(cur_location(), std::string(str_expected) + " expected instead of `" + std::string(cur_str()) + "`"); | ||||
| } | ||||
| 
 | ||||
| void lexer_init() { | ||||
|  |  | |||
|  | @ -31,6 +31,10 @@ enum TokenType { | |||
| 
 | ||||
|   tok_identifier, | ||||
| 
 | ||||
|   tok_true, | ||||
|   tok_false, | ||||
|   tok_nil,      // todo "null" keyword is still absent, "nil" in FunC is an empty tuple
 | ||||
| 
 | ||||
|   tok_plus, | ||||
|   tok_minus, | ||||
|   tok_mul, | ||||
|  | @ -108,7 +112,6 @@ enum TokenType { | |||
|   tok_builder, | ||||
|   tok_cont, | ||||
|   tok_tuple, | ||||
|   tok_type, | ||||
|   tok_mapsto, | ||||
|   tok_forall, | ||||
| 
 | ||||
|  | @ -206,10 +209,8 @@ public: | |||
| 
 | ||||
|   TokenType tok() const { return cur_token.type; } | ||||
|   std::string_view cur_str() const { return cur_token.str_val; } | ||||
|   std::string cur_str_std_string() const { return static_cast<std::string>(cur_token.str_val); } | ||||
|   SrcLocation cur_location() const { return location; } | ||||
|   const SrcFile* cur_file() const { return file; } | ||||
|   int cur_sym_idx() const; | ||||
| 
 | ||||
|   void next(); | ||||
|   void next_special(TokenType parse_next_as, const char* str_expected); | ||||
|  | @ -228,8 +229,6 @@ public: | |||
| 
 | ||||
|   GNU_ATTRIBUTE_NORETURN GNU_ATTRIBUTE_COLD | ||||
|   void error(const std::string& err_msg) const; | ||||
|   GNU_ATTRIBUTE_NORETURN GNU_ATTRIBUTE_COLD | ||||
|   void error_at(const std::string& prefix, const std::string& suffix) const; | ||||
| }; | ||||
| 
 | ||||
| void lexer_init(); | ||||
|  |  | |||
							
								
								
									
										1983
									
								
								tolk/parse-tolk.cpp
									
										
									
									
									
								
							
							
						
						
									
										1983
									
								
								tolk/parse-tolk.cpp
									
										
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -22,12 +22,6 @@ | |||
| namespace tolk { | ||||
| 
 | ||||
| 
 | ||||
| Symbol::Symbol(std::string str, sym_idx_t idx) : str(std::move(str)), idx(idx) { | ||||
|   subclass = this->str[0] == '.'   ? SymbolSubclass::dot_identifier | ||||
|              : this->str[0] == '~' ? SymbolSubclass::tilde_identifier | ||||
|                                    : SymbolSubclass::undef; | ||||
| } | ||||
| 
 | ||||
| std::string Symbol::unknown_symbol_name(sym_idx_t i) { | ||||
|   if (!i) { | ||||
|     return "_"; | ||||
|  | @ -78,7 +72,7 @@ void open_scope(SrcLocation loc) { | |||
|   G.scope_opened_at.push_back(loc); | ||||
| } | ||||
| 
 | ||||
| void close_scope(SrcLocation loc) { | ||||
| void close_scope() { | ||||
|   if (!G.scope_level) { | ||||
|     throw Fatal{"cannot close the outer scope"}; | ||||
|   } | ||||
|  |  | |||
|  | @ -36,18 +36,11 @@ struct SymValBase { | |||
| }; | ||||
| 
 | ||||
| 
 | ||||
| enum class SymbolSubclass { | ||||
|   undef = 0, | ||||
|   dot_identifier = 1,    // begins with . (a const method)
 | ||||
|   tilde_identifier = 2   // begins with ~ (a non-const method)
 | ||||
| }; | ||||
| 
 | ||||
| struct Symbol { | ||||
|   std::string str; | ||||
|   sym_idx_t idx; | ||||
|   SymbolSubclass subclass; | ||||
| 
 | ||||
|   Symbol(std::string str, sym_idx_t idx); | ||||
|   Symbol(std::string str, sym_idx_t idx) : str(std::move(str)), idx(idx) {} | ||||
| 
 | ||||
|   static std::string unknown_symbol_name(sym_idx_t i); | ||||
| }; | ||||
|  | @ -64,10 +57,10 @@ private: | |||
| public: | ||||
| 
 | ||||
|   static constexpr sym_idx_t not_found = 0; | ||||
|   sym_idx_t lookup(const std::string_view& str, int mode = 0) { | ||||
|   sym_idx_t lookup(std::string_view str, int mode = 0) { | ||||
|     return gen_lookup(str, mode); | ||||
|   } | ||||
|   sym_idx_t lookup_add(const std::string& str) { | ||||
|   sym_idx_t lookup_add(std::string_view str) { | ||||
|     return gen_lookup(str, 1); | ||||
|   } | ||||
|   Symbol* operator[](sym_idx_t i) const { | ||||
|  | @ -76,9 +69,6 @@ public: | |||
|   std::string get_name(sym_idx_t i) const { | ||||
|     return sym[i] ? sym[i]->str : Symbol::unknown_symbol_name(i); | ||||
|   } | ||||
|   SymbolSubclass get_subclass(sym_idx_t i) const { | ||||
|     return sym[i] ? sym[i]->subclass : SymbolSubclass::undef; | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| struct SymTableOverflow { | ||||
|  | @ -104,7 +94,7 @@ struct SymDef { | |||
| 
 | ||||
| 
 | ||||
| void open_scope(SrcLocation loc); | ||||
| void close_scope(SrcLocation loc); | ||||
| void close_scope(); | ||||
| SymDef* lookup_symbol(sym_idx_t idx); | ||||
| 
 | ||||
| SymDef* define_global_symbol(sym_idx_t name_idx, bool force_new = false, SrcLocation loc = {}); | ||||
|  |  | |||
|  | @ -27,7 +27,8 @@ | |||
| #include "compiler-state.h" | ||||
| #include "lexer.h" | ||||
| #include <getopt.h> | ||||
| #include "git.h" | ||||
| #include "ast-from-tokens.h" | ||||
| #include "ast-to-legacy.h" | ||||
| #include <fstream> | ||||
| #include "td/utils/port/path.h" | ||||
| #include <sys/stat.h> | ||||
|  | @ -269,13 +270,13 @@ int tolk_proceed(const std::string &entrypoint_file_name) { | |||
|       if (locate_res.is_error()) { | ||||
|         throw Fatal("Failed to locate stdlib: " + locate_res.error().message().str()); | ||||
|       } | ||||
|       parse_source_file(locate_res.move_as_ok()); | ||||
|       process_file_ast(parse_src_file_to_ast(locate_res.move_as_ok())); | ||||
|     } | ||||
|     td::Result<SrcFile*> locate_res = locate_source_file(entrypoint_file_name); | ||||
|     if (locate_res.is_error()) { | ||||
|       throw Fatal("Failed to locate " + entrypoint_file_name + ": " + locate_res.error().message().str()); | ||||
|     } | ||||
|     parse_source_file(locate_res.move_as_ok()); | ||||
|     process_file_ast(parse_src_file_to_ast(locate_res.move_as_ok())); | ||||
| 
 | ||||
|     // todo #ifdef TOLK_PROFILING + comment
 | ||||
|     // lexer_measure_performance(all_src_files.get_all_files());
 | ||||
|  | @ -293,6 +294,10 @@ int tolk_proceed(const std::string &entrypoint_file_name) { | |||
|     unif_err.print_message(std::cerr); | ||||
|     std::cerr << std::endl; | ||||
|     return 2; | ||||
|   } catch (UnexpectedASTNodeType& error) { | ||||
|     std::cerr << "fatal: " << error.what() << std::endl; | ||||
|     std::cerr << "It's a compiler bug, please report to developers" << std::endl; | ||||
|     return 2; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										172
									
								
								tolk/tolk.h
									
										
									
									
									
								
							
							
						
						
									
										172
									
								
								tolk/tolk.h
									
										
									
									
									
								
							|  | @ -17,7 +17,7 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include "src-file.h" | ||||
| #include "lexer.h" | ||||
| #include "type-expr.h" | ||||
| #include "symtable.h" | ||||
| #include "crypto/common/refint.h" | ||||
| #include "td/utils/Status.h" | ||||
|  | @ -38,136 +38,6 @@ namespace tolk { | |||
|  *  | ||||
|  */ | ||||
| 
 | ||||
| struct TypeExpr { | ||||
|   enum te_type { te_Unknown, te_Var, te_Indirect, te_Atomic, te_Tensor, te_Tuple, te_Map, te_ForAll } constr; | ||||
|   enum AtomicType { | ||||
|     _Int = tok_int, | ||||
|     _Cell = tok_cell, | ||||
|     _Slice = tok_slice, | ||||
|     _Builder = tok_builder, | ||||
|     _Cont = tok_cont, | ||||
|     _Tuple = tok_tuple, | ||||
|     _Type = tok_type | ||||
|   }; | ||||
|   int value; | ||||
|   int minw, maxw; | ||||
|   static constexpr int w_inf = 1023; | ||||
|   std::vector<TypeExpr*> args; | ||||
|   bool was_forall_var = false; | ||||
|   TypeExpr(te_type _constr, int _val = 0) : constr(_constr), value(_val), minw(0), maxw(w_inf) { | ||||
|   } | ||||
|   TypeExpr(te_type _constr, int _val, int width) : constr(_constr), value(_val), minw(width), maxw(width) { | ||||
|   } | ||||
|   TypeExpr(te_type _constr, std::vector<TypeExpr*> list) | ||||
|       : constr(_constr), value((int)list.size()), args(std::move(list)) { | ||||
|     compute_width(); | ||||
|   } | ||||
|   TypeExpr(te_type _constr, std::initializer_list<TypeExpr*> list) | ||||
|       : constr(_constr), value((int)list.size()), args(std::move(list)) { | ||||
|     compute_width(); | ||||
|   } | ||||
|   TypeExpr(te_type _constr, TypeExpr* elem0) : constr(_constr), value(1), args{elem0} { | ||||
|     compute_width(); | ||||
|   } | ||||
|   TypeExpr(te_type _constr, TypeExpr* elem0, std::vector<TypeExpr*> list) | ||||
|       : constr(_constr), value((int)list.size() + 1), args{elem0} { | ||||
|     args.insert(args.end(), list.begin(), list.end()); | ||||
|     compute_width(); | ||||
|   } | ||||
|   TypeExpr(te_type _constr, TypeExpr* elem0, std::initializer_list<TypeExpr*> list) | ||||
|       : constr(_constr), value((int)list.size() + 1), args{elem0} { | ||||
|     args.insert(args.end(), list.begin(), list.end()); | ||||
|     compute_width(); | ||||
|   } | ||||
|   bool is_atomic() const { | ||||
|     return constr == te_Atomic; | ||||
|   } | ||||
|   bool is_atomic(int v) const { | ||||
|     return constr == te_Atomic && value == v; | ||||
|   } | ||||
|   bool is_int() const { | ||||
|     return is_atomic(_Int); | ||||
|   } | ||||
|   bool is_var() const { | ||||
|     return constr == te_Var; | ||||
|   } | ||||
|   bool is_map() const { | ||||
|     return constr == te_Map; | ||||
|   } | ||||
|   bool is_tuple() const { | ||||
|     return constr == te_Tuple; | ||||
|   } | ||||
|   bool has_fixed_width() const { | ||||
|     return minw == maxw; | ||||
|   } | ||||
|   int get_width() const { | ||||
|     return has_fixed_width() ? minw : -1; | ||||
|   } | ||||
|   void compute_width(); | ||||
|   bool recompute_width(); | ||||
|   void show_width(std::ostream& os); | ||||
|   std::ostream& print(std::ostream& os, int prio = 0) const; | ||||
|   void replace_with(TypeExpr* te2); | ||||
|   int extract_components(std::vector<TypeExpr*>& comp_list); | ||||
|   bool equals_to(const TypeExpr* rhs) const; | ||||
|   bool has_unknown_inside() const; | ||||
|   static int holes, type_vars; | ||||
|   static TypeExpr* new_hole() { | ||||
|     return new TypeExpr{te_Unknown, ++holes}; | ||||
|   } | ||||
|   static TypeExpr* new_hole(int width) { | ||||
|     return new TypeExpr{te_Unknown, ++holes, width}; | ||||
|   } | ||||
|   static TypeExpr* new_unit() { | ||||
|     return new TypeExpr{te_Tensor, 0, 0}; | ||||
|   } | ||||
|   static TypeExpr* new_atomic(int value) { | ||||
|     return new TypeExpr{te_Atomic, value, 1}; | ||||
|   } | ||||
|   static TypeExpr* new_map(TypeExpr* from, TypeExpr* to); | ||||
|   static TypeExpr* new_func() { | ||||
|     return new_map(new_hole(), new_hole()); | ||||
|   } | ||||
|   static TypeExpr* new_tensor(std::vector<TypeExpr*> list, bool red = true) { | ||||
|     return red && list.size() == 1 ? list[0] : new TypeExpr{te_Tensor, std::move(list)}; | ||||
|   } | ||||
|   static TypeExpr* new_tensor(std::initializer_list<TypeExpr*> list) { | ||||
|     return new TypeExpr{te_Tensor, std::move(list)}; | ||||
|   } | ||||
|   static TypeExpr* new_tensor(TypeExpr* te1, TypeExpr* te2) { | ||||
|     return new_tensor({te1, te2}); | ||||
|   } | ||||
|   static TypeExpr* new_tensor(TypeExpr* te1, TypeExpr* te2, TypeExpr* te3) { | ||||
|     return new_tensor({te1, te2, te3}); | ||||
|   } | ||||
|   static TypeExpr* new_tuple(TypeExpr* arg0) { | ||||
|     return new TypeExpr{te_Tuple, arg0}; | ||||
|   } | ||||
|   static TypeExpr* new_tuple(std::vector<TypeExpr*> list, bool red = false) { | ||||
|     return new_tuple(new_tensor(std::move(list), red)); | ||||
|   } | ||||
|   static TypeExpr* new_tuple(std::initializer_list<TypeExpr*> list) { | ||||
|     return new_tuple(new_tensor(std::move(list))); | ||||
|   } | ||||
|   static TypeExpr* new_var() { | ||||
|     return new TypeExpr{te_Var, --type_vars, 1}; | ||||
|   } | ||||
|   static TypeExpr* new_var(int idx) { | ||||
|     return new TypeExpr{te_Var, idx, 1}; | ||||
|   } | ||||
|   static TypeExpr* new_forall(std::vector<TypeExpr*> list, TypeExpr* body) { | ||||
|     return new TypeExpr{te_ForAll, body, std::move(list)}; | ||||
|   } | ||||
|   static TypeExpr* new_forall(std::initializer_list<TypeExpr*> list, TypeExpr* body) { | ||||
|     return new TypeExpr{te_ForAll, body, std::move(list)}; | ||||
|   } | ||||
|   static bool remove_indirect(TypeExpr*& te, TypeExpr* forbidden = nullptr); | ||||
|   static std::vector<TypeExpr*> remove_forall(TypeExpr*& te); | ||||
|   static bool remove_forall_in(TypeExpr*& te, TypeExpr* te2, const std::vector<TypeExpr*>& new_vars); | ||||
| }; | ||||
| 
 | ||||
| std::ostream& operator<<(std::ostream& os, TypeExpr* type_expr); | ||||
| 
 | ||||
| struct UnifyError : std::exception { | ||||
|   TypeExpr* te1; | ||||
|   TypeExpr* te2; | ||||
|  | @ -197,14 +67,13 @@ using const_idx_t = int; | |||
| struct TmpVar { | ||||
|   TypeExpr* v_type; | ||||
|   var_idx_t idx; | ||||
|   enum { _In = 1, _Named = 2, _Tmp = 4, _UniqueName = 0x20 }; | ||||
|   int cls; | ||||
|   bool is_tmp_unnamed; | ||||
|   sym_idx_t name; | ||||
|   int coord; | ||||
|   SrcLocation where; | ||||
|   std::vector<std::function<void(SrcLocation)>> on_modification; | ||||
| 
 | ||||
|   TmpVar(var_idx_t _idx, int _cls, TypeExpr* _type, SymDef* sym, SrcLocation loc); | ||||
|   TmpVar(var_idx_t _idx, bool _is_tmp_unnamed, TypeExpr* _type, SymDef* sym, SrcLocation loc); | ||||
|   void show(std::ostream& os, int omit_idx = 0) const; | ||||
|   void dump(std::ostream& os) const; | ||||
|   void set_location(SrcLocation loc); | ||||
|  | @ -586,9 +455,9 @@ struct CodeBlob { | |||
|     return res; | ||||
|   } | ||||
|   bool import_params(FormalArgList arg_list); | ||||
|   var_idx_t create_var(int cls, TypeExpr* var_type, SymDef* sym, SrcLocation loc); | ||||
|   var_idx_t create_var(bool is_tmp_unnamed, TypeExpr* var_type, SymDef* sym, SrcLocation loc); | ||||
|   var_idx_t create_tmp_var(TypeExpr* var_type, SrcLocation loc) { | ||||
|     return create_var(TmpVar::_Tmp, var_type, nullptr, loc); | ||||
|     return create_var(true, var_type, nullptr, loc); | ||||
|   } | ||||
|   int split_vars(bool strict = false); | ||||
|   bool compute_used_code_vars(); | ||||
|  | @ -631,7 +500,6 @@ struct CodeBlob { | |||
| 
 | ||||
| struct SymVal : SymValBase { | ||||
|   TypeExpr* sym_type; | ||||
|   bool auto_apply{false}; | ||||
|   SymVal(SymValKind kind, int idx, TypeExpr* sym_type = nullptr) | ||||
|       : SymValBase(kind, idx), sym_type(sym_type) { | ||||
|   } | ||||
|  | @ -702,16 +570,6 @@ struct SymValCodeFunc : SymValFunc { | |||
|   bool does_need_codegen() const; | ||||
| }; | ||||
| 
 | ||||
| struct SymValType : SymValBase { | ||||
|   TypeExpr* sym_type; | ||||
|   SymValType(SymValKind kind, int idx, TypeExpr* _stype = nullptr) : SymValBase(kind, idx), sym_type(_stype) { | ||||
|   } | ||||
|   ~SymValType() override = default; | ||||
|   TypeExpr* get_type() const { | ||||
|     return sym_type; | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| struct SymValGlobVar : SymValBase { | ||||
|   TypeExpr* sym_type; | ||||
|   int out_idx{0}; | ||||
|  | @ -762,7 +620,6 @@ struct SymValConst : SymValBase { | |||
| 
 | ||||
| // defined in parse-tolk.cpp
 | ||||
| td::Result<SrcFile*> locate_source_file(const std::string& rel_filename); | ||||
| void parse_source_file(SrcFile* file); | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|  | @ -792,7 +649,7 @@ struct Expr { | |||
|   }; | ||||
|   ExprCls cls; | ||||
|   int val{0}; | ||||
|   enum { _IsType = 1, _IsRvalue = 2, _IsLvalue = 4, _IsImpure = 32, _IsInsideParenthesis = 64 }; | ||||
|   enum { _IsType = 1, _IsRvalue = 2, _IsLvalue = 4, _IsImpure = 32 }; | ||||
|   int flags{0}; | ||||
|   SrcLocation here; | ||||
|   td::RefInt256 intval; | ||||
|  | @ -834,18 +691,23 @@ struct Expr { | |||
|   bool is_type() const { | ||||
|     return flags & _IsType; | ||||
|   } | ||||
|   bool is_inside_parenthesis() const { | ||||
|     return flags & _IsInsideParenthesis; | ||||
|   } | ||||
|   bool is_type_apply() const { | ||||
|     return cls == _TypeApply; | ||||
|   } | ||||
|   bool is_mktuple() const { | ||||
|     return cls == _MkTuple; | ||||
|   } | ||||
|   void chk_rvalue(const Lexer& lex) const;  // todo here and below: strange to pass Lexer
 | ||||
|   void chk_lvalue(const Lexer& lex) const; | ||||
|   bool deduce_type(const Lexer& lex); | ||||
|   void chk_rvalue() const { | ||||
|     if (!is_rvalue()) { | ||||
|       throw ParseError(here, "rvalue expected"); | ||||
|     } | ||||
|   } | ||||
|   void chk_lvalue() const { | ||||
|     if (!is_lvalue()) { | ||||
|       throw ParseError(here, "lvalue expected"); | ||||
|     } | ||||
|   } | ||||
|   bool deduce_type(); | ||||
|   void set_location(SrcLocation loc) { | ||||
|     here = loc; | ||||
|   } | ||||
|  |  | |||
							
								
								
									
										140
									
								
								tolk/type-expr.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										140
									
								
								tolk/type-expr.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,140 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <vector> | ||||
| #include <iostream> | ||||
| #include "lexer.h" | ||||
| 
 | ||||
| namespace tolk { | ||||
| 
 | ||||
| struct TypeExpr { | ||||
|   enum Kind { te_Unknown, te_Var, te_Indirect, te_Atomic, te_Tensor, te_Tuple, te_Map, te_ForAll }; | ||||
|   // todo not _
 | ||||
|   enum AtomicType { | ||||
|     _Int = tok_int, | ||||
|     _Cell = tok_cell, | ||||
|     _Slice = tok_slice, | ||||
|     _Builder = tok_builder, | ||||
|     _Cont = tok_cont, | ||||
|     _Tuple = tok_tuple, | ||||
|   }; | ||||
|   Kind constr; | ||||
|   int value; | ||||
|   int minw, maxw; | ||||
|   static constexpr int w_inf = 1023; | ||||
|   std::vector<TypeExpr*> args; | ||||
|   bool was_forall_var = false; | ||||
|   TypeExpr(Kind _constr, int _val = 0) : constr(_constr), value(_val), minw(0), maxw(w_inf) { | ||||
|   } | ||||
|   TypeExpr(Kind _constr, int _val, int width) : constr(_constr), value(_val), minw(width), maxw(width) { | ||||
|   } | ||||
|   TypeExpr(Kind _constr, std::vector<TypeExpr*> list) | ||||
|       : constr(_constr), value((int)list.size()), args(std::move(list)) { | ||||
|     compute_width(); | ||||
|   } | ||||
|   TypeExpr(Kind _constr, std::initializer_list<TypeExpr*> list) | ||||
|       : constr(_constr), value((int)list.size()), args(std::move(list)) { | ||||
|     compute_width(); | ||||
|   } | ||||
|   TypeExpr(Kind _constr, TypeExpr* elem0) : constr(_constr), value(1), args{elem0} { | ||||
|     compute_width(); | ||||
|   } | ||||
|   TypeExpr(Kind _constr, TypeExpr* elem0, std::vector<TypeExpr*> list) | ||||
|       : constr(_constr), value((int)list.size() + 1), args{elem0} { | ||||
|     args.insert(args.end(), list.begin(), list.end()); | ||||
|     compute_width(); | ||||
|   } | ||||
|   TypeExpr(Kind _constr, TypeExpr* elem0, std::initializer_list<TypeExpr*> list) | ||||
|       : constr(_constr), value((int)list.size() + 1), args{elem0} { | ||||
|     args.insert(args.end(), list.begin(), list.end()); | ||||
|     compute_width(); | ||||
|   } | ||||
|   bool is_atomic() const { | ||||
|     return constr == te_Atomic; | ||||
|   } | ||||
|   bool is_atomic(int v) const { | ||||
|     return constr == te_Atomic && value == v; | ||||
|   } | ||||
|   bool is_int() const { | ||||
|     return is_atomic(_Int); | ||||
|   } | ||||
|   bool is_var() const { | ||||
|     return constr == te_Var; | ||||
|   } | ||||
|   bool is_map() const { | ||||
|     return constr == te_Map; | ||||
|   } | ||||
|   bool is_tuple() const { | ||||
|     return constr == te_Tuple; | ||||
|   } | ||||
|   bool has_fixed_width() const { | ||||
|     return minw == maxw; | ||||
|   } | ||||
|   int get_width() const { | ||||
|     return has_fixed_width() ? minw : -1; | ||||
|   } | ||||
|   void compute_width(); | ||||
|   bool recompute_width(); | ||||
|   void show_width(std::ostream& os); | ||||
|   std::ostream& print(std::ostream& os, int prio = 0) const; | ||||
|   void replace_with(TypeExpr* te2); | ||||
|   int extract_components(std::vector<TypeExpr*>& comp_list); | ||||
|   bool equals_to(const TypeExpr* rhs) const; | ||||
|   bool has_unknown_inside() const; | ||||
|   static int holes, type_vars; | ||||
|   static TypeExpr* new_hole() { | ||||
|     return new TypeExpr{te_Unknown, ++holes}; | ||||
|   } | ||||
|   static TypeExpr* new_hole(int width) { | ||||
|     return new TypeExpr{te_Unknown, ++holes, width}; | ||||
|   } | ||||
|   static TypeExpr* new_unit() { | ||||
|     return new TypeExpr{te_Tensor, 0, 0}; | ||||
|   } | ||||
|   static TypeExpr* new_atomic(int value) { | ||||
|     return new TypeExpr{te_Atomic, value, 1}; | ||||
|   } | ||||
|   static TypeExpr* new_map(TypeExpr* from, TypeExpr* to); | ||||
|   static TypeExpr* new_func() { | ||||
|     return new_map(new_hole(), new_hole()); | ||||
|   } | ||||
|   static TypeExpr* new_tensor(std::vector<TypeExpr*> list, bool red = true) { | ||||
|     return red && list.size() == 1 ? list[0] : new TypeExpr{te_Tensor, std::move(list)}; | ||||
|   } | ||||
|   static TypeExpr* new_tensor(std::initializer_list<TypeExpr*> list) { | ||||
|     return new TypeExpr{te_Tensor, std::move(list)}; | ||||
|   } | ||||
|   static TypeExpr* new_tensor(TypeExpr* te1, TypeExpr* te2) { | ||||
|     return new_tensor({te1, te2}); | ||||
|   } | ||||
|   static TypeExpr* new_tensor(TypeExpr* te1, TypeExpr* te2, TypeExpr* te3) { | ||||
|     return new_tensor({te1, te2, te3}); | ||||
|   } | ||||
|   static TypeExpr* new_tuple(TypeExpr* arg0) { | ||||
|     return new TypeExpr{te_Tuple, arg0}; | ||||
|   } | ||||
|   static TypeExpr* new_tuple(std::vector<TypeExpr*> list, bool red = false) { | ||||
|     return new_tuple(new_tensor(std::move(list), red)); | ||||
|   } | ||||
|   static TypeExpr* new_tuple(std::initializer_list<TypeExpr*> list) { | ||||
|     return new_tuple(new_tensor(list)); | ||||
|   } | ||||
|   static TypeExpr* new_var() { | ||||
|     return new TypeExpr{te_Var, --type_vars, 1}; | ||||
|   } | ||||
|   static TypeExpr* new_var(int idx) { | ||||
|     return new TypeExpr{te_Var, idx, 1}; | ||||
|   } | ||||
|   static TypeExpr* new_forall(std::vector<TypeExpr*> list, TypeExpr* body) { | ||||
|     return new TypeExpr{te_ForAll, body, std::move(list)}; | ||||
|   } | ||||
|   static TypeExpr* new_forall(std::initializer_list<TypeExpr*> list, TypeExpr* body) { | ||||
|     return new TypeExpr{te_ForAll, body, std::move(list)}; | ||||
|   } | ||||
|   static bool remove_indirect(TypeExpr*& te, TypeExpr* forbidden = nullptr); | ||||
|   static std::vector<TypeExpr*> remove_forall(TypeExpr*& te); | ||||
|   static bool remove_forall_in(TypeExpr*& te, TypeExpr* te2, const std::vector<TypeExpr*>& new_vars); | ||||
| }; | ||||
| 
 | ||||
| std::ostream& operator<<(std::ostream& os, TypeExpr* type_expr); | ||||
| 
 | ||||
| } // namespace tolk
 | ||||
|  | @ -268,8 +268,6 @@ std::ostream& TypeExpr::print(std::ostream& os, int lex_level) const { | |||
|           return os << "cont"; | ||||
|         case _Tuple: | ||||
|           return os << "tuple"; | ||||
|         case _Type: | ||||
|           return os << "type"; | ||||
|         default: | ||||
|           return os << "atomic-type-" << value; | ||||
|       } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue