mirror of
https://github.com/ton-blockchain/ton
synced 2025-03-09 15:40:10 +00:00
[Tolk] v0.6 syntax: fun
, import
, var
, types on the right, etc.
Lots of changes, actually. Most noticeable are: - traditional //comments - #include -> import - a rule "import what you use" - ~ found -> !found (for -1/0) - null() -> null - is_null?(v) -> v == null - throw is a keyword - catch with swapped arguments - throw_if, throw_unless -> assert - do until -> do while - elseif -> else if - drop ifnot, elseifnot - drop rarely used operators A testing framework also appears here. All tests existed earlier, but due to significant syntax changes, their history is useless.
This commit is contained in:
parent
5a3e3595d6
commit
e2edadba92
133 changed files with 8196 additions and 2605 deletions
|
@ -8,7 +8,6 @@ set(TOLK_SOURCE
|
|||
ast.cpp
|
||||
ast-from-tokens.cpp
|
||||
pipe-discover-parse-sources.cpp
|
||||
pipe-handle-pragmas.cpp
|
||||
pipe-register-symbols.cpp
|
||||
pipe-ast-to-legacy.cpp
|
||||
pipe-find-unused-symbols.cpp
|
||||
|
|
|
@ -92,12 +92,6 @@ void VarDescr::show_value(std::ostream& os) const {
|
|||
if (val & _Neg) {
|
||||
os << '<';
|
||||
}
|
||||
if (val & _Bool) {
|
||||
os << 'B';
|
||||
}
|
||||
if (val & _Bit) {
|
||||
os << 'b';
|
||||
}
|
||||
if (val & _Even) {
|
||||
os << 'E';
|
||||
}
|
||||
|
@ -144,16 +138,10 @@ void VarDescr::set_const(td::RefInt256 value) {
|
|||
val |= _Nan | _NonZero;
|
||||
} else if (s < 0) {
|
||||
val |= _NonZero | _Neg | _Finite;
|
||||
if (*int_const == -1) {
|
||||
val |= _Bool;
|
||||
}
|
||||
} else if (s > 0) {
|
||||
val |= _NonZero | _Pos | _Finite;
|
||||
} else if (!s) {
|
||||
//if (*int_const == 1) {
|
||||
// val |= _Bit;
|
||||
//}
|
||||
val |= _Zero | _Neg | _Pos | _Finite | _Bool | _Bit;
|
||||
} else {
|
||||
val |= _Zero | _Neg | _Pos | _Finite;
|
||||
}
|
||||
if (val & _Finite) {
|
||||
val |= int_const->get_bit(0) ? _Odd : _Even;
|
||||
|
|
|
@ -883,7 +883,7 @@ void Op::set_impure(const CodeBlob &code) {
|
|||
// todo calling this function with `code` is a bad design (flags are assigned after Op is constructed)
|
||||
// later it's better to check this somewhere in code.emplace_back()
|
||||
if (code.flags & CodeBlob::_ForbidImpure) {
|
||||
throw ParseError(where, "An impure operation in a pure function");
|
||||
throw ParseError(where, "an impure operation in a pure function");
|
||||
}
|
||||
flags |= _Impure;
|
||||
}
|
||||
|
@ -891,7 +891,7 @@ void Op::set_impure(const CodeBlob &code) {
|
|||
void Op::set_impure(const CodeBlob &code, bool flag) {
|
||||
if (flag) {
|
||||
if (code.flags & CodeBlob::_ForbidImpure) {
|
||||
throw ParseError(where, "An impure operation in a pure function");
|
||||
throw ParseError(where, "an impure operation in a pure function");
|
||||
}
|
||||
flags |= _Impure;
|
||||
} else {
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -71,18 +71,16 @@ protected:
|
|||
using parent = ASTReplacerInFunctionBody;
|
||||
|
||||
virtual AnyV replace(V<ast_empty> v) { return replace_children(v); }
|
||||
virtual AnyV replace(V<ast_parenthesized_expr> 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_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_null_keyword> 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); }
|
||||
|
@ -91,26 +89,28 @@ protected:
|
|||
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_do_while_statement> v) { return replace_children(v); }
|
||||
virtual AnyV replace(V<ast_throw_statement> v) { return replace_children(v); }
|
||||
virtual AnyV replace(V<ast_assert_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_local_var> v) { return replace_children(v); }
|
||||
virtual AnyV replace(V<ast_local_vars_declaration> 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_parenthesized_expr: return replace(v->as<ast_parenthesized_expr>());
|
||||
case ast_tensor: return replace(v->as<ast_tensor>());
|
||||
case ast_tensor_square: return replace(v->as<ast_tensor_square>());
|
||||
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_null_keyword: return replace(v->as<ast_null_keyword>());
|
||||
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>());
|
||||
|
@ -119,9 +119,13 @@ protected:
|
|||
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_do_while_statement: return replace(v->as<ast_do_while_statement>());
|
||||
case ast_throw_statement: return replace(v->as<ast_throw_statement>());
|
||||
case ast_assert_statement: return replace(v->as<ast_assert_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_local_var: return replace(v->as<ast_local_var>());
|
||||
case ast_local_vars_declaration: return replace(v->as<ast_local_vars_declaration>());
|
||||
case ast_asm_body: return replace(v->as<ast_asm_body>());
|
||||
default:
|
||||
throw UnexpectedASTNodeType(v, "ASTReplacerInFunctionBody::visit");
|
||||
|
|
|
@ -32,22 +32,18 @@ namespace tolk {
|
|||
class ASTStringifier final : public ASTVisitor {
|
||||
constexpr static std::pair<ASTNodeType, const char*> name_pairs[] = {
|
||||
{ast_empty, "ast_empty"},
|
||||
{ast_parenthesized_expr, "ast_parenthesized_expr"},
|
||||
{ast_tensor, "ast_tensor"},
|
||||
{ast_tensor_square, "ast_tensor_square"},
|
||||
{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_null_keyword, "ast_null_keyword"},
|
||||
{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"},
|
||||
|
@ -56,24 +52,39 @@ class ASTStringifier final : public ASTVisitor {
|
|||
{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_do_while_statement, "ast_do_while_statement"},
|
||||
{ast_throw_statement, "ast_throw_statement"},
|
||||
{ast_assert_statement, "ast_assert_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_genericsT_item, "ast_genericsT_item"},
|
||||
{ast_genericsT_list, "ast_genericsT_list"},
|
||||
{ast_parameter, "ast_parameter"},
|
||||
{ast_parameter_list, "ast_parameter_list"},
|
||||
{ast_asm_body, "ast_asm_body"},
|
||||
{ast_annotation, "ast_annotation"},
|
||||
{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_local_var, "ast_local_var"},
|
||||
{ast_local_vars_declaration, "ast_local_vars_declaration"},
|
||||
{ast_tolk_required_version, "ast_tolk_required_version"},
|
||||
{ast_import_statement, "ast_import_statement"},
|
||||
{ast_tolk_file, "ast_tolk_file"},
|
||||
};
|
||||
|
||||
static_assert(std::size(name_pairs) == ast_tolk_file + 1, "name_pairs needs to be updated");
|
||||
|
||||
constexpr static std::pair<AnnotationKind, const char*> annotation_kinds[] = {
|
||||
{AnnotationKind::inline_simple, "@inline"},
|
||||
{AnnotationKind::inline_ref, "@inline_ref"},
|
||||
{AnnotationKind::method_id, "@method_id"},
|
||||
{AnnotationKind::pure, "@pure"},
|
||||
{AnnotationKind::deprecated, "@deprecated"},
|
||||
};
|
||||
|
||||
static_assert(std::size(annotation_kinds) == static_cast<size_t>(AnnotationKind::unknown), "annotation_kinds needs to be updated");
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -118,16 +129,6 @@ class ASTStringifier final : public ASTVisitor {
|
|||
return static_cast<std::string>(v->as<ast_global_var_declaration>()->get_identifier()->name);
|
||||
case ast_constant_declaration:
|
||||
return static_cast<std::string>(v->as<ast_constant_declaration>()->get_identifier()->name);
|
||||
case ast_type_expression: {
|
||||
std::ostringstream os;
|
||||
os << v->as<ast_type_expression>()->declared_type;
|
||||
return os.str();
|
||||
}
|
||||
case ast_variable_declaration: {
|
||||
std::ostringstream os;
|
||||
os << v->as<ast_variable_declaration>()->declared_type;
|
||||
return os.str();
|
||||
}
|
||||
case ast_dot_tilde_call:
|
||||
return static_cast<std::string>(v->as<ast_dot_tilde_call>()->method_name);
|
||||
case ast_unary_operator:
|
||||
|
@ -138,26 +139,34 @@ class ASTStringifier final : public ASTVisitor {
|
|||
return "↓" + std::to_string(v->as<ast_sequence>()->get_items().size());
|
||||
case ast_if_statement:
|
||||
return v->as<ast_if_statement>()->is_ifnot ? "ifnot" : "";
|
||||
case ast_argument: {
|
||||
case ast_annotation:
|
||||
return annotation_kinds[static_cast<int>(v->as<ast_annotation>()->kind)].second;
|
||||
case ast_parameter: {
|
||||
std::ostringstream os;
|
||||
os << v->as<ast_argument>()->arg_type;
|
||||
return static_cast<std::string>(v->as<ast_argument>()->get_identifier()->name) + ": " + os.str();
|
||||
os << v->as<ast_parameter>()->param_type;
|
||||
return static_cast<std::string>(v->as<ast_parameter>()->get_identifier()->name) + ": " + os.str();
|
||||
}
|
||||
case ast_function_declaration: {
|
||||
std::string arg_names;
|
||||
for (int i = 0; i < v->as<ast_function_declaration>()->get_num_args(); i++) {
|
||||
if (!arg_names.empty())
|
||||
arg_names += ",";
|
||||
arg_names += v->as<ast_function_declaration>()->get_arg(i)->get_identifier()->name;
|
||||
std::string param_names;
|
||||
for (int i = 0; i < v->as<ast_function_declaration>()->get_num_params(); i++) {
|
||||
if (!param_names.empty())
|
||||
param_names += ",";
|
||||
param_names += v->as<ast_function_declaration>()->get_param(i)->get_identifier()->name;
|
||||
}
|
||||
return "fun " + static_cast<std::string>(v->as<ast_function_declaration>()->get_identifier()->name) + "(" + arg_names + ")";
|
||||
return "fun " + static_cast<std::string>(v->as<ast_function_declaration>()->get_identifier()->name) + "(" + param_names + ")";
|
||||
}
|
||||
case ast_pragma_no_arg:
|
||||
return static_cast<std::string>(v->as<ast_pragma_no_arg>()->pragma_name);
|
||||
case ast_pragma_version:
|
||||
return static_cast<std::string>(v->as<ast_pragma_version>()->semver);
|
||||
case ast_include_statement:
|
||||
return static_cast<std::string>(v->as<ast_include_statement>()->get_file_leaf()->str_val);
|
||||
case ast_local_var: {
|
||||
std::ostringstream os;
|
||||
os << v->as<ast_local_var>()->declared_type;
|
||||
if (auto v_ident = v->as<ast_local_var>()->get_identifier()->try_as<ast_identifier>()) {
|
||||
return static_cast<std::string>(v_ident->name) + ":" + os.str();
|
||||
}
|
||||
return "_: " + os.str();
|
||||
}
|
||||
case ast_tolk_required_version:
|
||||
return static_cast<std::string>(v->as<ast_tolk_required_version>()->semver);
|
||||
case ast_import_statement:
|
||||
return static_cast<std::string>(v->as<ast_import_statement>()->get_file_leaf()->str_val);
|
||||
case ast_tolk_file:
|
||||
return v->as<ast_tolk_file>()->file->rel_filename;
|
||||
default:
|
||||
|
@ -191,22 +200,18 @@ public:
|
|||
void visit(AnyV v) override {
|
||||
switch (v->type) {
|
||||
case ast_empty: return handle_vertex(v->as<ast_empty>());
|
||||
case ast_parenthesized_expr: return handle_vertex(v->as<ast_parenthesized_expr>());
|
||||
case ast_tensor: return handle_vertex(v->as<ast_tensor>());
|
||||
case ast_tensor_square: return handle_vertex(v->as<ast_tensor_square>());
|
||||
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_null_keyword: return handle_vertex(v->as<ast_null_keyword>());
|
||||
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>());
|
||||
|
@ -215,18 +220,22 @@ public:
|
|||
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_do_while_statement: return handle_vertex(v->as<ast_do_while_statement>());
|
||||
case ast_throw_statement: return handle_vertex(v->as<ast_throw_statement>());
|
||||
case ast_assert_statement: return handle_vertex(v->as<ast_assert_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_genericsT_item: return handle_vertex(v->as<ast_genericsT_item>());
|
||||
case ast_genericsT_list: return handle_vertex(v->as<ast_genericsT_list>());
|
||||
case ast_parameter: return handle_vertex(v->as<ast_parameter>());
|
||||
case ast_parameter_list: return handle_vertex(v->as<ast_parameter_list>());
|
||||
case ast_asm_body: return handle_vertex(v->as<ast_asm_body>());
|
||||
case ast_annotation: return handle_vertex(v->as<ast_annotation>());
|
||||
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_local_var: return handle_vertex(v->as<ast_local_var>());
|
||||
case ast_local_vars_declaration: return handle_vertex(v->as<ast_local_vars_declaration>());
|
||||
case ast_tolk_required_version: return handle_vertex(v->as<ast_tolk_required_version>());
|
||||
case ast_import_statement: return handle_vertex(v->as<ast_import_statement>());
|
||||
case ast_tolk_file: return handle_vertex(v->as<ast_tolk_file>());
|
||||
default:
|
||||
throw UnexpectedASTNodeType(v, "ASTStringifier::visit");
|
||||
|
|
|
@ -67,18 +67,16 @@ protected:
|
|||
using parent = ASTVisitorFunctionBody;
|
||||
|
||||
virtual void visit(V<ast_empty> v) { return visit_children(v); }
|
||||
virtual void visit(V<ast_parenthesized_expr> 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_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_null_keyword> 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); }
|
||||
|
@ -87,26 +85,26 @@ protected:
|
|||
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_do_while_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_local_var> v) { return visit_children(v); }
|
||||
virtual void visit(V<ast_local_vars_declaration> 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_parenthesized_expr: return visit(v->as<ast_parenthesized_expr>());
|
||||
case ast_tensor: return visit(v->as<ast_tensor>());
|
||||
case ast_tensor_square: return visit(v->as<ast_tensor_square>());
|
||||
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_null_keyword: return visit(v->as<ast_null_keyword>());
|
||||
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>());
|
||||
|
@ -115,9 +113,13 @@ protected:
|
|||
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_do_while_statement: return visit(v->as<ast_do_while_statement>());
|
||||
case ast_throw_statement: return visit(v->as<ast_throw_statement>());
|
||||
case ast_assert_statement: return visit(v->as<ast_assert_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_local_var: return visit(v->as<ast_local_var>());
|
||||
case ast_local_vars_declaration: return visit(v->as<ast_local_vars_declaration>());
|
||||
case ast_asm_body: return visit(v->as<ast_asm_body>());
|
||||
default:
|
||||
throw UnexpectedASTNodeType(v, "ASTVisitorFunctionBody::visit");
|
||||
|
|
29
tolk/ast.cpp
29
tolk/ast.cpp
|
@ -49,25 +49,44 @@ 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 {
|
||||
AnnotationKind Vertex<ast_annotation>::parse_kind(std::string_view name) {
|
||||
if (name == "@pure") {
|
||||
return AnnotationKind::pure;
|
||||
}
|
||||
if (name == "@inline") {
|
||||
return AnnotationKind::inline_simple;
|
||||
}
|
||||
if (name == "@inline_ref") {
|
||||
return AnnotationKind::inline_ref;
|
||||
}
|
||||
if (name == "@method_id") {
|
||||
return AnnotationKind::method_id;
|
||||
}
|
||||
if (name == "@deprecated") {
|
||||
return AnnotationKind::deprecated;
|
||||
}
|
||||
return AnnotationKind::unknown;
|
||||
}
|
||||
|
||||
int Vertex<ast_genericsT_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) {
|
||||
if (children[idx] && children[idx]->as<ast_genericsT_item>()->nameT == nameT) {
|
||||
return static_cast<int>(idx);
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int Vertex<ast_argument_list>::lookup_idx(std::string_view arg_name) const {
|
||||
int Vertex<ast_parameter_list>::lookup_idx(std::string_view param_name) const {
|
||||
for (size_t idx = 0; idx < children.size(); ++idx) {
|
||||
if (children[idx] && children[idx]->as<ast_argument>()->get_identifier()->name == arg_name) {
|
||||
if (children[idx] && children[idx]->as<ast_parameter>()->get_identifier()->name == param_name) {
|
||||
return static_cast<int>(idx);
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void Vertex<ast_include_statement>::mutate_set_src_file(const SrcFile* file) const {
|
||||
void Vertex<ast_import_statement>::mutate_set_src_file(const SrcFile* file) const {
|
||||
const_cast<Vertex*>(this)->file = file;
|
||||
}
|
||||
|
||||
|
|
277
tolk/ast.h
277
tolk/ast.h
|
@ -60,22 +60,18 @@ namespace tolk {
|
|||
|
||||
enum ASTNodeType {
|
||||
ast_empty,
|
||||
ast_parenthesized_expr,
|
||||
ast_tensor,
|
||||
ast_tensor_square,
|
||||
ast_identifier,
|
||||
ast_int_const,
|
||||
ast_string_const,
|
||||
ast_bool_const,
|
||||
ast_nil_tuple,
|
||||
ast_null_keyword,
|
||||
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,
|
||||
|
@ -84,21 +80,34 @@ enum ASTNodeType {
|
|||
ast_sequence,
|
||||
ast_repeat_statement,
|
||||
ast_while_statement,
|
||||
ast_do_until_statement,
|
||||
ast_do_while_statement,
|
||||
ast_throw_statement,
|
||||
ast_assert_statement,
|
||||
ast_try_catch_statement,
|
||||
ast_if_statement,
|
||||
ast_forall_item,
|
||||
ast_forall_list,
|
||||
ast_argument,
|
||||
ast_argument_list,
|
||||
ast_genericsT_item,
|
||||
ast_genericsT_list,
|
||||
ast_parameter,
|
||||
ast_parameter_list,
|
||||
ast_asm_body,
|
||||
ast_annotation,
|
||||
ast_function_declaration,
|
||||
ast_pragma_no_arg,
|
||||
ast_pragma_version,
|
||||
ast_include_statement,
|
||||
ast_local_var,
|
||||
ast_local_vars_declaration,
|
||||
ast_tolk_required_version,
|
||||
ast_import_statement,
|
||||
ast_tolk_file,
|
||||
};
|
||||
|
||||
enum class AnnotationKind {
|
||||
inline_simple,
|
||||
inline_ref,
|
||||
method_id,
|
||||
pure,
|
||||
deprecated,
|
||||
unknown,
|
||||
};
|
||||
|
||||
struct ASTNodeBase;
|
||||
|
||||
using AnyV = const ASTNodeBase*;
|
||||
|
@ -210,6 +219,32 @@ struct Vertex<ast_empty> final : ASTNodeLeaf {
|
|||
: ASTNodeLeaf(ast_empty, loc) {}
|
||||
};
|
||||
|
||||
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_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_identifier> final : ASTNodeLeaf {
|
||||
std::string_view name;
|
||||
|
@ -244,45 +279,29 @@ struct Vertex<ast_bool_const> final : ASTNodeLeaf {
|
|||
};
|
||||
|
||||
template<>
|
||||
struct Vertex<ast_nil_tuple> final : ASTNodeLeaf {
|
||||
struct Vertex<ast_null_keyword> final : ASTNodeLeaf {
|
||||
explicit Vertex(SrcLocation loc)
|
||||
: ASTNodeLeaf(ast_nil_tuple, loc) {}
|
||||
: ASTNodeLeaf(ast_null_keyword, 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; }
|
||||
auto get_called_arg() const { return rhs->as<ast_tensor>(); }
|
||||
|
||||
Vertex(SrcLocation loc, AnyV lhs_f, AnyV arg)
|
||||
Vertex(SrcLocation loc, AnyV lhs_f, V<ast_tensor> 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 : ASTNodeUnary {
|
||||
TypeExpr* declared_type; // may be nullptr
|
||||
|
||||
auto get_identifier() const { return child->as<ast_identifier>(); }
|
||||
|
||||
Vertex(SrcLocation loc, V<ast_identifier> var_identifier, TypeExpr* declared_type)
|
||||
: ASTNodeUnary(ast_global_var_declaration, loc, var_identifier), 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)) {}
|
||||
Vertex(SrcLocation loc, V<ast_identifier> name_identifier, TypeExpr* declared_type)
|
||||
: ASTNodeUnary(ast_global_var_declaration, loc, name_identifier), declared_type(declared_type) {}
|
||||
};
|
||||
|
||||
template<>
|
||||
|
@ -292,16 +311,8 @@ struct Vertex<ast_constant_declaration> final : ASTNodeBinary {
|
|||
auto get_identifier() const { return lhs->as<ast_identifier>(); }
|
||||
AnyV get_init_value() const { return rhs; }
|
||||
|
||||
Vertex(SrcLocation loc, V<ast_identifier> const_identifier, TypeExpr* declared_type, AnyV init_value)
|
||||
: ASTNodeBinary(ast_constant_declaration, loc, const_identifier, init_value), 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)) {}
|
||||
Vertex(SrcLocation loc, V<ast_identifier> name_identifier, TypeExpr* declared_type, AnyV init_value)
|
||||
: ASTNodeBinary(ast_constant_declaration, loc, name_identifier, init_value), declared_type(declared_type) {}
|
||||
};
|
||||
|
||||
template<>
|
||||
|
@ -310,51 +321,15 @@ struct Vertex<ast_underscore> final : ASTNodeLeaf {
|
|||
: 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; }
|
||||
auto get_arg() const { return rhs->as<ast_tensor>(); }
|
||||
|
||||
Vertex(SrcLocation loc, std::string_view method_name, AnyV lhs, AnyV rhs)
|
||||
: ASTNodeBinary(ast_dot_tilde_call, loc, lhs, rhs), method_name(method_name) {}
|
||||
Vertex(SrcLocation loc, std::string_view method_name, AnyV lhs, V<ast_tensor> arg)
|
||||
: ASTNodeBinary(ast_dot_tilde_call, loc, lhs, arg), method_name(method_name) {}
|
||||
};
|
||||
|
||||
template<>
|
||||
|
@ -428,27 +403,46 @@ struct Vertex<ast_while_statement> final : ASTNodeBinary {
|
|||
};
|
||||
|
||||
template<>
|
||||
struct Vertex<ast_do_until_statement> final : ASTNodeBinary {
|
||||
struct Vertex<ast_do_while_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) {}
|
||||
: ASTNodeBinary(ast_do_while_statement, loc, body, cond) {}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Vertex<ast_throw_statement> final : ASTNodeBinary {
|
||||
AnyV get_thrown_code() const { return lhs; }
|
||||
AnyV get_thrown_arg() const { return rhs; } // may be ast_empty
|
||||
bool has_thrown_arg() const { return rhs->type != ast_empty; }
|
||||
|
||||
Vertex(SrcLocation loc, AnyV thrown_code, AnyV thrown_arg)
|
||||
: ASTNodeBinary(ast_throw_statement, loc, thrown_code, thrown_arg) {}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Vertex<ast_assert_statement> final : ASTNodeBinary {
|
||||
AnyV get_cond() const { return lhs; }
|
||||
AnyV get_thrown_code() const { return rhs; }
|
||||
|
||||
Vertex(SrcLocation loc, AnyV cond, AnyV thrown_code)
|
||||
: ASTNodeBinary(ast_assert_statement, loc, cond, thrown_code) {}
|
||||
};
|
||||
|
||||
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_expr() const { return children.at(1)->as<ast_tensor>(); } // (excNo, arg), always len 2
|
||||
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)
|
||||
Vertex(SrcLocation loc, V<ast_sequence> try_body, V<ast_tensor> 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;
|
||||
bool is_ifnot; // if(!cond), to generate more optimal fift code
|
||||
|
||||
AnyV get_cond() const { return children.at(0); }
|
||||
auto get_if_body() const { return children.at(1)->as<ast_sequence>(); }
|
||||
|
@ -459,33 +453,44 @@ struct Vertex<ast_if_statement> final : ASTNodeVararg {
|
|||
};
|
||||
|
||||
template<>
|
||||
struct Vertex<ast_forall_item> final : ASTNodeLeaf {
|
||||
struct Vertex<ast_genericsT_item> final : ASTNodeLeaf {
|
||||
TypeExpr* created_type; // used to keep same pointer, since TypeExpr::new_var(i) always allocates
|
||||
std::string nameT;
|
||||
std::string_view nameT;
|
||||
|
||||
Vertex(SrcLocation loc, TypeExpr* created_type, std::string nameT)
|
||||
: ASTNodeLeaf(ast_forall_item, loc), created_type(created_type), nameT(std::move(nameT)) {}
|
||||
Vertex(SrcLocation loc, TypeExpr* created_type, std::string_view nameT)
|
||||
: ASTNodeLeaf(ast_genericsT_item, loc), created_type(created_type), nameT(nameT) {}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Vertex<ast_forall_list> final : ASTNodeVararg {
|
||||
struct Vertex<ast_genericsT_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>(); }
|
||||
auto get_item(int i) const { return children.at(i)->as<ast_genericsT_item>(); }
|
||||
|
||||
Vertex(SrcLocation loc, std::vector<AnyV> forall_items)
|
||||
: ASTNodeVararg(ast_forall_list, loc, std::move(forall_items)) {}
|
||||
Vertex(SrcLocation loc, std::vector<AnyV> genericsT_items)
|
||||
: ASTNodeVararg(ast_genericsT_list, loc, std::move(genericsT_items)) {}
|
||||
|
||||
int lookup_idx(std::string_view nameT) const;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Vertex<ast_argument> final : ASTNodeUnary {
|
||||
TypeExpr* arg_type;
|
||||
struct Vertex<ast_parameter> final : ASTNodeUnary {
|
||||
TypeExpr* param_type;
|
||||
|
||||
auto get_identifier() const { return child->as<ast_identifier>(); }
|
||||
auto get_identifier() const { return child->as<ast_identifier>(); } // for underscore, its str_val is empty
|
||||
|
||||
Vertex(SrcLocation loc, V<ast_identifier> arg_identifier, TypeExpr* arg_type)
|
||||
: ASTNodeUnary(ast_argument, loc, arg_identifier), arg_type(arg_type) {}
|
||||
Vertex(SrcLocation loc, V<ast_identifier> name_identifier, TypeExpr* param_type)
|
||||
: ASTNodeUnary(ast_parameter, loc, name_identifier), param_type(param_type) {}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Vertex<ast_parameter_list> final : ASTNodeVararg {
|
||||
const std::vector<AnyV>& get_params() const { return children; }
|
||||
auto get_param(int i) const { return children.at(i)->as<ast_parameter>(); }
|
||||
|
||||
Vertex(SrcLocation loc, std::vector<AnyV> params)
|
||||
: ASTNodeVararg(ast_parameter_list, loc, std::move(params)) {}
|
||||
|
||||
int lookup_idx(std::string_view param_name) const;
|
||||
};
|
||||
|
||||
template<>
|
||||
|
@ -500,26 +505,48 @@ struct Vertex<ast_asm_body> final : ASTNodeVararg {
|
|||
};
|
||||
|
||||
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>(); }
|
||||
struct Vertex<ast_annotation> final : ASTNodeUnary {
|
||||
AnnotationKind kind;
|
||||
|
||||
Vertex(SrcLocation loc, std::vector<AnyV> args)
|
||||
: ASTNodeVararg(ast_argument_list, loc, std::move(args)) {}
|
||||
auto get_arg() const { return child->as<ast_tensor>(); }
|
||||
|
||||
int lookup_idx(std::string_view arg_name) const;
|
||||
static AnnotationKind parse_kind(std::string_view name);
|
||||
|
||||
Vertex(SrcLocation loc, AnnotationKind kind, V<ast_tensor> arg_probably_empty)
|
||||
: ASTNodeUnary(ast_annotation, loc, arg_probably_empty), kind(kind) {}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Vertex<ast_local_var> final : ASTNodeUnary {
|
||||
TypeExpr* declared_type;
|
||||
bool marked_as_redef; // var (existing_var redef, new_var: int) = ...
|
||||
|
||||
AnyV get_identifier() const { return child; } // ast_identifier / ast_underscore
|
||||
|
||||
Vertex(SrcLocation loc, AnyV name_identifier, TypeExpr* declared_type, bool marked_as_redef)
|
||||
: ASTNodeUnary(ast_local_var, loc, name_identifier), declared_type(declared_type), marked_as_redef(marked_as_redef) {}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Vertex<ast_local_vars_declaration> final : ASTNodeBinary {
|
||||
AnyV get_lhs() const { return lhs; } // ast_local_var / ast_tensor / ast_tensor_square
|
||||
AnyV get_assigned_val() const { return rhs; }
|
||||
|
||||
Vertex(SrcLocation loc, AnyV lhs, AnyV assigned_val)
|
||||
: ASTNodeBinary(ast_local_vars_declaration, loc, lhs, assigned_val) {}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Vertex<ast_function_declaration> final : ASTNodeVararg {
|
||||
auto get_identifier() const { return children.at(0)->as<ast_identifier>(); }
|
||||
int get_num_args() const { return children.at(1)->as<ast_argument_list>()->size(); }
|
||||
auto get_arg_list() const { return children.at(1)->as<ast_argument_list>(); }
|
||||
auto get_arg(int i) const { return children.at(1)->as<ast_argument_list>()->get_arg(i); }
|
||||
int get_num_params() const { return children.at(1)->as<ast_parameter_list>()->size(); }
|
||||
auto get_param_list() const { return children.at(1)->as<ast_parameter_list>(); }
|
||||
auto get_param(int i) const { return children.at(1)->as<ast_parameter_list>()->get_param(i); }
|
||||
AnyV get_body() const { return children.at(2); } // ast_sequence / ast_asm_body
|
||||
|
||||
TypeExpr* ret_type = nullptr;
|
||||
V<ast_forall_list> forall_list = nullptr;
|
||||
V<ast_genericsT_list> genericsT_list = nullptr;
|
||||
bool is_entrypoint = false;
|
||||
bool marked_as_pure = false;
|
||||
bool marked_as_builtin = false;
|
||||
bool marked_as_get_method = false;
|
||||
|
@ -529,29 +556,21 @@ struct Vertex<ast_function_declaration> final : ASTNodeVararg {
|
|||
|
||||
bool is_asm_function() const { return children.at(2)->type == ast_asm_body; }
|
||||
|
||||
Vertex(SrcLocation loc, V<ast_identifier> name_identifier, V<ast_argument_list> args, AnyV body)
|
||||
: ASTNodeVararg(ast_function_declaration, loc, {name_identifier, args, body}) {}
|
||||
Vertex(SrcLocation loc, V<ast_identifier> name_identifier, V<ast_parameter_list> parameters, AnyV body)
|
||||
: ASTNodeVararg(ast_function_declaration, loc, {name_identifier, parameters, body}) {}
|
||||
};
|
||||
|
||||
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 {
|
||||
struct Vertex<ast_tolk_required_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) {}
|
||||
: ASTNodeLeaf(ast_tolk_required_version, loc), cmp_tok(cmp_tok), semver(semver) {}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Vertex<ast_include_statement> final : ASTNodeUnary {
|
||||
struct Vertex<ast_import_statement> final : ASTNodeUnary {
|
||||
const SrcFile* file = nullptr; // assigned after includes have been resolved
|
||||
|
||||
auto get_file_leaf() const { return child->as<ast_string_const>(); }
|
||||
|
@ -561,7 +580,7 @@ struct Vertex<ast_include_statement> final : ASTNodeUnary {
|
|||
void mutate_set_src_file(const SrcFile* file) const;
|
||||
|
||||
Vertex(SrcLocation loc, V<ast_string_const> file_name)
|
||||
: ASTNodeUnary(ast_include_statement, loc, file_name) {}
|
||||
: ASTNodeUnary(ast_import_statement, loc, file_name) {}
|
||||
};
|
||||
|
||||
template<>
|
||||
|
|
|
@ -91,10 +91,6 @@ int emulate_negate(int a) {
|
|||
if ((a & f) && (~a & f)) {
|
||||
a ^= f;
|
||||
}
|
||||
f = VarDescr::_Bit | VarDescr::_Bool;
|
||||
if ((a & f) && (~a & f)) {
|
||||
a ^= f;
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
|
@ -129,9 +125,9 @@ int emulate_sub(int a, int b) {
|
|||
}
|
||||
|
||||
int emulate_mul(int a, int b) {
|
||||
if ((b & (VarDescr::_NonZero | VarDescr::_Bit)) == (VarDescr::_NonZero | VarDescr::_Bit)) {
|
||||
if ((b & VarDescr::ConstOne) == VarDescr::ConstOne) {
|
||||
return a;
|
||||
} else if ((a & (VarDescr::_NonZero | VarDescr::_Bit)) == (VarDescr::_NonZero | VarDescr::_Bit)) {
|
||||
} else if ((a & VarDescr::ConstOne) == VarDescr::ConstOne) {
|
||||
return b;
|
||||
}
|
||||
int u = a & b, v = a | b;
|
||||
|
@ -151,11 +147,6 @@ int emulate_mul(int a, int b) {
|
|||
} else if (!(~v & (VarDescr::_Pos | VarDescr::_Neg))) {
|
||||
r |= VarDescr::_Neg;
|
||||
}
|
||||
if (u & (VarDescr::_Bit | VarDescr::_Bool)) {
|
||||
r |= VarDescr::_Bit;
|
||||
} else if (!(~v & (VarDescr::_Bit | VarDescr::_Bool))) {
|
||||
r |= VarDescr::_Bool;
|
||||
}
|
||||
r |= v & VarDescr::_Even;
|
||||
r |= u & (VarDescr::_Odd | VarDescr::_NonZero);
|
||||
return r;
|
||||
|
@ -172,7 +163,6 @@ int emulate_bitwise_and(int a, int b) {
|
|||
return VarDescr::ConstZero;
|
||||
}
|
||||
r |= both & (VarDescr::_Even | VarDescr::_Odd);
|
||||
r |= both & (VarDescr::_Bit | VarDescr::_Bool);
|
||||
if (both & VarDescr::_Odd) {
|
||||
r |= VarDescr::_NonZero;
|
||||
}
|
||||
|
@ -228,7 +218,7 @@ int emulate_bitwise_not(int a) {
|
|||
if ((a2 & f) && (~a2 & f)) {
|
||||
a2 ^= f;
|
||||
}
|
||||
a2 &= ~(VarDescr::_Zero | VarDescr::_NonZero | VarDescr::_Bit | VarDescr::_Pos | VarDescr::_Neg);
|
||||
a2 &= ~(VarDescr::_Zero | VarDescr::_NonZero | VarDescr::_Pos | VarDescr::_Neg);
|
||||
if ((a & VarDescr::_Neg) && (a & VarDescr::_NonZero)) {
|
||||
a2 |= VarDescr::_Pos;
|
||||
}
|
||||
|
@ -251,9 +241,9 @@ int emulate_lshift(int a, int b) {
|
|||
}
|
||||
|
||||
int emulate_div(int a, int b) {
|
||||
if ((b & (VarDescr::_NonZero | VarDescr::_Bit)) == (VarDescr::_NonZero | VarDescr::_Bit)) {
|
||||
if ((b & VarDescr::ConstOne) == VarDescr::ConstOne) {
|
||||
return a;
|
||||
} else if ((b & (VarDescr::_NonZero | VarDescr::_Bool)) == (VarDescr::_NonZero | VarDescr::_Bool)) {
|
||||
} else if ((b & VarDescr::ConstOne) == VarDescr::ConstOne) {
|
||||
return emulate_negate(a);
|
||||
}
|
||||
if (b & VarDescr::_Zero) {
|
||||
|
@ -276,11 +266,6 @@ int emulate_div(int a, int b) {
|
|||
} else if (!(~v & (VarDescr::_Pos | VarDescr::_Neg))) {
|
||||
r |= VarDescr::_Neg;
|
||||
}
|
||||
if (u & (VarDescr::_Bit | VarDescr::_Bool)) {
|
||||
r |= VarDescr::_Bit;
|
||||
} else if (!(~v & (VarDescr::_Bit | VarDescr::_Bool))) {
|
||||
r |= VarDescr::_Bool;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
|
@ -297,9 +282,7 @@ int emulate_rshift(int a, int b) {
|
|||
}
|
||||
|
||||
int emulate_mod(int a, int b, int round_mode = -1) {
|
||||
if ((b & (VarDescr::_NonZero | VarDescr::_Bit)) == (VarDescr::_NonZero | VarDescr::_Bit)) {
|
||||
return VarDescr::ConstZero;
|
||||
} else if ((b & (VarDescr::_NonZero | VarDescr::_Bool)) == (VarDescr::_NonZero | VarDescr::_Bool)) {
|
||||
if ((b & VarDescr::ConstOne) == VarDescr::ConstOne) {
|
||||
return VarDescr::ConstZero;
|
||||
}
|
||||
if (b & VarDescr::_Zero) {
|
||||
|
@ -321,14 +304,6 @@ int emulate_mod(int a, int b, int round_mode = -1) {
|
|||
} else if (round_mode > 0) {
|
||||
r |= emulate_negate(b) & (VarDescr::_Pos | VarDescr::_Neg);
|
||||
}
|
||||
if (a & (VarDescr::_Bit | VarDescr::_Bool)) {
|
||||
if (r & VarDescr::_Pos) {
|
||||
r |= VarDescr::_Bit;
|
||||
}
|
||||
if (r & VarDescr::_Neg) {
|
||||
r |= VarDescr::_Bool;
|
||||
}
|
||||
}
|
||||
if (b & VarDescr::_Even) {
|
||||
r |= a & (VarDescr::_Even | VarDescr::_Odd);
|
||||
}
|
||||
|
@ -513,6 +488,18 @@ AsmOp compile_unary_plus(std::vector<VarDescr>& res, std::vector<VarDescr>& args
|
|||
return AsmOp::Nop();
|
||||
}
|
||||
|
||||
AsmOp compile_logical_not(std::vector<VarDescr>& res, std::vector<VarDescr>& args, SrcLocation where) {
|
||||
tolk_assert(res.size() == 1 && args.size() == 1);
|
||||
VarDescr &r = res[0], &x = args[0];
|
||||
if (x.is_int_const()) {
|
||||
r.set_const(x.int_const == 0 ? -1 : 0);
|
||||
x.unused();
|
||||
return push_const(r.int_const);
|
||||
}
|
||||
r.val = VarDescr::ValBool;
|
||||
return exec_op("0 EQINT", 1);
|
||||
}
|
||||
|
||||
AsmOp compile_bitwise_and(std::vector<VarDescr>& res, std::vector<VarDescr>& args, SrcLocation where) {
|
||||
tolk_assert(res.size() == 1 && args.size() == 2);
|
||||
VarDescr &r = res[0], &x = args[0], &y = args[1];
|
||||
|
@ -976,9 +963,14 @@ AsmOp compile_throw(std::vector<VarDescr>& res, std::vector<VarDescr>& args, Src
|
|||
}
|
||||
}
|
||||
|
||||
AsmOp compile_cond_throw(std::vector<VarDescr>& res, std::vector<VarDescr>& args, bool mode) {
|
||||
tolk_assert(res.empty() && args.size() == 2);
|
||||
VarDescr &x = args[0], &y = args[1];
|
||||
AsmOp compile_throw_if_unless(std::vector<VarDescr>& res, std::vector<VarDescr>& args) {
|
||||
tolk_assert(res.empty() && args.size() == 3);
|
||||
VarDescr &x = args[0], &y = args[1], &z = args[2];
|
||||
if (!z.always_true() && !z.always_false()) {
|
||||
throw Fatal("invalid usage of built-in symbol");
|
||||
}
|
||||
bool mode = z.always_true();
|
||||
z.unused();
|
||||
std::string suff = (mode ? "IF" : "IFNOT");
|
||||
bool skip_cond = false;
|
||||
if (y.always_true() || y.always_false()) {
|
||||
|
@ -1008,27 +1000,6 @@ AsmOp compile_throw_arg(std::vector<VarDescr>& res, std::vector<VarDescr>& args,
|
|||
}
|
||||
}
|
||||
|
||||
AsmOp compile_cond_throw_arg(std::vector<VarDescr>& res, std::vector<VarDescr>& args, bool mode) {
|
||||
tolk_assert(res.empty() && args.size() == 3);
|
||||
VarDescr &x = args[1], &y = args[2];
|
||||
std::string suff = (mode ? "IF" : "IFNOT");
|
||||
bool skip_cond = false;
|
||||
if (y.always_true() || y.always_false()) {
|
||||
y.unused();
|
||||
skip_cond = true;
|
||||
if (y.always_true() != mode) {
|
||||
x.unused();
|
||||
return AsmOp::Nop();
|
||||
}
|
||||
}
|
||||
if (x.is_int_const() && x.int_const->unsigned_fits_bits(11)) {
|
||||
x.unused();
|
||||
return skip_cond ? exec_arg_op("THROWARG", x.int_const, 1, 0) : exec_arg_op("THROWARG"s + suff, x.int_const, 2, 0);
|
||||
} else {
|
||||
return skip_cond ? exec_op("THROWARGANY", 2, 0) : exec_op("THROWARGANY"s + suff, 3, 0);
|
||||
}
|
||||
}
|
||||
|
||||
AsmOp compile_bool_const(std::vector<VarDescr>& res, std::vector<VarDescr>& args, bool val) {
|
||||
tolk_assert(res.size() == 1 && args.empty());
|
||||
VarDescr& r = res[0];
|
||||
|
@ -1098,15 +1069,9 @@ AsmOp compile_tuple_at(std::vector<VarDescr>& res, std::vector<VarDescr>& args,
|
|||
return exec_op("INDEXVAR", 2, 1);
|
||||
}
|
||||
|
||||
// int null?(X arg)
|
||||
// fun __isNull<X>(X arg): int
|
||||
AsmOp compile_is_null(std::vector<VarDescr>& res, std::vector<VarDescr>& args, SrcLocation) {
|
||||
tolk_assert(args.size() == 1 && res.size() == 1);
|
||||
auto &x = args[0], &r = res[0];
|
||||
if (x.always_null() || x.always_not_null()) {
|
||||
x.unused();
|
||||
r.set_const(x.always_null() ? -1 : 0);
|
||||
return push_const(r.int_const);
|
||||
}
|
||||
res[0].val = VarDescr::ValBool;
|
||||
return exec_op("ISNULL", 1, 1);
|
||||
}
|
||||
|
@ -1131,7 +1096,6 @@ void define_builtins() {
|
|||
auto XY = TypeExpr::new_tensor({X, Y});
|
||||
auto arith_bin_op = TypeExpr::new_map(Int2, Int);
|
||||
auto arith_un_op = TypeExpr::new_map(Int, Int);
|
||||
auto impure_bin_op = TypeExpr::new_map(Int2, Unit);
|
||||
auto impure_un_op = TypeExpr::new_map(Int, Unit);
|
||||
auto fetch_int_op = TypeExpr::new_map(SliceInt, SliceInt);
|
||||
auto prefetch_int_op = TypeExpr::new_map(SliceInt, Int);
|
||||
|
@ -1142,7 +1106,6 @@ void define_builtins() {
|
|||
auto prefetch_slice_op = TypeExpr::new_map(SliceInt, Slice);
|
||||
//auto arith_null_op = TypeExpr::new_map(TypeExpr::new_unit(), Int);
|
||||
auto throw_arg_op = TypeExpr::new_forall({X}, TypeExpr::new_map(TypeExpr::new_tensor({X, Int}), Unit));
|
||||
auto cond_throw_arg_op = TypeExpr::new_forall({X}, TypeExpr::new_map(TypeExpr::new_tensor({X, Int, Int}), Unit));
|
||||
|
||||
// prevent unused vars warnings (there vars are created to acquire initial id of TypeExpr::value)
|
||||
static_cast<void>(Z);
|
||||
|
@ -1158,9 +1121,6 @@ void define_builtins() {
|
|||
define_builtin_func("_~/_", arith_bin_op, std::bind(compile_div, _1, _2, _3, 0));
|
||||
define_builtin_func("_^/_", arith_bin_op, std::bind(compile_div, _1, _2, _3, 1));
|
||||
define_builtin_func("_%_", arith_bin_op, std::bind(compile_mod, _1, _2, _3, -1));
|
||||
define_builtin_func("_~%_", arith_bin_op, std::bind(compile_mod, _1, _2, _3, 0));
|
||||
define_builtin_func("_^%_", arith_bin_op, std::bind(compile_mod, _1, _2, _3, 1));
|
||||
define_builtin_func("_/%_", TypeExpr::new_map(Int2, Int2), AsmOp::Custom("DIVMOD", 2, 2));
|
||||
define_builtin_func("divmod", TypeExpr::new_map(Int2, Int2), AsmOp::Custom("DIVMOD", 2, 2));
|
||||
define_builtin_func("~divmod", TypeExpr::new_map(Int2, Int2), AsmOp::Custom("DIVMOD", 2, 2));
|
||||
define_builtin_func("moddiv", TypeExpr::new_map(Int2, Int2), AsmOp::Custom("DIVMOD", 2, 2), {}, {1, 0});
|
||||
|
@ -1169,23 +1129,18 @@ void define_builtins() {
|
|||
define_builtin_func("_>>_", arith_bin_op, std::bind(compile_rshift, _1, _2, _3, -1));
|
||||
define_builtin_func("_~>>_", arith_bin_op, std::bind(compile_rshift, _1, _2, _3, 0));
|
||||
define_builtin_func("_^>>_", arith_bin_op, std::bind(compile_rshift, _1, _2, _3, 1));
|
||||
define_builtin_func("!_", arith_un_op, compile_logical_not);
|
||||
define_builtin_func("~_", arith_un_op, compile_bitwise_not);
|
||||
define_builtin_func("_&_", arith_bin_op, compile_bitwise_and);
|
||||
define_builtin_func("_|_", arith_bin_op, compile_bitwise_or);
|
||||
define_builtin_func("_^_", arith_bin_op, compile_bitwise_xor);
|
||||
define_builtin_func("~_", arith_un_op, compile_bitwise_not);
|
||||
define_builtin_func("^_+=_", arith_bin_op, compile_add);
|
||||
define_builtin_func("^_-=_", arith_bin_op, compile_sub);
|
||||
define_builtin_func("^_*=_", arith_bin_op, compile_mul);
|
||||
define_builtin_func("^_/=_", arith_bin_op, std::bind(compile_div, _1, _2, _3, -1));
|
||||
define_builtin_func("^_~/=_", arith_bin_op, std::bind(compile_div, _1, _2, _3, 0));
|
||||
define_builtin_func("^_^/=_", arith_bin_op, std::bind(compile_div, _1, _2, _3, 1));
|
||||
define_builtin_func("^_%=_", arith_bin_op, std::bind(compile_mod, _1, _2, _3, -1));
|
||||
define_builtin_func("^_~%=_", arith_bin_op, std::bind(compile_mod, _1, _2, _3, 0));
|
||||
define_builtin_func("^_^%=_", arith_bin_op, std::bind(compile_mod, _1, _2, _3, 1));
|
||||
define_builtin_func("^_<<=_", arith_bin_op, compile_lshift);
|
||||
define_builtin_func("^_>>=_", arith_bin_op, std::bind(compile_rshift, _1, _2, _3, -1));
|
||||
define_builtin_func("^_~>>=_", arith_bin_op, std::bind(compile_rshift, _1, _2, _3, 0));
|
||||
define_builtin_func("^_^>>=_", arith_bin_op, std::bind(compile_rshift, _1, _2, _3, 1));
|
||||
define_builtin_func("^_&=_", arith_bin_op, compile_bitwise_and);
|
||||
define_builtin_func("^_|=_", arith_bin_op, compile_bitwise_or);
|
||||
define_builtin_func("^_^=_", arith_bin_op, compile_bitwise_xor);
|
||||
|
@ -1200,17 +1155,13 @@ 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_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_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);
|
||||
define_builtin_func("throw_unless", impure_bin_op, std::bind(compile_cond_throw, _1, _2, false), true);
|
||||
define_builtin_func("throw_arg", throw_arg_op, compile_throw_arg, true);
|
||||
define_builtin_func("throw_arg_if", cond_throw_arg_op, std::bind(compile_cond_throw_arg, _1, _2, true), true);
|
||||
define_builtin_func("throw_arg_unless", cond_throw_arg_op, std::bind(compile_cond_throw_arg, _1, _2, false), true);
|
||||
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", TypeExpr::new_forall({X}, TypeExpr::new_map(TypeExpr::new_unit(), X)), AsmOp::Const("PUSHNULL"));
|
||||
define_builtin_func("__isNull", 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_arg", throw_arg_op, compile_throw_arg, true);
|
||||
define_builtin_func("__throw_if_unless", TypeExpr::new_map(Int3, Unit), std::bind(compile_throw_if_unless, _1, _2), true);
|
||||
define_builtin_func("load_int", fetch_int_op, std::bind(compile_fetch_int, _1, _2, true, true), {}, {1, 0});
|
||||
define_builtin_func("load_uint", fetch_int_op, std::bind(compile_fetch_int, _1, _2, true, false), {}, {1, 0});
|
||||
define_builtin_func("preload_int", prefetch_int_op, std::bind(compile_fetch_int, _1, _2, false, true));
|
||||
|
|
|
@ -497,11 +497,10 @@ bool Op::generate_code_step(Stack& stack) {
|
|||
asm_fv->compile(stack.o, res, args, where); // compile res := f (args)
|
||||
} else {
|
||||
auto fv = dynamic_cast<const SymValCodeFunc*>(fun_ref->value);
|
||||
// todo can be fv == nullptr?
|
||||
std::string name = G.symbols.get_name(fun_ref->sym_idx);
|
||||
if (fv && (fv->is_inline() || fv->is_inline_ref())) {
|
||||
if (fv->is_inline() || fv->is_inline_ref()) {
|
||||
stack.o << AsmOp::Custom(name + " INLINECALLDICT", (int)right.size(), (int)left.size());
|
||||
} else if (fv && fv->code && fv->code->require_callxargs) {
|
||||
} else if (fv->code && fv->code->require_callxargs) {
|
||||
stack.o << AsmOp::Custom(name + (" PREPAREDICT"), 0, 2);
|
||||
exec_callxargs((int)right.size() + 1, (int)left.size());
|
||||
} else {
|
||||
|
|
|
@ -15,33 +15,42 @@
|
|||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "compiler-state.h"
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
namespace tolk {
|
||||
|
||||
std::string tolk_version{"0.5.0"};
|
||||
|
||||
CompilerState G; // the only mutable global variable in tolk internals
|
||||
|
||||
void GlobalPragma::enable(SrcLocation loc) {
|
||||
if (deprecated_from_v_) {
|
||||
loc.show_warning(PSTRING() << "#pragma " << name_ <<
|
||||
" is deprecated since Tolk v" << deprecated_from_v_ <<
|
||||
". Please, remove this line from your code.");
|
||||
return;
|
||||
}
|
||||
if (!loc.get_src_file()->is_entrypoint_file()) {
|
||||
// todo generally it's not true; rework pragmas completely
|
||||
loc.show_warning(PSTRING() << "#pragma " << name_ <<
|
||||
" should be used in the main file only.");
|
||||
}
|
||||
|
||||
enabled_ = true;
|
||||
void ExperimentalOption::mark_deprecated(const char* deprecated_from_v, const char* deprecated_reason) {
|
||||
this->deprecated_from_v = deprecated_from_v;
|
||||
this->deprecated_reason = deprecated_reason;
|
||||
}
|
||||
|
||||
void GlobalPragma::always_on_and_deprecated(const char *deprecated_from_v) {
|
||||
deprecated_from_v_ = deprecated_from_v;
|
||||
enabled_ = true;
|
||||
void CompilerSettings::enable_experimental_option(std::string_view name) {
|
||||
ExperimentalOption* to_enable = nullptr;
|
||||
|
||||
if (name == remove_unused_functions.name) {
|
||||
to_enable = &remove_unused_functions;
|
||||
}
|
||||
|
||||
if (to_enable == nullptr) {
|
||||
std::cerr << "unknown experimental option: " << name << std::endl;
|
||||
} else if (to_enable->deprecated_from_v) {
|
||||
std::cerr << "experimental option " << name << " "
|
||||
<< "is deprecated since Tolk v" << to_enable->deprecated_from_v
|
||||
<< ": " << to_enable->deprecated_reason << std::endl;
|
||||
} else {
|
||||
to_enable->enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
void CompilerSettings::parse_experimental_options_cmd_arg(const std::string& cmd_arg) {
|
||||
std::istringstream stream(cmd_arg);
|
||||
std::string token;
|
||||
while (std::getline(stream, token, ',')) {
|
||||
enable_experimental_option(token);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace tolk
|
||||
|
|
|
@ -24,21 +24,21 @@
|
|||
|
||||
namespace tolk {
|
||||
|
||||
extern std::string tolk_version;
|
||||
// with cmd option -x, the user can pass experimental options to use
|
||||
class ExperimentalOption {
|
||||
friend struct CompilerSettings;
|
||||
|
||||
class GlobalPragma {
|
||||
std::string name_;
|
||||
bool enabled_ = false;
|
||||
const char* deprecated_from_v_ = nullptr;
|
||||
const std::string_view name;
|
||||
bool enabled = false;
|
||||
const char* deprecated_from_v = nullptr; // when an option becomes deprecated (after the next compiler release),
|
||||
const char* deprecated_reason = nullptr; // but the user still passes it, we'll warn to stderr
|
||||
|
||||
public:
|
||||
explicit GlobalPragma(std::string name) : name_(std::move(name)) { }
|
||||
explicit ExperimentalOption(std::string_view name) : name(name) {}
|
||||
|
||||
const std::string& name() const { return name_; }
|
||||
void mark_deprecated(const char* deprecated_from_v, const char* deprecated_reason);
|
||||
|
||||
bool enabled() const { return enabled_; }
|
||||
void enable(SrcLocation loc);
|
||||
void always_on_and_deprecated(const char* deprecated_from_v);
|
||||
explicit operator bool() const { return enabled; }
|
||||
};
|
||||
|
||||
// CompilerSettings contains settings that can be passed via cmd line or (partially) wasm envelope.
|
||||
|
@ -58,6 +58,11 @@ struct CompilerSettings {
|
|||
std::string stdlib_filename;
|
||||
|
||||
FsReadCallback read_callback;
|
||||
|
||||
ExperimentalOption remove_unused_functions{"remove-unused-functions"};
|
||||
|
||||
void enable_experimental_option(std::string_view name);
|
||||
void parse_experimental_options_cmd_arg(const std::string& cmd_arg);
|
||||
};
|
||||
|
||||
// CompilerState contains a mutable state that is changed while the compilation is going on.
|
||||
|
@ -78,9 +83,6 @@ struct CompilerState {
|
|||
AllRegisteredSrcFiles all_src_files;
|
||||
|
||||
std::string generated_from;
|
||||
GlobalPragma pragma_allow_post_modification{"allow-post-modification"};
|
||||
GlobalPragma pragma_compute_asm_ltr{"compute-asm-ltr"};
|
||||
GlobalPragma pragma_remove_unused_functions{"remove-unused-functions"};
|
||||
|
||||
bool is_verbosity(int gt_eq) const { return settings.verbosity >= gt_eq; }
|
||||
};
|
||||
|
|
|
@ -148,8 +148,7 @@ bool Expr::deduce_type() {
|
|||
int Expr::define_new_vars(CodeBlob& code) {
|
||||
switch (cls) {
|
||||
case _Tensor:
|
||||
case _MkTuple:
|
||||
case _TypeApply: {
|
||||
case _MkTuple: {
|
||||
int res = 0;
|
||||
for (const auto& x : args) {
|
||||
res += x->define_new_vars(code);
|
||||
|
@ -174,8 +173,7 @@ int Expr::define_new_vars(CodeBlob& code) {
|
|||
int Expr::predefine_vars() {
|
||||
switch (cls) {
|
||||
case _Tensor:
|
||||
case _MkTuple:
|
||||
case _TypeApply: {
|
||||
case _MkTuple: {
|
||||
int res = 0;
|
||||
for (const auto& x : args) {
|
||||
res += x->predefine_vars();
|
||||
|
@ -210,12 +208,6 @@ void add_set_globs(CodeBlob& code, std::vector<std::pair<SymDef*, var_idx_t>>& g
|
|||
}
|
||||
|
||||
std::vector<var_idx_t> pre_compile_let(CodeBlob& code, Expr* lhs, Expr* rhs, SrcLocation here) {
|
||||
while (lhs->is_type_apply()) {
|
||||
lhs = lhs->args.at(0);
|
||||
}
|
||||
while (rhs->is_type_apply()) {
|
||||
rhs = rhs->args.at(0);
|
||||
}
|
||||
if (lhs->is_mktuple()) {
|
||||
if (rhs->is_mktuple()) {
|
||||
return pre_compile_let(code, lhs->args.at(0), rhs->args.at(0), here);
|
||||
|
@ -301,7 +293,7 @@ std::vector<var_idx_t> pre_compile_tensor(const std::vector<Expr *>& args, CodeB
|
|||
}
|
||||
|
||||
std::vector<var_idx_t> Expr::pre_compile(CodeBlob& code, std::vector<std::pair<SymDef*, var_idx_t>>* lval_globs) const {
|
||||
if (lval_globs && !(cls == _Tensor || cls == _Var || cls == _Hole || cls == _TypeApply || cls == _GlobVar)) {
|
||||
if (lval_globs && !(cls == _Tensor || cls == _Var || cls == _Hole || cls == _GlobVar)) {
|
||||
std::cerr << "lvalue expression constructor is " << cls << std::endl;
|
||||
throw Fatal{"cannot compile lvalue expression with unknown constructor"};
|
||||
}
|
||||
|
@ -344,8 +336,6 @@ std::vector<var_idx_t> Expr::pre_compile(CodeBlob& code, std::vector<std::pair<S
|
|||
}
|
||||
return rvect;
|
||||
}
|
||||
case _TypeApply:
|
||||
return args[0]->pre_compile(code, lval_globs);
|
||||
case _Var:
|
||||
case _Hole:
|
||||
if (val < 0) {
|
||||
|
|
157
tolk/lexer.cpp
157
tolk/lexer.cpp
|
@ -158,8 +158,7 @@ struct ChunkInlineComment final : ChunkLexerBase {
|
|||
struct ChunkMultilineComment final : ChunkLexerBase {
|
||||
bool parse(Lexer* lex) const override {
|
||||
while (!lex->is_eof()) {
|
||||
// todo drop -} later
|
||||
if ((lex->char_at() == '-' && lex->char_at(1) == '}') || (lex->char_at() == '*' && lex->char_at(1) == '/')) {
|
||||
if (lex->char_at() == '*' && lex->char_at(1) == '/') {
|
||||
lex->skip_chars(2);
|
||||
return true;
|
||||
}
|
||||
|
@ -221,6 +220,22 @@ struct ChunkMultilineString final : ChunkLexerBase {
|
|||
}
|
||||
};
|
||||
|
||||
// An annotation for a function (in the future, for vars also):
|
||||
// @inline and others
|
||||
struct ChunkAnnotation final : ChunkLexerBase {
|
||||
bool parse(Lexer* lex) const override {
|
||||
const char* str_begin = lex->c_str();
|
||||
lex->skip_chars(1);
|
||||
while (std::isalnum(lex->char_at()) || lex->char_at() == '_') {
|
||||
lex->skip_chars(1);
|
||||
}
|
||||
|
||||
std::string_view str_val(str_begin, lex->c_str() - str_begin);
|
||||
lex->add_token(tok_annotation_at, str_val);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// A number, may be a hex one.
|
||||
struct ChunkNumber final : ChunkLexerBase {
|
||||
bool parse(Lexer* lex) const override {
|
||||
|
@ -255,32 +270,6 @@ struct ChunkNumber final : ChunkLexerBase {
|
|||
}
|
||||
};
|
||||
|
||||
// Anything starting from # is a compiler directive.
|
||||
// Technically, #include and #pragma can be mapped as separate chunks,
|
||||
// but storing such long strings in a trie increases its memory usage.
|
||||
struct ChunkCompilerDirective final : ChunkLexerBase {
|
||||
bool parse(Lexer* lex) const override {
|
||||
const char* str_begin = lex->c_str();
|
||||
|
||||
lex->skip_chars(1);
|
||||
while (std::isalnum(lex->char_at())) {
|
||||
lex->skip_chars(1);
|
||||
}
|
||||
|
||||
std::string_view str_val(str_begin, lex->c_str() - str_begin);
|
||||
if (str_val == "#include") {
|
||||
lex->add_token(tok_include, str_val);
|
||||
return true;
|
||||
}
|
||||
if (str_val == "#pragma") {
|
||||
lex->add_token(tok_pragma, str_val);
|
||||
return true;
|
||||
}
|
||||
|
||||
lex->error("unknown compiler directive");
|
||||
}
|
||||
};
|
||||
|
||||
// Tokens like !=, &, etc. emit just a simple TokenType.
|
||||
// Since they are stored in trie, "parsing" them is just skipping len chars.
|
||||
struct ChunkSimpleToken final : ChunkLexerBase {
|
||||
|
@ -307,23 +296,9 @@ struct ChunkSkipWhitespace final : ChunkLexerBase {
|
|||
};
|
||||
|
||||
// Here we handle corner cases of grammar that are requested on demand.
|
||||
// E.g., for 'pragma version >0.5.0', '0.5.0' should be parsed specially to emit tok_semver.
|
||||
// E.g., for 'tolk >0.5.0', '0.5.0' should be parsed specially to emit tok_semver.
|
||||
// See TolkLanguageGrammar::parse_next_chunk_special().
|
||||
struct ChunkSpecialParsing {
|
||||
static bool parse_pragma_name(Lexer* lex) {
|
||||
const char* str_begin = lex->c_str();
|
||||
while (std::isalnum(lex->char_at()) || lex->char_at() == '-') {
|
||||
lex->skip_chars(1);
|
||||
}
|
||||
|
||||
std::string_view str_val(str_begin, lex->c_str() - str_begin);
|
||||
if (str_val.empty()) {
|
||||
return false;
|
||||
}
|
||||
lex->add_token(tok_pragma_name, str_val);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool parse_semver(Lexer* lex) {
|
||||
const char* str_begin = lex->c_str();
|
||||
while (std::isdigit(lex->char_at()) || lex->char_at() == '.') {
|
||||
|
@ -358,53 +333,55 @@ struct ChunkIdentifierOrKeyword final : ChunkLexerBase {
|
|||
case 3:
|
||||
if (str == "int") return tok_int;
|
||||
if (str == "var") return tok_var;
|
||||
if (str == "fun") return tok_fun;
|
||||
if (str == "asm") return tok_asm;
|
||||
if (str == "get") return tok_get;
|
||||
if (str == "try") return tok_try;
|
||||
if (str == "nil") return tok_nil;
|
||||
if (str == "val") return tok_val;
|
||||
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 == "null") return tok_null;
|
||||
if (str == "void") return tok_void;
|
||||
if (str == "bool") return tok_bool;
|
||||
if (str == "auto") return tok_auto;
|
||||
if (str == "tolk") return tok_tolk;
|
||||
if (str == "type") return tok_type;
|
||||
if (str == "enum") return tok_enum;
|
||||
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 == "redef") return tok_redef;
|
||||
if (str == "while") return tok_while;
|
||||
if (str == "until") return tok_until;
|
||||
if (str == "break") return tok_break;
|
||||
if (str == "throw") return tok_throw;
|
||||
if (str == "catch") return tok_catch;
|
||||
if (str == "ifnot") return tok_ifnot;
|
||||
if (str == "infix") return tok_infix;
|
||||
break;
|
||||
case 6:
|
||||
if (str == "return") return tok_return;
|
||||
if (str == "repeat") return tok_repeat;
|
||||
if (str == "elseif") return tok_elseif;
|
||||
if (str == "forall") return tok_forall;
|
||||
if (str == "extern") return tok_extern;
|
||||
if (str == "assert") return tok_assert;
|
||||
if (str == "import") return tok_import;
|
||||
if (str == "global") return tok_global;
|
||||
if (str == "impure") return tok_impure;
|
||||
if (str == "inline") return tok_inline;
|
||||
if (str == "repeat") return tok_repeat;
|
||||
if (str == "struct") return tok_struct;
|
||||
if (str == "export") return tok_export;
|
||||
break;
|
||||
case 7:
|
||||
if (str == "builder") return tok_builder;
|
||||
if (str == "builtin") return tok_builtin;
|
||||
break;
|
||||
case 8:
|
||||
if (str == "continue") return tok_continue;
|
||||
if (str == "operator") return tok_operator;
|
||||
break;
|
||||
case 9:
|
||||
if (str == "elseifnot") return tok_elseifnot;
|
||||
if (str == "method_id") return tok_method_id;
|
||||
break;
|
||||
case 10:
|
||||
if (str == "inline_ref") return tok_inlineref;
|
||||
if (str == "auto_apply") return tok_autoapply;
|
||||
case 12:
|
||||
if (str == "continuation") return tok_continuation;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -418,7 +395,7 @@ struct ChunkIdentifierOrKeyword final : ChunkLexerBase {
|
|||
while (!lex->is_eof()) {
|
||||
char c = lex->char_at();
|
||||
// the pattern of valid identifier first symbol is provided in trie, here we test for identifier middle
|
||||
bool allowed_in_identifier = std::isalnum(c) || c == '_' || c == '$' || c == ':' || c == '?' || c == '!' || c == '\'';
|
||||
bool allowed_in_identifier = std::isalnum(c) || c == '_' || c == '$' || c == '?' || c == '!' || c == '\'';
|
||||
if (!allowed_in_identifier) {
|
||||
break;
|
||||
}
|
||||
|
@ -445,12 +422,12 @@ struct ChunkIdentifierInBackticks final : ChunkLexerBase {
|
|||
lex->skip_chars(1);
|
||||
while (!lex->is_eof() && lex->char_at() != '`' && lex->char_at() != '\n') {
|
||||
if (std::isspace(lex->char_at())) { // probably, I'll remove this restriction after rewriting symtable and cur_sym_idx
|
||||
lex->error("An identifier can't have a space in its name (even inside backticks)");
|
||||
lex->error("an identifier can't have a space in its name (even inside backticks)");
|
||||
}
|
||||
lex->skip_chars(1);
|
||||
}
|
||||
if (lex->char_at() != '`') {
|
||||
lex->error("Unclosed backtick `");
|
||||
lex->error("unclosed backtick `");
|
||||
}
|
||||
|
||||
std::string_view str_val(str_begin + 1, lex->c_str() - str_begin - 1);
|
||||
|
@ -461,6 +438,28 @@ struct ChunkIdentifierInBackticks final : ChunkLexerBase {
|
|||
}
|
||||
};
|
||||
|
||||
// Handle ~`some_method` and .`some_method` todo to be removed later
|
||||
struct ChunkDotTildeAndBackticks final : ChunkLexerBase {
|
||||
bool parse(Lexer* lex) const override {
|
||||
const char* str_begin = lex->c_str();
|
||||
lex->skip_chars(2);
|
||||
while (!lex->is_eof() && lex->char_at() != '`' && lex->char_at() != '\n') {
|
||||
lex->skip_chars(1);
|
||||
}
|
||||
if (lex->char_at() != '`') {
|
||||
lex->error("unclosed backtick `");
|
||||
}
|
||||
|
||||
std::string_view in_backticks(str_begin + 2, lex->c_str() - str_begin - 2);
|
||||
std::string full = std::string(1, *str_begin) + static_cast<std::string>(in_backticks);
|
||||
std::string* allocated = new std::string(full);
|
||||
lex->skip_chars(1);
|
||||
std::string_view str_val(allocated->c_str(), allocated->size());
|
||||
lex->add_token(tok_identifier, str_val);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// ----------------------------------------------------------------------
|
||||
// Here we define a grammar of Tolk.
|
||||
|
@ -477,8 +476,6 @@ struct TolkLanguageGrammar {
|
|||
|
||||
static bool parse_next_chunk_special(Lexer* lex, TokenType parse_next_as) {
|
||||
switch (parse_next_as) {
|
||||
case tok_pragma_name:
|
||||
return ChunkSpecialParsing::parse_pragma_name(lex);
|
||||
case tok_semver:
|
||||
return ChunkSpecialParsing::parse_semver(lex);
|
||||
default:
|
||||
|
@ -493,21 +490,21 @@ struct TolkLanguageGrammar {
|
|||
|
||||
static void init() {
|
||||
trie.add_prefix("//", singleton<ChunkInlineComment>());
|
||||
trie.add_prefix(";;", singleton<ChunkInlineComment>());
|
||||
trie.add_prefix("/*", singleton<ChunkMultilineComment>());
|
||||
trie.add_prefix("{-", singleton<ChunkMultilineComment>());
|
||||
trie.add_prefix(R"(")", singleton<ChunkString>());
|
||||
trie.add_prefix(R"(""")", singleton<ChunkMultilineString>());
|
||||
trie.add_prefix("@", singleton<ChunkAnnotation>());
|
||||
trie.add_prefix(" ", singleton<ChunkSkipWhitespace>());
|
||||
trie.add_prefix("\t", singleton<ChunkSkipWhitespace>());
|
||||
trie.add_prefix("\r", singleton<ChunkSkipWhitespace>());
|
||||
trie.add_prefix("\n", singleton<ChunkSkipWhitespace>());
|
||||
trie.add_prefix("#", singleton<ChunkCompilerDirective>());
|
||||
|
||||
trie.add_pattern("[0-9]", singleton<ChunkNumber>());
|
||||
// todo think of . ~
|
||||
trie.add_pattern("[a-zA-Z_$.~]", singleton<ChunkIdentifierOrKeyword>());
|
||||
trie.add_prefix("`", singleton<ChunkIdentifierInBackticks>());
|
||||
// todo to be removed after ~ becomes invalid and . becomes a separate token
|
||||
trie.add_pattern("[.~]`", singleton<ChunkDotTildeAndBackticks>());
|
||||
|
||||
register_token("+", 1, tok_plus);
|
||||
register_token("-", 1, tok_minus);
|
||||
|
@ -527,6 +524,7 @@ struct TolkLanguageGrammar {
|
|||
register_token("=", 1, tok_assign);
|
||||
register_token("<", 1, tok_lt);
|
||||
register_token(">", 1, tok_gt);
|
||||
register_token("!", 1, tok_logical_not);
|
||||
register_token("&", 1, tok_bitwise_and);
|
||||
register_token("|", 1, tok_bitwise_or);
|
||||
register_token("^", 1, tok_bitwise_xor);
|
||||
|
@ -536,11 +534,10 @@ struct TolkLanguageGrammar {
|
|||
register_token(">=", 2, tok_geq);
|
||||
register_token("<<", 2, tok_lshift);
|
||||
register_token(">>", 2, tok_rshift);
|
||||
register_token("&&", 2, tok_logical_and);
|
||||
register_token("||", 2, tok_logical_or);
|
||||
register_token("~/", 2, tok_divR);
|
||||
register_token("^/", 2, tok_divC);
|
||||
register_token("~%", 2, tok_modR);
|
||||
register_token("^%", 2, tok_modC);
|
||||
register_token("/%", 2, tok_divmod);
|
||||
register_token("+=", 2, tok_set_plus);
|
||||
register_token("-=", 2, tok_set_minus);
|
||||
register_token("*=", 2, tok_set_mul);
|
||||
|
@ -549,18 +546,12 @@ struct TolkLanguageGrammar {
|
|||
register_token("&=", 2, tok_set_bitwise_and);
|
||||
register_token("|=", 2, tok_set_bitwise_or);
|
||||
register_token("^=", 2, tok_set_bitwise_xor);
|
||||
register_token("->", 2, tok_mapsto);
|
||||
register_token("->", 2, tok_arrow);
|
||||
register_token("<=>", 3, tok_spaceship);
|
||||
register_token("~>>", 3, tok_rshiftR);
|
||||
register_token("^>>", 3, tok_rshiftC);
|
||||
register_token("~/=", 3, tok_set_divR);
|
||||
register_token("^/=", 3, tok_set_divC);
|
||||
register_token("~%=", 3, tok_set_modR);
|
||||
register_token("^%=", 3, tok_set_modC);
|
||||
register_token("<<=", 3, tok_set_lshift);
|
||||
register_token(">>=", 3, tok_set_rshift);
|
||||
register_token("~>>=", 4, tok_set_rshiftR);
|
||||
register_token("^>>=", 4, tok_set_rshiftC);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -593,7 +584,7 @@ void Lexer::next() {
|
|||
while (cur_token_idx == last_token_idx && !is_eof()) {
|
||||
update_location();
|
||||
if (!TolkLanguageGrammar::parse_next_chunk(this)) {
|
||||
error("Failed to parse");
|
||||
error("failed to parse");
|
||||
}
|
||||
}
|
||||
if (is_eof()) {
|
||||
|
@ -616,8 +607,8 @@ void Lexer::error(const std::string& err_msg) const {
|
|||
throw ParseError(cur_location(), err_msg);
|
||||
}
|
||||
|
||||
void Lexer::on_expect_call_failed(const char* str_expected) const {
|
||||
throw ParseError(cur_location(), std::string(str_expected) + " expected instead of `" + std::string(cur_str()) + "`");
|
||||
void Lexer::unexpected(const char* str_expected) const {
|
||||
throw ParseError(cur_location(), "expected " + std::string(str_expected) + ", got `" + std::string(cur_str()) + "`");
|
||||
}
|
||||
|
||||
void lexer_init() {
|
||||
|
|
90
tolk/lexer.h
90
tolk/lexer.h
|
@ -25,15 +25,33 @@ namespace tolk {
|
|||
enum TokenType {
|
||||
tok_empty,
|
||||
|
||||
tok_fun,
|
||||
tok_get,
|
||||
tok_type,
|
||||
tok_enum,
|
||||
tok_struct,
|
||||
tok_operator,
|
||||
tok_infix,
|
||||
|
||||
tok_global,
|
||||
tok_const,
|
||||
tok_var,
|
||||
tok_val,
|
||||
tok_redef,
|
||||
|
||||
tok_annotation_at,
|
||||
tok_colon,
|
||||
tok_asm,
|
||||
tok_builtin,
|
||||
|
||||
tok_int_const,
|
||||
tok_string_const,
|
||||
tok_string_modifier,
|
||||
|
||||
tok_identifier,
|
||||
|
||||
tok_true,
|
||||
tok_false,
|
||||
tok_nil, // todo "null" keyword is still absent, "nil" in FunC is an empty tuple
|
||||
tok_null,
|
||||
|
||||
tok_identifier,
|
||||
|
||||
tok_plus,
|
||||
tok_minus,
|
||||
|
@ -41,7 +59,6 @@ enum TokenType {
|
|||
tok_div,
|
||||
tok_mod,
|
||||
tok_question,
|
||||
tok_colon,
|
||||
tok_comma,
|
||||
tok_semicolon,
|
||||
tok_oppar,
|
||||
|
@ -54,11 +71,13 @@ enum TokenType {
|
|||
tok_underscore,
|
||||
tok_lt,
|
||||
tok_gt,
|
||||
tok_logical_not,
|
||||
tok_logical_and,
|
||||
tok_logical_or,
|
||||
tok_bitwise_and,
|
||||
tok_bitwise_or,
|
||||
tok_bitwise_xor,
|
||||
tok_bitwise_not,
|
||||
tok_dot,
|
||||
|
||||
tok_eq,
|
||||
tok_neq,
|
||||
|
@ -71,71 +90,45 @@ enum TokenType {
|
|||
tok_rshiftC,
|
||||
tok_divR,
|
||||
tok_divC,
|
||||
tok_modR,
|
||||
tok_modC,
|
||||
tok_divmod,
|
||||
tok_set_plus,
|
||||
tok_set_minus,
|
||||
tok_set_mul,
|
||||
tok_set_div,
|
||||
tok_set_divR,
|
||||
tok_set_divC,
|
||||
tok_set_mod,
|
||||
tok_set_modR,
|
||||
tok_set_modC,
|
||||
tok_set_lshift,
|
||||
tok_set_rshift,
|
||||
tok_set_rshiftR,
|
||||
tok_set_rshiftC,
|
||||
tok_set_bitwise_and,
|
||||
tok_set_bitwise_or,
|
||||
tok_set_bitwise_xor,
|
||||
|
||||
tok_return,
|
||||
tok_var,
|
||||
tok_repeat,
|
||||
tok_do,
|
||||
tok_while,
|
||||
tok_until,
|
||||
tok_break,
|
||||
tok_continue,
|
||||
tok_try,
|
||||
tok_catch,
|
||||
tok_throw,
|
||||
tok_assert,
|
||||
tok_if,
|
||||
tok_ifnot,
|
||||
tok_then,
|
||||
tok_else,
|
||||
tok_elseif,
|
||||
tok_elseifnot,
|
||||
|
||||
tok_int,
|
||||
tok_cell,
|
||||
tok_bool,
|
||||
tok_slice,
|
||||
tok_builder,
|
||||
tok_cont,
|
||||
tok_continuation,
|
||||
tok_tuple,
|
||||
tok_mapsto,
|
||||
tok_forall,
|
||||
tok_auto,
|
||||
tok_void,
|
||||
tok_arrow,
|
||||
|
||||
tok_extern,
|
||||
tok_global,
|
||||
tok_asm,
|
||||
tok_impure,
|
||||
tok_pure,
|
||||
tok_inline,
|
||||
tok_inlineref,
|
||||
tok_builtin,
|
||||
tok_autoapply,
|
||||
tok_method_id,
|
||||
tok_get,
|
||||
tok_operator,
|
||||
tok_infix,
|
||||
tok_infixl,
|
||||
tok_infixr,
|
||||
tok_const,
|
||||
|
||||
tok_pragma,
|
||||
tok_pragma_name,
|
||||
tok_tolk,
|
||||
tok_semver,
|
||||
tok_include,
|
||||
tok_import,
|
||||
tok_export,
|
||||
|
||||
tok_eof
|
||||
};
|
||||
|
@ -167,9 +160,6 @@ class Lexer {
|
|||
location.char_offset = static_cast<int>(p_next - p_start);
|
||||
}
|
||||
|
||||
GNU_ATTRIBUTE_NORETURN GNU_ATTRIBUTE_COLD
|
||||
void on_expect_call_failed(const char* str_expected) const;
|
||||
|
||||
public:
|
||||
|
||||
explicit Lexer(const SrcFile* file);
|
||||
|
@ -217,16 +207,18 @@ public:
|
|||
|
||||
void check(TokenType next_tok, const char* str_expected) const {
|
||||
if (cur_token.type != next_tok) {
|
||||
on_expect_call_failed(str_expected); // unlikely path, not inlined
|
||||
unexpected(str_expected); // unlikely path, not inlined
|
||||
}
|
||||
}
|
||||
void expect(TokenType next_tok, const char* str_expected) {
|
||||
if (cur_token.type != next_tok) {
|
||||
on_expect_call_failed(str_expected);
|
||||
unexpected(str_expected);
|
||||
}
|
||||
next();
|
||||
}
|
||||
|
||||
GNU_ATTRIBUTE_NORETURN GNU_ATTRIBUTE_COLD
|
||||
void unexpected(const char* str_expected) const;
|
||||
GNU_ATTRIBUTE_NORETURN GNU_ATTRIBUTE_COLD
|
||||
void error(const std::string& err_msg) const;
|
||||
};
|
||||
|
|
|
@ -34,10 +34,10 @@
|
|||
namespace tolk {
|
||||
|
||||
static int calc_sym_idx(std::string_view sym_name) {
|
||||
return G.symbols.lookup_add(sym_name);
|
||||
return G.symbols.lookup(sym_name);
|
||||
}
|
||||
|
||||
Expr* process_expr(AnyV v, CodeBlob& code, bool nv = false);
|
||||
Expr* process_expr(AnyV v, CodeBlob& code);
|
||||
|
||||
static void check_global_func(SrcLocation loc, sym_idx_t func_name) {
|
||||
SymDef* sym_def = lookup_symbol(func_name);
|
||||
|
@ -77,42 +77,63 @@ static void check_import_exists_when_using_sym(AnyV v_usage, const SymDef* used_
|
|||
}
|
||||
}
|
||||
|
||||
static Expr* process_expr(V<ast_binary_operator> v, CodeBlob& code, bool nv) {
|
||||
static Expr* create_new_local_variable(SrcLocation loc, std::string_view var_name, TypeExpr* var_type) {
|
||||
SymDef* sym = lookup_symbol(calc_sym_idx(var_name));
|
||||
if (sym) { // creating a new variable, but something found in symtable
|
||||
if (sym->level != G.scope_level) {
|
||||
sym = nullptr; // declaring a new variable with the same name, but in another scope
|
||||
} else {
|
||||
throw ParseError(loc, "redeclaration of local variable `" + static_cast<std::string>(var_name) + "`");
|
||||
}
|
||||
}
|
||||
Expr* x = new Expr{Expr::_Var, loc};
|
||||
x->val = ~calc_sym_idx(var_name);
|
||||
x->e_type = var_type;
|
||||
x->flags = Expr::_IsLvalue;
|
||||
return x;
|
||||
}
|
||||
|
||||
static Expr* create_new_underscore_variable(SrcLocation loc, TypeExpr* var_type) {
|
||||
Expr* x = new Expr{Expr::_Hole, loc};
|
||||
x->val = -1;
|
||||
x->flags = Expr::_IsLvalue;
|
||||
x->e_type = var_type;
|
||||
return x;
|
||||
}
|
||||
|
||||
static Expr* process_expr(V<ast_binary_operator> v, CodeBlob& code) {
|
||||
TokenType t = v->tok;
|
||||
std::string operator_name = static_cast<std::string>(v->operator_name);
|
||||
|
||||
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) {
|
||||
Expr* x = process_expr(v->get_lhs(), code, nv);
|
||||
if (t == tok_set_plus || t == tok_set_minus || t == tok_set_mul || t == tok_set_div ||
|
||||
t == tok_set_mod || t == tok_set_lshift || t == tok_set_rshift ||
|
||||
t == tok_set_bitwise_and || t == tok_set_bitwise_or || t == tok_set_bitwise_xor) {
|
||||
Expr* x = process_expr(v->get_lhs(), code);
|
||||
x->chk_lvalue();
|
||||
x->chk_rvalue();
|
||||
sym_idx_t name = G.symbols.lookup_add("^_" + operator_name + "_");
|
||||
Expr* y = process_expr(v->get_rhs(), code, false);
|
||||
Expr* y = process_expr(v->get_rhs(), code);
|
||||
y->chk_rvalue();
|
||||
Expr* z = new Expr{Expr::_Apply, name, {x, y}};
|
||||
z->here = v->loc;
|
||||
z->set_val(t);
|
||||
z->flags = Expr::_IsRvalue;
|
||||
z->deduce_type();
|
||||
Expr* res = new Expr{Expr::_Letop, {x->copy(), z}};
|
||||
res->here = v->loc;
|
||||
res->flags = (x->flags & ~Expr::_IsType) | Expr::_IsRvalue;
|
||||
res->set_val(t);
|
||||
res->flags = x->flags | Expr::_IsRvalue;
|
||||
res->deduce_type();
|
||||
return res;
|
||||
}
|
||||
if (t == tok_assign) {
|
||||
Expr* x = process_expr(v->get_lhs(), code, nv);
|
||||
Expr* x = process_expr(v->get_lhs(), code);
|
||||
x->chk_lvalue();
|
||||
Expr* y = process_expr(v->get_rhs(), code, false);
|
||||
Expr* y = process_expr(v->get_rhs(), code);
|
||||
y->chk_rvalue();
|
||||
x->predefine_vars();
|
||||
x->define_new_vars(code);
|
||||
Expr* res = new Expr{Expr::_Letop, {x, y}};
|
||||
res->here = v->loc;
|
||||
res->flags = (x->flags & ~Expr::_IsType) | Expr::_IsRvalue;
|
||||
res->set_val(t);
|
||||
res->flags = x->flags | Expr::_IsRvalue;
|
||||
res->deduce_type();
|
||||
return res;
|
||||
}
|
||||
|
@ -120,20 +141,21 @@ static Expr* process_expr(V<ast_binary_operator> v, CodeBlob& code, bool nv) {
|
|||
t == tok_bitwise_and || t == tok_bitwise_or || t == tok_bitwise_xor ||
|
||||
t == tok_eq || t == tok_lt || t == tok_gt || t == tok_leq || t == tok_geq || t == tok_neq || t == tok_spaceship ||
|
||||
t == tok_lshift || t == tok_rshift || t == tok_rshiftC || t == tok_rshiftR ||
|
||||
t == tok_mul || t == tok_div || t == tok_mod || t == tok_divmod ||
|
||||
t == tok_divC || t == tok_divR || t == tok_modC || t == tok_modR) {
|
||||
Expr* res = process_expr(v->get_lhs(), code, nv);
|
||||
t == tok_mul || t == tok_div || t == tok_mod || t == tok_divC || t == tok_divR) {
|
||||
Expr* res = process_expr(v->get_lhs(), code);
|
||||
res->chk_rvalue();
|
||||
sym_idx_t name = G.symbols.lookup_add("_" + operator_name + "_");
|
||||
Expr* x = process_expr(v->get_rhs(), code, false);
|
||||
Expr* x = process_expr(v->get_rhs(), code);
|
||||
x->chk_rvalue();
|
||||
res = new Expr{Expr::_Apply, name, {res, x}};
|
||||
res->here = v->loc;
|
||||
res->set_val(t);
|
||||
res->flags = Expr::_IsRvalue;
|
||||
res->deduce_type();
|
||||
return res;
|
||||
}
|
||||
if (t == tok_logical_and || t == tok_logical_or) {
|
||||
v->error("logical operators are not supported yet");
|
||||
}
|
||||
|
||||
v->error("unsupported binary operator");
|
||||
}
|
||||
|
@ -141,7 +163,7 @@ static Expr* process_expr(V<ast_binary_operator> v, CodeBlob& code, bool nv) {
|
|||
static Expr* process_expr(V<ast_unary_operator> v, CodeBlob& code) {
|
||||
TokenType t = v->tok;
|
||||
sym_idx_t name = G.symbols.lookup_add(static_cast<std::string>(v->operator_name) + "_");
|
||||
Expr* x = process_expr(v->get_rhs(), code, false);
|
||||
Expr* x = process_expr(v->get_rhs(), code);
|
||||
x->chk_rvalue();
|
||||
|
||||
// here's an optimization to convert "-1" (tok_minus tok_int_const) to a const -1, not to Expr::Apply(-,1)
|
||||
|
@ -151,28 +173,26 @@ static Expr* process_expr(V<ast_unary_operator> v, CodeBlob& code) {
|
|||
// `var snd = - 1;` // is Expr::Apply(-), a comment "snd=1" is lost in stack layout comments, and so on
|
||||
// hence, when after grammar modification tok_minus became a true unary operator (not a part of a number),
|
||||
// and thus to preserve existing behavior until compiler parts are completely rewritten, handle this case here
|
||||
if (x->cls == Expr::_Const) {
|
||||
if (t == tok_bitwise_not) {
|
||||
x->intval = ~x->intval;
|
||||
} else if (t == tok_minus) {
|
||||
x->intval = -x->intval;
|
||||
}
|
||||
if (t == tok_minus && x->cls == Expr::_Const) {
|
||||
x->intval = -x->intval;
|
||||
if (!x->intval->signed_fits_bits(257)) {
|
||||
v->error("integer overflow");
|
||||
}
|
||||
return x;
|
||||
}
|
||||
if (t == tok_plus && x->cls == Expr::_Const) {
|
||||
return x;
|
||||
}
|
||||
|
||||
auto res = new Expr{Expr::_Apply, name, {x}};
|
||||
res->here = v->loc;
|
||||
res->set_val(t);
|
||||
res->flags = Expr::_IsRvalue;
|
||||
res->deduce_type();
|
||||
return res;
|
||||
}
|
||||
|
||||
static Expr* process_expr(V<ast_dot_tilde_call> v, CodeBlob& code, bool nv) {
|
||||
Expr* res = process_expr(v->get_lhs(), code, nv);
|
||||
static Expr* process_expr(V<ast_dot_tilde_call> v, CodeBlob& code) {
|
||||
Expr* res = process_expr(v->get_lhs(), code);
|
||||
bool modify = v->method_name[0] == '~';
|
||||
Expr* obj = res;
|
||||
if (modify) {
|
||||
|
@ -188,7 +208,6 @@ static Expr* process_expr(V<ast_dot_tilde_call> v, CodeBlob& code, bool nv) {
|
|||
const SymDef* sym1 = lookup_symbol(name1);
|
||||
if (sym1 && dynamic_cast<SymValFunc*>(sym1->value)) {
|
||||
name_idx = name1;
|
||||
sym = sym1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -198,7 +217,7 @@ static Expr* process_expr(V<ast_dot_tilde_call> v, CodeBlob& code, bool nv) {
|
|||
if (!val) {
|
||||
v->error("undefined method call");
|
||||
}
|
||||
Expr* x = process_expr(v->get_arg(), code, false);
|
||||
Expr* x = process_expr(v->get_arg(), code);
|
||||
x->chk_rvalue();
|
||||
if (x->cls == Expr::_Tensor) {
|
||||
res = new Expr{Expr::_Apply, name_idx, {obj}};
|
||||
|
@ -210,7 +229,7 @@ static Expr* process_expr(V<ast_dot_tilde_call> v, CodeBlob& code, bool nv) {
|
|||
res->flags = Expr::_IsRvalue | (val->is_marked_as_pure() ? 0 : Expr::_IsImpure);
|
||||
res->deduce_type();
|
||||
if (modify) {
|
||||
auto tmp = res;
|
||||
Expr* tmp = res;
|
||||
res = new Expr{Expr::_LetFirst, {obj->copy(), tmp}};
|
||||
res->here = v->loc;
|
||||
res->flags = tmp->flags;
|
||||
|
@ -220,12 +239,12 @@ static Expr* process_expr(V<ast_dot_tilde_call> v, CodeBlob& code, bool nv) {
|
|||
return res;
|
||||
}
|
||||
|
||||
static Expr* process_expr(V<ast_ternary_operator> v, CodeBlob& code, bool nv) {
|
||||
Expr* cond = process_expr(v->get_cond(), code, nv);
|
||||
static Expr* process_expr(V<ast_ternary_operator> v, CodeBlob& code) {
|
||||
Expr* cond = process_expr(v->get_cond(), code);
|
||||
cond->chk_rvalue();
|
||||
Expr* x = process_expr(v->get_when_true(), code, false);
|
||||
Expr* x = process_expr(v->get_when_true(), code);
|
||||
x->chk_rvalue();
|
||||
Expr* y = process_expr(v->get_when_false(), code, false);
|
||||
Expr* y = process_expr(v->get_when_false(), code);
|
||||
y->chk_rvalue();
|
||||
Expr* res = new Expr{Expr::_CondExpr, {cond, x, y}};
|
||||
res->here = v->loc;
|
||||
|
@ -234,9 +253,14 @@ static Expr* process_expr(V<ast_ternary_operator> v, CodeBlob& code, bool nv) {
|
|||
return res;
|
||||
}
|
||||
|
||||
static Expr* process_expr(V<ast_function_call> v, CodeBlob& code, bool nv) {
|
||||
Expr* res = process_expr(v->get_called_f(), code, nv);
|
||||
Expr* x = process_expr(v->get_called_arg(), code, false);
|
||||
static Expr* process_expr(V<ast_function_call> v, CodeBlob& code) {
|
||||
// special error for "null()" which is a FunC syntax
|
||||
if (v->get_called_f()->type == ast_null_keyword) {
|
||||
v->error("null is not a function: use `null`, not `null()`");
|
||||
}
|
||||
|
||||
Expr* res = process_expr(v->get_called_f(), code);
|
||||
Expr* x = process_expr(v->get_called_arg(), code);
|
||||
x->chk_rvalue();
|
||||
res = make_func_apply(res, x);
|
||||
res->here = v->loc;
|
||||
|
@ -244,7 +268,7 @@ static Expr* process_expr(V<ast_function_call> v, CodeBlob& code, bool nv) {
|
|||
return res;
|
||||
}
|
||||
|
||||
static Expr* process_expr(V<ast_tensor> v, CodeBlob& code, bool nv) {
|
||||
static Expr* process_expr(V<ast_tensor> v, CodeBlob& code) {
|
||||
if (v->empty()) {
|
||||
Expr* res = new Expr{Expr::_Tensor, {}};
|
||||
res->flags = Expr::_IsRvalue;
|
||||
|
@ -253,13 +277,13 @@ static Expr* process_expr(V<ast_tensor> v, CodeBlob& code, bool nv) {
|
|||
return res;
|
||||
}
|
||||
|
||||
Expr* res = process_expr(v->get_item(0), code, nv);
|
||||
Expr* res = process_expr(v->get_item(0), code);
|
||||
std::vector<TypeExpr*> type_list;
|
||||
type_list.push_back(res->e_type);
|
||||
int f = res->flags;
|
||||
res = new Expr{Expr::_Tensor, {res}};
|
||||
for (int i = 1; i < v->size(); ++i) {
|
||||
Expr* x = process_expr(v->get_item(i), code, nv);
|
||||
Expr* x = process_expr(v->get_item(i), code);
|
||||
res->pb_arg(x);
|
||||
f &= x->flags;
|
||||
type_list.push_back(x->e_type);
|
||||
|
@ -270,25 +294,7 @@ static Expr* process_expr(V<ast_tensor> v, CodeBlob& code, bool nv) {
|
|||
return res;
|
||||
}
|
||||
|
||||
static Expr* process_expr(V<ast_variable_declaration> v, CodeBlob& code) {
|
||||
Expr* x = process_expr(v->get_variable_or_list(), code, true);
|
||||
x->chk_lvalue(); // chk_lrvalue() ?
|
||||
Expr* res = new Expr{Expr::_TypeApply, {x}};
|
||||
res->e_type = v->declared_type;
|
||||
res->here = v->loc;
|
||||
try {
|
||||
unify(res->e_type, x->e_type);
|
||||
} catch (UnifyError& ue) {
|
||||
std::ostringstream os;
|
||||
os << "cannot transform expression of type " << x->e_type << " to explicitly requested type " << res->e_type
|
||||
<< ": " << ue;
|
||||
v->error(os.str());
|
||||
}
|
||||
res->flags = x->flags;
|
||||
return res;
|
||||
}
|
||||
|
||||
static Expr* process_expr(V<ast_tensor_square> v, CodeBlob& code, bool nv) {
|
||||
static Expr* process_expr(V<ast_tensor_square> v, CodeBlob& code) {
|
||||
if (v->empty()) {
|
||||
Expr* res = new Expr{Expr::_Tensor, {}};
|
||||
res->flags = Expr::_IsRvalue;
|
||||
|
@ -301,13 +307,13 @@ static Expr* process_expr(V<ast_tensor_square> v, CodeBlob& code, bool nv) {
|
|||
return res;
|
||||
}
|
||||
|
||||
Expr* res = process_expr(v->get_item(0), code, nv);
|
||||
Expr* res = process_expr(v->get_item(0), code);
|
||||
std::vector<TypeExpr*> type_list;
|
||||
type_list.push_back(res->e_type);
|
||||
int f = res->flags;
|
||||
res = new Expr{Expr::_Tensor, {res}};
|
||||
for (int i = 1; i < v->size(); ++i) {
|
||||
Expr* x = process_expr(v->get_item(i), code, nv);
|
||||
Expr* x = process_expr(v->get_item(i), code);
|
||||
res->pb_arg(x);
|
||||
f &= x->flags;
|
||||
type_list.push_back(x->e_type);
|
||||
|
@ -364,7 +370,7 @@ static Expr* process_expr(V<ast_string_const> v) {
|
|||
unsigned char buff[128];
|
||||
int bits = (int)td::bitstring::parse_bitstring_hex_literal(buff, sizeof(buff), str.data(), str.data() + str.size());
|
||||
if (bits < 0) {
|
||||
v->error("Invalid hex bitstring constant '" + str + "'");
|
||||
v->error("invalid hex bitstring constant '" + str + "'");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -406,32 +412,23 @@ static Expr* process_expr(V<ast_string_const> v) {
|
|||
}
|
||||
|
||||
static Expr* process_expr(V<ast_bool_const> v) {
|
||||
SymDef* sym = lookup_symbol(calc_sym_idx(v->bool_val ? "true" : "false"));
|
||||
tolk_assert(sym);
|
||||
Expr* res = new Expr{Expr::_Apply, sym, {}};
|
||||
SymDef* builtin_sym = lookup_symbol(calc_sym_idx(v->bool_val ? "__true" : "__false"));
|
||||
Expr* res = new Expr{Expr::_Apply, builtin_sym, {}};
|
||||
res->flags = Expr::_IsRvalue;
|
||||
res->deduce_type();
|
||||
return res;
|
||||
}
|
||||
|
||||
static Expr* process_expr([[maybe_unused]] V<ast_nil_tuple> v) {
|
||||
SymDef* sym = lookup_symbol(calc_sym_idx("nil"));
|
||||
tolk_assert(sym);
|
||||
Expr* res = new Expr{Expr::_Apply, sym, {}};
|
||||
static Expr* process_expr([[maybe_unused]] V<ast_null_keyword> v) {
|
||||
SymDef* builtin_sym = lookup_symbol(calc_sym_idx("__null"));
|
||||
Expr* res = new Expr{Expr::_Apply, builtin_sym, {}};
|
||||
res->flags = Expr::_IsRvalue;
|
||||
res->deduce_type();
|
||||
return res;
|
||||
}
|
||||
|
||||
static Expr* process_expr(V<ast_identifier> v, bool nv) {
|
||||
static Expr* process_identifier(V<ast_identifier> v) {
|
||||
SymDef* sym = lookup_symbol(calc_sym_idx(v->name));
|
||||
if (nv && sym) {
|
||||
if (sym->level != G.scope_level) {
|
||||
sym = nullptr; // declaring a new variable with the same name, but in another scope
|
||||
} else {
|
||||
v->error("redeclaration of local variable `" + static_cast<std::string>(v->name) + "`");
|
||||
}
|
||||
}
|
||||
if (sym && dynamic_cast<SymValGlobVar*>(sym->value)) {
|
||||
check_import_exists_when_using_sym(v, sym);
|
||||
auto val = dynamic_cast<SymValGlobVar*>(sym->value);
|
||||
|
@ -455,7 +452,7 @@ static Expr* process_expr(V<ast_identifier> v, bool nv) {
|
|||
res->strval = val->get_str_value();
|
||||
res->e_type = TypeExpr::new_atomic(tok_slice);
|
||||
} else {
|
||||
v->error("Invalid symbolic constant type");
|
||||
v->error("invalid symbolic constant type");
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
@ -463,86 +460,65 @@ static Expr* process_expr(V<ast_identifier> v, bool nv) {
|
|||
check_import_exists_when_using_sym(v, sym);
|
||||
}
|
||||
Expr* res = new Expr{Expr::_Var, v->loc};
|
||||
if (nv) {
|
||||
res->val = ~calc_sym_idx(v->name);
|
||||
res->e_type = TypeExpr::new_hole();
|
||||
res->flags = Expr::_IsLvalue;
|
||||
// std::cerr << "defined new variable " << lex.cur().str << " : " << res->e_type << std::endl;
|
||||
} else {
|
||||
if (!sym) {
|
||||
check_global_func(v->loc, calc_sym_idx(v->name));
|
||||
sym = lookup_symbol(calc_sym_idx(v->name));
|
||||
}
|
||||
res->sym = sym;
|
||||
SymVal* val = nullptr;
|
||||
bool impure = false;
|
||||
if (sym) {
|
||||
val = dynamic_cast<SymVal*>(sym->value);
|
||||
}
|
||||
if (!val) {
|
||||
v->error("undefined identifier '" + static_cast<std::string>(v->name) + "'");
|
||||
}
|
||||
if (val->kind == SymValKind::_Func) {
|
||||
res->e_type = val->get_type();
|
||||
res->cls = Expr::_GlobFunc;
|
||||
impure = !dynamic_cast<SymValFunc*>(val)->is_marked_as_pure();
|
||||
} else {
|
||||
tolk_assert(val->idx >= 0);
|
||||
res->val = val->idx;
|
||||
res->e_type = val->get_type();
|
||||
// std::cerr << "accessing variable " << lex.cur().str << " : " << res->e_type << std::endl;
|
||||
}
|
||||
// std::cerr << "accessing symbol " << lex.cur().str << " : " << res->e_type << (val->impure ? " (impure)" : " (pure)") << std::endl;
|
||||
res->flags = Expr::_IsLvalue | Expr::_IsRvalue | (impure ? Expr::_IsImpure : 0);
|
||||
if (!sym) {
|
||||
check_global_func(v->loc, calc_sym_idx(v->name));
|
||||
sym = lookup_symbol(calc_sym_idx(v->name));
|
||||
}
|
||||
res->sym = sym;
|
||||
SymVal* val = nullptr;
|
||||
bool impure = false;
|
||||
if (sym) {
|
||||
val = dynamic_cast<SymVal*>(sym->value);
|
||||
}
|
||||
if (!val) {
|
||||
v->error("undefined identifier '" + static_cast<std::string>(v->name) + "'");
|
||||
}
|
||||
if (val->kind == SymValKind::_Func) {
|
||||
res->e_type = val->get_type();
|
||||
res->cls = Expr::_GlobFunc;
|
||||
impure = !dynamic_cast<SymValFunc*>(val)->is_marked_as_pure();
|
||||
} else {
|
||||
tolk_assert(val->idx >= 0);
|
||||
res->val = val->idx;
|
||||
res->e_type = val->get_type();
|
||||
// std::cerr << "accessing variable " << lex.cur().str << " : " << res->e_type << std::endl;
|
||||
}
|
||||
// std::cerr << "accessing symbol " << lex.cur().str << " : " << res->e_type << (val->impure ? " (impure)" : " (pure)") << std::endl;
|
||||
res->flags = Expr::_IsLvalue | Expr::_IsRvalue | (impure ? Expr::_IsImpure : 0);
|
||||
res->deduce_type();
|
||||
return res;
|
||||
}
|
||||
|
||||
Expr* process_expr(AnyV v, CodeBlob& code, bool nv) {
|
||||
Expr* process_expr(AnyV v, CodeBlob& code) {
|
||||
switch (v->type) {
|
||||
case ast_binary_operator:
|
||||
return process_expr(v->as<ast_binary_operator>(), code, nv);
|
||||
return process_expr(v->as<ast_binary_operator>(), code);
|
||||
case ast_unary_operator:
|
||||
return process_expr(v->as<ast_unary_operator>(), code);
|
||||
case ast_dot_tilde_call:
|
||||
return process_expr(v->as<ast_dot_tilde_call>(), code, nv);
|
||||
return process_expr(v->as<ast_dot_tilde_call>(), code);
|
||||
case ast_ternary_operator:
|
||||
return process_expr(v->as<ast_ternary_operator>(), code, nv);
|
||||
return process_expr(v->as<ast_ternary_operator>(), code);
|
||||
case ast_function_call:
|
||||
return process_expr(v->as<ast_function_call>(), code, nv);
|
||||
return process_expr(v->as<ast_function_call>(), code);
|
||||
case ast_parenthesized_expr:
|
||||
return process_expr(v->as<ast_parenthesized_expr>()->get_expr(), code, nv);
|
||||
case ast_variable_declaration:
|
||||
return process_expr(v->as<ast_variable_declaration>(), code);
|
||||
return process_expr(v->as<ast_parenthesized_expr>()->get_expr(), code);
|
||||
case ast_tensor:
|
||||
return process_expr(v->as<ast_tensor>(), code, nv);
|
||||
return process_expr(v->as<ast_tensor>(), code);
|
||||
case ast_tensor_square:
|
||||
return process_expr(v->as<ast_tensor_square>(), code, nv);
|
||||
return process_expr(v->as<ast_tensor_square>(), code);
|
||||
case ast_int_const:
|
||||
return process_expr(v->as<ast_int_const>());
|
||||
case ast_string_const:
|
||||
return process_expr(v->as<ast_string_const>());
|
||||
case ast_bool_const:
|
||||
return process_expr(v->as<ast_bool_const>());
|
||||
case ast_nil_tuple:
|
||||
return process_expr(v->as<ast_nil_tuple>());
|
||||
case ast_null_keyword:
|
||||
return process_expr(v->as<ast_null_keyword>());
|
||||
case ast_identifier:
|
||||
return process_expr(v->as<ast_identifier>(), nv);
|
||||
|
||||
case ast_underscore: {
|
||||
Expr* res = new Expr{Expr::_Hole, v->loc};
|
||||
res->val = -1;
|
||||
res->flags = Expr::_IsLvalue;
|
||||
res->e_type = TypeExpr::new_hole();
|
||||
return res;
|
||||
}
|
||||
case ast_type_expression: {
|
||||
Expr* res = new Expr{Expr::_Type, v->loc};
|
||||
res->flags = Expr::_IsType;
|
||||
res->e_type = v->as<ast_type_expression>()->declared_type;
|
||||
return res;
|
||||
}
|
||||
return process_identifier(v->as<ast_identifier>());
|
||||
case ast_underscore:
|
||||
return create_new_underscore_variable(v->loc, TypeExpr::new_hole());
|
||||
default:
|
||||
throw UnexpectedASTNodeType(v, "process_expr");
|
||||
}
|
||||
|
@ -562,6 +538,70 @@ void combine_parallel(val& x, const val y) {
|
|||
}
|
||||
} // namespace blk_fl
|
||||
|
||||
static Expr* process_local_vars_lhs(AnyV v, CodeBlob& code) {
|
||||
switch (v->type) {
|
||||
case ast_local_var: {
|
||||
if (v->as<ast_local_var>()->marked_as_redef) {
|
||||
return process_identifier(v->as<ast_local_var>()->get_identifier()->as<ast_identifier>());
|
||||
}
|
||||
TypeExpr* declared_type = v->as<ast_local_var>()->declared_type;
|
||||
if (auto v_ident = v->as<ast_local_var>()->get_identifier()->try_as<ast_identifier>()) {
|
||||
return create_new_local_variable(v->loc, v_ident->name, declared_type ? declared_type : TypeExpr::new_hole());
|
||||
} else {
|
||||
return create_new_underscore_variable(v->loc, declared_type ? declared_type : TypeExpr::new_hole());
|
||||
}
|
||||
}
|
||||
case ast_parenthesized_expr:
|
||||
return process_local_vars_lhs(v->as<ast_parenthesized_expr>()->get_expr(), code);
|
||||
case ast_tensor: {
|
||||
std::vector<TypeExpr*> type_list;
|
||||
Expr* res = new Expr{Expr::_Tensor, v->loc};
|
||||
for (AnyV item : v->as<ast_tensor>()->get_items()) {
|
||||
Expr* x = process_local_vars_lhs(item, code);
|
||||
res->pb_arg(x);
|
||||
res->flags |= x->flags;
|
||||
type_list.push_back(x->e_type);
|
||||
}
|
||||
res->e_type = TypeExpr::new_tensor(std::move(type_list));
|
||||
return res;
|
||||
}
|
||||
case ast_tensor_square: {
|
||||
std::vector<TypeExpr*> type_list;
|
||||
Expr* res = new Expr{Expr::_Tensor, v->loc};
|
||||
for (AnyV item : v->as<ast_tensor_square>()->get_items()) {
|
||||
Expr* x = process_local_vars_lhs(item, code);
|
||||
res->pb_arg(x);
|
||||
res->flags |= x->flags;
|
||||
type_list.push_back(x->e_type);
|
||||
}
|
||||
res->e_type = TypeExpr::new_tensor(std::move(type_list));
|
||||
res = new Expr{Expr::_MkTuple, {res}};
|
||||
res->flags = res->args.at(0)->flags;
|
||||
res->here = v->loc;
|
||||
res->e_type = TypeExpr::new_tuple(res->args.at(0)->e_type);
|
||||
return res;
|
||||
}
|
||||
default:
|
||||
throw UnexpectedASTNodeType(v, "process_local_vars_lhs");
|
||||
}
|
||||
}
|
||||
|
||||
static blk_fl::val process_vertex(V<ast_local_vars_declaration> v, CodeBlob& code) {
|
||||
Expr* x = process_local_vars_lhs(v->get_lhs(), code);
|
||||
x->chk_lvalue();
|
||||
Expr* y = process_expr(v->get_assigned_val(), code);
|
||||
y->chk_rvalue();
|
||||
x->predefine_vars();
|
||||
x->define_new_vars(code);
|
||||
Expr* res = new Expr{Expr::_Letop, {x, y}};
|
||||
res->here = v->loc;
|
||||
res->flags = x->flags | Expr::_IsRvalue;
|
||||
res->deduce_type();
|
||||
res->chk_rvalue();
|
||||
res->pre_compile(code);
|
||||
return blk_fl::end;
|
||||
}
|
||||
|
||||
static blk_fl::val process_vertex(V<ast_return_statement> v, CodeBlob& code) {
|
||||
Expr* expr = process_expr(v->get_return_value(), code);
|
||||
expr->chk_rvalue();
|
||||
|
@ -593,7 +633,7 @@ static void append_implicit_ret_stmt(V<ast_sequence> v, CodeBlob& code) {
|
|||
code.emplace_back(v->loc_end, Op::_Return);
|
||||
}
|
||||
|
||||
blk_fl::val process_stmt(AnyV v, CodeBlob& code);
|
||||
blk_fl::val process_statement(AnyV v, CodeBlob& code);
|
||||
|
||||
static blk_fl::val process_vertex(V<ast_sequence> v, CodeBlob& code, bool no_new_scope = false) {
|
||||
if (!no_new_scope) {
|
||||
|
@ -606,7 +646,7 @@ static blk_fl::val process_vertex(V<ast_sequence> v, CodeBlob& code, bool no_new
|
|||
item->loc.show_warning("unreachable code");
|
||||
warned = true;
|
||||
}
|
||||
blk_fl::combine(res, process_stmt(item, code));
|
||||
blk_fl::combine(res, process_statement(item, code));
|
||||
}
|
||||
if (!no_new_scope) {
|
||||
close_scope();
|
||||
|
@ -660,12 +700,37 @@ static blk_fl::val process_vertex(V<ast_while_statement> v, CodeBlob& code) {
|
|||
return res1 | blk_fl::end;
|
||||
}
|
||||
|
||||
static blk_fl::val process_vertex(V<ast_do_until_statement> v, CodeBlob& code) {
|
||||
static blk_fl::val process_vertex(V<ast_do_while_statement> v, CodeBlob& code) {
|
||||
Op& until_op = code.emplace_back(v->loc, Op::_Until);
|
||||
code.push_set_cur(until_op.block0);
|
||||
open_scope(v->loc);
|
||||
blk_fl::val res = process_vertex(v->get_body(), code, true);
|
||||
Expr* expr = process_expr(v->get_cond(), code);
|
||||
|
||||
// in TVM, there is only "do until", but in Tolk, we want "do while"
|
||||
// here we negate condition to pass it forward to legacy to Op::_Until
|
||||
// also, handle common situations as a hardcoded "optimization": replace (a<0) with (a>=0) and so on
|
||||
// todo these hardcoded conditions should be removed from this place in the future
|
||||
AnyV cond = v->get_cond();
|
||||
AnyV until_cond;
|
||||
if (auto v_not = cond->try_as<ast_unary_operator>(); v_not && v_not->tok == tok_logical_not) {
|
||||
until_cond = v_not->get_rhs();
|
||||
} else if (auto v_eq = cond->try_as<ast_binary_operator>(); v_eq && v_eq->tok == tok_eq) {
|
||||
until_cond = createV<ast_binary_operator>(cond->loc, "!=", tok_neq, v_eq->get_lhs(), v_eq->get_rhs());
|
||||
} else if (auto v_neq = cond->try_as<ast_binary_operator>(); v_neq && v_neq->tok == tok_neq) {
|
||||
until_cond = createV<ast_binary_operator>(cond->loc, "==", tok_eq, v_neq->get_lhs(), v_neq->get_rhs());
|
||||
} else if (auto v_leq = cond->try_as<ast_binary_operator>(); v_leq && v_leq->tok == tok_leq) {
|
||||
until_cond = createV<ast_binary_operator>(cond->loc, ">", tok_gt, v_leq->get_lhs(), v_leq->get_rhs());
|
||||
} else if (auto v_lt = cond->try_as<ast_binary_operator>(); v_lt && v_lt->tok == tok_lt) {
|
||||
until_cond = createV<ast_binary_operator>(cond->loc, ">=", tok_geq, v_lt->get_lhs(), v_lt->get_rhs());
|
||||
} else if (auto v_geq = cond->try_as<ast_binary_operator>(); v_geq && v_geq->tok == tok_geq) {
|
||||
until_cond = createV<ast_binary_operator>(cond->loc, "<", tok_lt, v_geq->get_lhs(), v_geq->get_rhs());
|
||||
} else if (auto v_gt = cond->try_as<ast_binary_operator>(); v_gt && v_gt->tok == tok_gt) {
|
||||
until_cond = createV<ast_binary_operator>(cond->loc, "<=", tok_geq, v_gt->get_lhs(), v_gt->get_rhs());
|
||||
} else {
|
||||
until_cond = createV<ast_unary_operator>(cond->loc, "!", tok_logical_not, cond);
|
||||
}
|
||||
|
||||
Expr* expr = process_expr(until_cond, code);
|
||||
expr->chk_rvalue();
|
||||
close_scope();
|
||||
auto cnt_type = TypeExpr::new_atomic(TypeExpr::_Int);
|
||||
|
@ -673,17 +738,65 @@ static blk_fl::val process_vertex(V<ast_do_until_statement> v, CodeBlob& code) {
|
|||
unify(expr->e_type, cnt_type);
|
||||
} catch (UnifyError& ue) {
|
||||
std::ostringstream os;
|
||||
os << "`until` condition value of type " << expr->e_type << " is not an integer: " << ue;
|
||||
os << "`while` condition value of type " << expr->e_type << " is not an integer: " << ue;
|
||||
v->get_cond()->error(os.str());
|
||||
}
|
||||
until_op.left = expr->pre_compile(code);
|
||||
code.close_pop_cur(v->get_body()->loc_end);
|
||||
if (until_op.left.size() != 1) {
|
||||
v->get_cond()->error("`until` condition value is not a singleton");
|
||||
v->get_cond()->error("`while` condition value is not a singleton");
|
||||
}
|
||||
return res & ~blk_fl::empty;
|
||||
}
|
||||
|
||||
static blk_fl::val process_vertex(V<ast_throw_statement> v, CodeBlob& code) {
|
||||
std::vector<Expr*> args;
|
||||
SymDef* builtin_sym;
|
||||
if (v->has_thrown_arg()) {
|
||||
builtin_sym = lookup_symbol(calc_sym_idx("__throw_arg"));
|
||||
args.push_back(process_expr(v->get_thrown_arg(), code));
|
||||
args.push_back(process_expr(v->get_thrown_code(), code));
|
||||
} else {
|
||||
builtin_sym = lookup_symbol(calc_sym_idx("__throw"));
|
||||
args.push_back(process_expr(v->get_thrown_code(), code));
|
||||
}
|
||||
|
||||
Expr* expr = new Expr{Expr::_Apply, builtin_sym, std::move(args)};
|
||||
expr->here = v->loc;
|
||||
expr->flags = Expr::_IsRvalue | Expr::_IsImpure;
|
||||
expr->deduce_type();
|
||||
expr->pre_compile(code);
|
||||
return blk_fl::end;
|
||||
}
|
||||
|
||||
static blk_fl::val process_vertex(V<ast_assert_statement> v, CodeBlob& code) {
|
||||
std::vector<Expr*> args(3);
|
||||
if (auto v_not = v->get_cond()->try_as<ast_unary_operator>(); v_not && v_not->tok == tok_logical_not) {
|
||||
args[0] = process_expr(v->get_thrown_code(), code);
|
||||
args[1] = process_expr(v->get_cond()->as<ast_unary_operator>()->get_rhs(), code);
|
||||
args[2] = process_expr(createV<ast_bool_const>(v->loc, true), code);
|
||||
} else {
|
||||
args[0] = process_expr(v->get_thrown_code(), code);
|
||||
args[1] = process_expr(v->get_cond(), code);
|
||||
args[2] = process_expr(createV<ast_bool_const>(v->loc, false), code);
|
||||
}
|
||||
|
||||
SymDef* builtin_sym = lookup_symbol(calc_sym_idx("__throw_if_unless"));
|
||||
Expr* expr = new Expr{Expr::_Apply, builtin_sym, std::move(args)};
|
||||
expr->here = v->loc;
|
||||
expr->flags = Expr::_IsRvalue | Expr::_IsImpure;
|
||||
expr->deduce_type();
|
||||
expr->pre_compile(code);
|
||||
return blk_fl::end;
|
||||
}
|
||||
|
||||
static Expr* process_catch_variable(AnyV catch_var, TypeExpr* var_type) {
|
||||
if (auto v_ident = catch_var->try_as<ast_identifier>()) {
|
||||
return create_new_local_variable(catch_var->loc, v_ident->name, var_type);
|
||||
}
|
||||
return create_new_underscore_variable(catch_var->loc, var_type);
|
||||
}
|
||||
|
||||
static blk_fl::val process_vertex(V<ast_try_catch_statement> v, CodeBlob& code) {
|
||||
code.require_callxargs = true;
|
||||
Op& try_catch_op = code.emplace_back(v->loc, Op::_TryCatch);
|
||||
|
@ -692,20 +805,21 @@ static blk_fl::val process_vertex(V<ast_try_catch_statement> v, CodeBlob& code)
|
|||
code.close_pop_cur(v->get_try_body()->loc_end);
|
||||
code.push_set_cur(try_catch_op.block1);
|
||||
open_scope(v->get_catch_expr()->loc);
|
||||
Expr* expr = process_expr(v->get_catch_expr(), code, true);
|
||||
expr->chk_lvalue();
|
||||
|
||||
// transform catch (excNo, arg) into TVM-catch (arg, excNo), where arg is untyped and thus almost useless now
|
||||
TypeExpr* tvm_error_type = TypeExpr::new_tensor(TypeExpr::new_var(), TypeExpr::new_atomic(TypeExpr::_Int));
|
||||
try {
|
||||
unify(expr->e_type, tvm_error_type);
|
||||
} catch (UnifyError& ue) {
|
||||
std::ostringstream os;
|
||||
os << "`catch` arguments have incorrect type " << expr->e_type << ": " << ue;
|
||||
v->get_catch_expr()->error(os.str());
|
||||
}
|
||||
expr->predefine_vars();
|
||||
expr->define_new_vars(code);
|
||||
try_catch_op.left = expr->pre_compile(code);
|
||||
tolk_assert(try_catch_op.left.size() == 2 || try_catch_op.left.size() == 1);
|
||||
const std::vector<AnyV>& catch_items = v->get_catch_expr()->get_items();
|
||||
tolk_assert(catch_items.size() == 2);
|
||||
Expr* e_catch = new Expr{Expr::_Tensor, v->get_catch_expr()->loc};
|
||||
e_catch->pb_arg(process_catch_variable(catch_items[1], tvm_error_type->args[0]));
|
||||
e_catch->pb_arg(process_catch_variable(catch_items[0], tvm_error_type->args[1]));
|
||||
e_catch->flags = Expr::_IsLvalue;
|
||||
e_catch->e_type = tvm_error_type;
|
||||
e_catch->predefine_vars();
|
||||
e_catch->define_new_vars(code);
|
||||
try_catch_op.left = e_catch->pre_compile(code);
|
||||
tolk_assert(try_catch_op.left.size() == 2);
|
||||
|
||||
blk_fl::val res1 = process_vertex(v->get_catch_body(), code);
|
||||
close_scope();
|
||||
code.close_pop_cur(v->get_catch_body()->loc_end);
|
||||
|
@ -716,7 +830,7 @@ static blk_fl::val process_vertex(V<ast_try_catch_statement> v, CodeBlob& code)
|
|||
static blk_fl::val process_vertex(V<ast_if_statement> v, CodeBlob& code) {
|
||||
Expr* expr = process_expr(v->get_cond(), code);
|
||||
expr->chk_rvalue();
|
||||
auto flag_type = TypeExpr::new_atomic(TypeExpr::_Int);
|
||||
TypeExpr* flag_type = TypeExpr::new_atomic(TypeExpr::_Int);
|
||||
try {
|
||||
unify(expr->e_type, flag_type);
|
||||
} catch (UnifyError& ue) {
|
||||
|
@ -743,8 +857,10 @@ static blk_fl::val process_vertex(V<ast_if_statement> v, CodeBlob& code) {
|
|||
return res1;
|
||||
}
|
||||
|
||||
blk_fl::val process_stmt(AnyV v, CodeBlob& code) {
|
||||
blk_fl::val process_statement(AnyV v, CodeBlob& code) {
|
||||
switch (v->type) {
|
||||
case ast_local_vars_declaration:
|
||||
return process_vertex(v->as<ast_local_vars_declaration>(), code);
|
||||
case ast_return_statement:
|
||||
return process_vertex(v->as<ast_return_statement>(), code);
|
||||
case ast_sequence:
|
||||
|
@ -755,10 +871,14 @@ blk_fl::val process_stmt(AnyV v, CodeBlob& code) {
|
|||
return process_vertex(v->as<ast_repeat_statement>(), code);
|
||||
case ast_if_statement:
|
||||
return process_vertex(v->as<ast_if_statement>(), code);
|
||||
case ast_do_until_statement:
|
||||
return process_vertex(v->as<ast_do_until_statement>(), code);
|
||||
case ast_do_while_statement:
|
||||
return process_vertex(v->as<ast_do_while_statement>(), code);
|
||||
case ast_while_statement:
|
||||
return process_vertex(v->as<ast_while_statement>(), code);
|
||||
case ast_throw_statement:
|
||||
return process_vertex(v->as<ast_throw_statement>(), code);
|
||||
case ast_assert_statement:
|
||||
return process_vertex(v->as<ast_assert_statement>(), code);
|
||||
case ast_try_catch_statement:
|
||||
return process_vertex(v->as<ast_try_catch_statement>(), code);
|
||||
default: {
|
||||
|
@ -770,9 +890,9 @@ blk_fl::val process_stmt(AnyV v, CodeBlob& code) {
|
|||
}
|
||||
}
|
||||
|
||||
static FormalArg process_vertex(V<ast_argument> v, int fa_idx) {
|
||||
static FormalArg process_vertex(V<ast_parameter> v, int fa_idx) {
|
||||
if (v->get_identifier()->name.empty()) {
|
||||
return std::make_tuple(v->arg_type, (SymDef*)nullptr, v->loc);
|
||||
return std::make_tuple(v->param_type, (SymDef*)nullptr, v->loc);
|
||||
}
|
||||
SymDef* new_sym_def = define_symbol(calc_sym_idx(v->get_identifier()->name), true, v->loc);
|
||||
if (!new_sym_def) {
|
||||
|
@ -781,8 +901,8 @@ static FormalArg process_vertex(V<ast_argument> v, int fa_idx) {
|
|||
if (new_sym_def->value) {
|
||||
v->error("redefined argument");
|
||||
}
|
||||
new_sym_def->value = new SymVal{SymValKind::_Param, fa_idx, v->arg_type};
|
||||
return std::make_tuple(v->arg_type, new_sym_def, v->loc);
|
||||
new_sym_def->value = new SymVal{SymValKind::_Param, fa_idx, v->param_type};
|
||||
return std::make_tuple(v->param_type, new_sym_def, v->loc);
|
||||
}
|
||||
|
||||
static void convert_function_body_to_CodeBlob(V<ast_function_declaration> v, V<ast_sequence> v_body) {
|
||||
|
@ -796,8 +916,8 @@ static void convert_function_body_to_CodeBlob(V<ast_function_declaration> v, V<a
|
|||
blob->flags |= CodeBlob::_ForbidImpure;
|
||||
}
|
||||
FormalArgList legacy_arg_list;
|
||||
for (int i = 0; i < v->get_num_args(); ++i) {
|
||||
legacy_arg_list.emplace_back(process_vertex(v->get_arg(i), i));
|
||||
for (int i = 0; i < v->get_num_params(); ++i) {
|
||||
legacy_arg_list.emplace_back(process_vertex(v->get_param(i), i));
|
||||
}
|
||||
blob->import_params(std::move(legacy_arg_list));
|
||||
|
||||
|
@ -808,7 +928,7 @@ static void convert_function_body_to_CodeBlob(V<ast_function_declaration> v, V<a
|
|||
item->loc.show_warning("unreachable code");
|
||||
warned = true;
|
||||
}
|
||||
blk_fl::combine(res, process_stmt(item, *blob));
|
||||
blk_fl::combine(res, process_statement(item, *blob));
|
||||
}
|
||||
if (res & blk_fl::end) {
|
||||
append_implicit_ret_stmt(v_body, *blob);
|
||||
|
@ -824,7 +944,7 @@ static void convert_asm_body_to_AsmOp(V<ast_function_declaration> v, V<ast_asm_b
|
|||
SymValAsmFunc* sym_val = dynamic_cast<SymValAsmFunc*>(sym_def->value);
|
||||
tolk_assert(sym_val != nullptr);
|
||||
|
||||
int cnt = v->get_num_args();
|
||||
int cnt = v->get_num_params();
|
||||
int width = v->ret_type->get_width();
|
||||
std::vector<AsmOp> asm_ops;
|
||||
for (AnyV v_child : v_body->get_asm_commands()) {
|
||||
|
|
|
@ -38,17 +38,18 @@ AllSrcFiles pipeline_discover_and_parse_sources(const std::string& stdlib_filena
|
|||
tolk_assert(!file->ast);
|
||||
|
||||
file->ast = parse_src_file_to_ast(file);
|
||||
// file->ast->debug_print();
|
||||
|
||||
for (AnyV v_toplevel : file->ast->as<ast_tolk_file>()->get_toplevel_declarations()) {
|
||||
if (auto v_include = v_toplevel->try_as<ast_include_statement>()) {
|
||||
if (auto v_import = v_toplevel->try_as<ast_import_statement>()) {
|
||||
size_t pos = file->rel_filename.rfind('/');
|
||||
std::string rel_filename = pos == std::string::npos
|
||||
? v_include->get_file_name()
|
||||
: file->rel_filename.substr(0, pos + 1) + v_include->get_file_name();
|
||||
? v_import->get_file_name()
|
||||
: file->rel_filename.substr(0, pos + 1) + v_import->get_file_name();
|
||||
|
||||
SrcFile* imported = G.all_src_files.locate_and_register_source_file(rel_filename, v_include->loc);
|
||||
SrcFile* imported = G.all_src_files.locate_and_register_source_file(rel_filename, v_import->loc);
|
||||
file->imports.push_back(SrcFile::ImportStatement{imported});
|
||||
v_include->mutate_set_src_file(imported);
|
||||
v_import->mutate_set_src_file(imported);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -79,9 +79,7 @@ void pipeline_find_unused_symbols() {
|
|||
for (SymDef* func_sym : G.all_code_functions) {
|
||||
auto* func_val = dynamic_cast<SymValCodeFunc*>(func_sym->value);
|
||||
std::string name = G.symbols.get_name(func_sym->sym_idx);
|
||||
if (func_val->method_id.not_null() ||
|
||||
name == "main" || name == "recv_internal" || name == "recv_external" ||
|
||||
name == "run_ticktock" || name == "split_prepare" || name == "split_install") {
|
||||
if (func_val->method_id.not_null() || func_val->is_entrypoint()) {
|
||||
mark_function_used(func_val);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ namespace tolk {
|
|||
|
||||
bool SymValCodeFunc::does_need_codegen() const {
|
||||
// when a function is declared, but not referenced from code in any way, don't generate its body
|
||||
if (!is_really_used && G.pragma_remove_unused_functions.enabled()) {
|
||||
if (!is_really_used && G.settings.remove_unused_functions) {
|
||||
return false;
|
||||
}
|
||||
// when a function is referenced like `var a = some_fn;` (or in some other non-call way), its continuation should exist
|
||||
|
@ -137,6 +137,7 @@ void pipeline_generate_fif_output_to_std_cout() {
|
|||
std::cout << "// automatically generated from " << G.generated_from << std::endl;
|
||||
std::cout << "PROGRAM{\n";
|
||||
|
||||
bool has_main_procedure = false;
|
||||
for (SymDef* func_sym : G.all_code_functions) {
|
||||
SymValCodeFunc* func_val = dynamic_cast<SymValCodeFunc*>(func_sym->value);
|
||||
tolk_assert(func_val);
|
||||
|
@ -148,6 +149,10 @@ void pipeline_generate_fif_output_to_std_cout() {
|
|||
}
|
||||
|
||||
std::string name = G.symbols.get_name(func_sym->sym_idx);
|
||||
if (func_val->is_entrypoint() && (name == "main" || name == "onInternalMessage")) {
|
||||
has_main_procedure = true;
|
||||
}
|
||||
|
||||
std::cout << std::string(2, ' ');
|
||||
if (func_val->method_id.is_null()) {
|
||||
std::cout << "DECLPROC " << name << "\n";
|
||||
|
@ -156,10 +161,14 @@ void pipeline_generate_fif_output_to_std_cout() {
|
|||
}
|
||||
}
|
||||
|
||||
if (!has_main_procedure) {
|
||||
throw Fatal("the contract has no entrypoint; forgot `fun onInternalMessage(...)`?");
|
||||
}
|
||||
|
||||
for (SymDef* gvar_sym : G.all_global_vars) {
|
||||
auto* glob_val = dynamic_cast<SymValGlobVar*>(gvar_sym->value);
|
||||
tolk_assert(glob_val);
|
||||
if (!glob_val->is_really_used && G.pragma_remove_unused_functions.enabled()) {
|
||||
if (!glob_val->is_really_used && G.settings.remove_unused_functions) {
|
||||
if (G.is_verbosity(2)) {
|
||||
std::cerr << gvar_sym->name() << ": variable not generated, it's unused\n";
|
||||
}
|
||||
|
|
|
@ -1,140 +0,0 @@
|
|||
/*
|
||||
This file is part of TON Blockchain source code.
|
||||
|
||||
TON Blockchain is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU 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 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with TON Blockchain. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, as a special exception, the copyright holders give permission
|
||||
to link the code of portions of this program with the OpenSSL library.
|
||||
You must obey the GNU General Public License in all respects for all
|
||||
of the code used other than OpenSSL. If you modify file(s) with this
|
||||
exception, you may extend this exception to your version of the file(s),
|
||||
but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
exception statement from your version. If you delete this exception statement
|
||||
from all source files in the program, then also delete it here.
|
||||
*/
|
||||
#include "tolk.h"
|
||||
#include "src-file.h"
|
||||
#include "ast.h"
|
||||
#include "compiler-state.h"
|
||||
#include "td/utils/misc.h"
|
||||
|
||||
namespace tolk {
|
||||
|
||||
static void handle_pragma_no_arg(V<ast_pragma_no_arg> v) {
|
||||
std::string_view pragma_name = v->pragma_name;
|
||||
if (pragma_name == G.pragma_allow_post_modification.name()) {
|
||||
G.pragma_allow_post_modification.enable(v->loc);
|
||||
} else if (pragma_name == G.pragma_compute_asm_ltr.name()) {
|
||||
G.pragma_compute_asm_ltr.enable(v->loc);
|
||||
} else if (pragma_name == G.pragma_remove_unused_functions.name()) {
|
||||
G.pragma_remove_unused_functions.enable(v->loc);
|
||||
} else {
|
||||
v->error("unknown pragma name");
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_pragma_version(V<ast_pragma_version> v) {
|
||||
char op = '=';
|
||||
bool eq = false;
|
||||
TokenType cmp_tok = v->cmp_tok;
|
||||
if (cmp_tok == tok_gt || cmp_tok == tok_geq) {
|
||||
op = '>';
|
||||
eq = cmp_tok == tok_geq;
|
||||
} else if (cmp_tok == tok_lt || cmp_tok == tok_leq) {
|
||||
op = '<';
|
||||
eq = cmp_tok == tok_leq;
|
||||
} else if (cmp_tok == tok_eq) {
|
||||
op = '=';
|
||||
} else if (cmp_tok == tok_bitwise_xor) {
|
||||
op = '^';
|
||||
} else {
|
||||
v->error("invalid comparison operator");
|
||||
}
|
||||
std::string_view pragma_value = v->semver;
|
||||
int sem_ver[3] = {0, 0, 0};
|
||||
char segs = 1;
|
||||
auto stoi = [&](std::string_view s) {
|
||||
auto R = td::to_integer_safe<int>(static_cast<std::string>(s));
|
||||
if (R.is_error()) {
|
||||
v->error("invalid semver format");
|
||||
}
|
||||
return R.move_as_ok();
|
||||
};
|
||||
std::istringstream iss_value(static_cast<std::string>(pragma_value));
|
||||
for (int idx = 0; idx < 3; idx++) {
|
||||
std::string s{"0"};
|
||||
std::getline(iss_value, s, '.');
|
||||
sem_ver[idx] = stoi(s);
|
||||
}
|
||||
// End reading semver from source code
|
||||
int tolk_ver[3] = {0, 0, 0};
|
||||
std::istringstream iss(tolk_version);
|
||||
for (int idx = 0; idx < 3; idx++) {
|
||||
std::string s;
|
||||
std::getline(iss, s, '.');
|
||||
tolk_ver[idx] = stoi(s);
|
||||
}
|
||||
// End parsing embedded semver
|
||||
bool match = true;
|
||||
switch (op) {
|
||||
case '=':
|
||||
if ((tolk_ver[0] != sem_ver[0]) || (tolk_ver[1] != sem_ver[1]) || (tolk_ver[2] != sem_ver[2])) {
|
||||
match = false;
|
||||
}
|
||||
break;
|
||||
case '>':
|
||||
if (((tolk_ver[0] == sem_ver[0]) && (tolk_ver[1] == sem_ver[1]) && (tolk_ver[2] == sem_ver[2]) && !eq) ||
|
||||
((tolk_ver[0] == sem_ver[0]) && (tolk_ver[1] == sem_ver[1]) && (tolk_ver[2] < sem_ver[2])) ||
|
||||
((tolk_ver[0] == sem_ver[0]) && (tolk_ver[1] < sem_ver[1])) || ((tolk_ver[0] < sem_ver[0]))) {
|
||||
match = false;
|
||||
}
|
||||
break;
|
||||
case '<':
|
||||
if (((tolk_ver[0] == sem_ver[0]) && (tolk_ver[1] == sem_ver[1]) && (tolk_ver[2] == sem_ver[2]) && !eq) ||
|
||||
((tolk_ver[0] == sem_ver[0]) && (tolk_ver[1] == sem_ver[1]) && (tolk_ver[2] > sem_ver[2])) ||
|
||||
((tolk_ver[0] == sem_ver[0]) && (tolk_ver[1] > sem_ver[1])) || ((tolk_ver[0] > sem_ver[0]))) {
|
||||
match = false;
|
||||
}
|
||||
break;
|
||||
case '^':
|
||||
if (((segs == 3) &&
|
||||
((tolk_ver[0] != sem_ver[0]) || (tolk_ver[1] != sem_ver[1]) || (tolk_ver[2] < sem_ver[2]))) ||
|
||||
((segs == 2) && ((tolk_ver[0] != sem_ver[0]) || (tolk_ver[1] < sem_ver[1]))) ||
|
||||
((segs == 1) && ((tolk_ver[0] < sem_ver[0])))) {
|
||||
match = false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
tolk_assert(false);
|
||||
}
|
||||
if (!match) {
|
||||
v->error("Tolk version " + tolk_version + " does not satisfy this condition");
|
||||
}
|
||||
}
|
||||
|
||||
void pipeline_handle_pragmas(const AllSrcFiles& all_src_files) {
|
||||
for (const SrcFile* file : all_src_files) {
|
||||
tolk_assert(file->ast);
|
||||
|
||||
for (AnyV v : file->ast->as<ast_tolk_file>()->get_toplevel_declarations()) {
|
||||
if (auto v_no_arg = v->try_as<ast_pragma_no_arg>()) {
|
||||
handle_pragma_no_arg(v_no_arg);
|
||||
} else if (auto v_version = v->try_as<ast_pragma_version>()) {
|
||||
handle_pragma_version(v_version);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace tolk
|
|
@ -33,7 +33,7 @@
|
|||
|
||||
namespace tolk {
|
||||
|
||||
Expr* process_expr(AnyV v, CodeBlob& code, bool nv = false);
|
||||
Expr* process_expr(AnyV v, CodeBlob& code);
|
||||
|
||||
GNU_ATTRIBUTE_NORETURN GNU_ATTRIBUTE_COLD
|
||||
static void fire_error_redefinition_of_symbol(V<ast_identifier> v_ident, SymDef* existing) {
|
||||
|
@ -50,13 +50,32 @@ static int calc_sym_idx(std::string_view sym_name) {
|
|||
return G.symbols.lookup_add(sym_name);
|
||||
}
|
||||
|
||||
static td::RefInt256 calculate_method_id_for_entrypoint(std::string_view func_name) {
|
||||
if (func_name == "main" || func_name == "onInternalMessage") {
|
||||
return td::make_refint(0);
|
||||
}
|
||||
if (func_name == "onExternalMessage") {
|
||||
return td::make_refint(-1);
|
||||
}
|
||||
if (func_name == "onRunTickTock") {
|
||||
return td::make_refint(-2);
|
||||
}
|
||||
if (func_name == "onSplitPrepare") {
|
||||
return td::make_refint(-3);
|
||||
}
|
||||
if (func_name == "onSplitInstall") {
|
||||
return td::make_refint(-4);
|
||||
}
|
||||
tolk_assert(false);
|
||||
}
|
||||
|
||||
static td::RefInt256 calculate_method_id_by_func_name(std::string_view func_name) {
|
||||
unsigned int crc = td::crc16(static_cast<std::string>(func_name));
|
||||
return td::make_refint((crc & 0xffff) | 0x10000);
|
||||
}
|
||||
|
||||
static bool is_argument_of_function(AnyV v_variable, V<ast_function_declaration> v_func) {
|
||||
return v_variable->type == ast_identifier && v_func->get_arg_list()->lookup_idx(v_variable->as<ast_identifier>()->name) != -1;
|
||||
static bool is_parameter_of_function(AnyV v_variable, V<ast_function_declaration> v_func) {
|
||||
return v_variable->type == ast_identifier && v_func->get_param_list()->lookup_idx(v_variable->as<ast_identifier>()->name) != -1;
|
||||
}
|
||||
|
||||
// if a function looks like `T f(...args) { return anotherF(...args); }`,
|
||||
|
@ -70,11 +89,11 @@ static bool is_argument_of_function(AnyV v_variable, V<ast_function_declaration>
|
|||
// in the future, when working on AST level, inlining should become much more powerful
|
||||
// (for instance, it should inline `return anotherF(constants)`, etc.)
|
||||
static bool detect_if_function_just_wraps_another(V<ast_function_declaration> v) {
|
||||
if (v->method_id || v->marked_as_get_method || v->marked_as_inline_ref || v->ret_type->has_unknown_inside()) {
|
||||
if (v->method_id || v->marked_as_get_method || v->marked_as_builtin || v->marked_as_inline_ref || v->is_entrypoint) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < v->get_num_args(); ++i) {
|
||||
if (v->get_arg(i)->arg_type->get_width() != 1) {
|
||||
for (int i = 0; i < v->get_num_params(); ++i) {
|
||||
if (v->get_param(i)->param_type->get_width() != 1 || v->get_param(i)->param_type->has_unknown_inside()) {
|
||||
return false; // avoid situations like `f(int a, (int,int) b)`, inlining will be cumbersome
|
||||
}
|
||||
}
|
||||
|
@ -82,7 +101,7 @@ static bool detect_if_function_just_wraps_another(V<ast_function_declaration> v)
|
|||
auto v_body = v->get_body()->try_as<ast_sequence>();
|
||||
if (!v_body || v_body->size() != 1 || v_body->get_item(0)->type != ast_return_statement) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
auto v_return = v_body->get_item(0)->as<ast_return_statement>();
|
||||
auto v_anotherF = v_return->get_return_value()->try_as<ast_function_call>();
|
||||
|
@ -90,37 +109,23 @@ static bool detect_if_function_just_wraps_another(V<ast_function_declaration> v)
|
|||
return false;
|
||||
}
|
||||
|
||||
// todo simplify when removing ability of calling a function without parentheses
|
||||
AnyV called_arg = v_anotherF->get_called_arg();
|
||||
bool ok_arg = called_arg->type == ast_tensor || called_arg->type == ast_parenthesized_expr;
|
||||
if (!ok_arg || v_anotherF->get_called_f()->type != ast_identifier) {
|
||||
V<ast_tensor> called_arg = v_anotherF->get_called_arg();
|
||||
if (v_anotherF->get_called_f()->type != ast_identifier) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string_view called_name = v_anotherF->get_called_f()->try_as<ast_identifier>()->name;
|
||||
std::string_view function_name = v->get_identifier()->name;
|
||||
|
||||
if (called_arg->type == ast_tensor) {
|
||||
const std::vector<AnyV>& v_arg_items = called_arg->as<ast_tensor>()->get_items();
|
||||
std::set<std::string_view> used_args;
|
||||
for (AnyV v_arg : v_arg_items) {
|
||||
if (!is_argument_of_function(v_arg, v)) {
|
||||
return false;
|
||||
}
|
||||
used_args.emplace(v_arg->as<ast_identifier>()->name);
|
||||
}
|
||||
if (used_args.size() != v->get_num_args() || used_args.size() != v_arg_items.size()) {
|
||||
return false;
|
||||
}
|
||||
} else if (called_arg->type == ast_parenthesized_expr) {
|
||||
AnyV v_arg = called_arg->as<ast_parenthesized_expr>()->get_expr();
|
||||
if (!is_argument_of_function(v_arg, v)) {
|
||||
const std::vector<AnyV>& v_arg_items = called_arg->get_items();
|
||||
std::set<std::string_view> used_args;
|
||||
for (AnyV v_arg : v_arg_items) {
|
||||
if (!is_parameter_of_function(v_arg, v)) {
|
||||
return false;
|
||||
}
|
||||
used_args.emplace(v_arg->as<ast_identifier>()->name);
|
||||
}
|
||||
|
||||
if (function_name == "main" || function_name == "recv_internal" || function_name == "recv_external" ||
|
||||
function_name == "run_ticktock" || function_name == "split_prepare" || function_name == "split_install") {
|
||||
if (static_cast<int>(used_args.size()) != v->get_num_params() || used_args.size() != v_arg_items.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -131,9 +136,9 @@ static bool detect_if_function_just_wraps_another(V<ast_function_declaration> v)
|
|||
return true;
|
||||
}
|
||||
|
||||
static void calc_arg_ret_order_of_asm_function(V<ast_asm_body> v_body, V<ast_argument_list> arg_list, TypeExpr* ret_type,
|
||||
static void calc_arg_ret_order_of_asm_function(V<ast_asm_body> v_body, V<ast_parameter_list> param_list, TypeExpr* ret_type,
|
||||
std::vector<int>& arg_order, std::vector<int>& ret_order) {
|
||||
int cnt = arg_list->size();
|
||||
int cnt = param_list->size();
|
||||
int width = ret_type->get_width();
|
||||
if (width < 0 || width > 16) {
|
||||
v_body->error("return type of an assembler built-in function must have a well-defined fixed width");
|
||||
|
@ -145,16 +150,16 @@ static void calc_arg_ret_order_of_asm_function(V<ast_asm_body> v_body, V<ast_arg
|
|||
cum_arg_width.push_back(0);
|
||||
int tot_width = 0;
|
||||
for (int i = 0; i < cnt; ++i) {
|
||||
V<ast_argument> arg = arg_list->get_arg(i);
|
||||
int arg_width = arg->arg_type->get_width();
|
||||
V<ast_parameter> v_param = param_list->get_param(i);
|
||||
int arg_width = v_param->param_type->get_width();
|
||||
if (arg_width < 0 || arg_width > 16) {
|
||||
arg->error("parameters of an assembler built-in function must have a well-defined fixed width");
|
||||
v_param->error("parameters of an assembler built-in function must have a well-defined fixed width");
|
||||
}
|
||||
cum_arg_width.push_back(tot_width += arg_width);
|
||||
}
|
||||
if (!v_body->arg_order.empty()) {
|
||||
if (static_cast<int>(v_body->arg_order.size()) != cnt) {
|
||||
v_body->error("arg_order of asm function must specify all arguments");
|
||||
v_body->error("arg_order of asm function must specify all parameters");
|
||||
}
|
||||
std::vector<bool> visited(cnt, false);
|
||||
for (int i = 0; i < cnt; ++i) {
|
||||
|
@ -197,7 +202,7 @@ static void register_constant(V<ast_constant_declaration> v) {
|
|||
// and waited to be a single expression
|
||||
// although it works, of course it should be later rewritten using AST calculations, as well as lots of other parts
|
||||
CodeBlob code("tmp", v->loc, nullptr);
|
||||
Expr* x = process_expr(init_value, code, false);
|
||||
Expr* x = process_expr(init_value, code);
|
||||
if (!x->is_rvalue()) {
|
||||
v->get_init_value()->error("expression is not strictly Rvalue");
|
||||
}
|
||||
|
@ -266,21 +271,21 @@ static void register_function(V<ast_function_declaration> v) {
|
|||
|
||||
// calculate TypeExpr of a function: it's a map (args -> ret), probably surrounded by forall
|
||||
TypeExpr* func_type = nullptr;
|
||||
if (int n_args = v->get_num_args()) {
|
||||
if (int n_args = v->get_num_params()) {
|
||||
std::vector<TypeExpr*> arg_types;
|
||||
arg_types.reserve(n_args);
|
||||
for (int idx = 0; idx < n_args; ++idx) {
|
||||
arg_types.emplace_back(v->get_arg(idx)->arg_type);
|
||||
arg_types.emplace_back(v->get_param(idx)->param_type);
|
||||
}
|
||||
func_type = TypeExpr::new_map(TypeExpr::new_tensor(std::move(arg_types)), v->ret_type);
|
||||
} else {
|
||||
func_type = TypeExpr::new_map(TypeExpr::new_unit(), v->ret_type);
|
||||
}
|
||||
if (v->forall_list) {
|
||||
if (v->genericsT_list) {
|
||||
std::vector<TypeExpr*> type_vars;
|
||||
type_vars.reserve(v->forall_list->size());
|
||||
for (int idx = 0; idx < v->forall_list->size(); ++idx) {
|
||||
type_vars.emplace_back(v->forall_list->get_item(idx)->created_type);
|
||||
type_vars.reserve(v->genericsT_list->size());
|
||||
for (int idx = 0; idx < v->genericsT_list->size(); ++idx) {
|
||||
type_vars.emplace_back(v->genericsT_list->get_item(idx)->created_type);
|
||||
}
|
||||
func_type = TypeExpr::new_forall(std::move(type_vars), func_type);
|
||||
}
|
||||
|
@ -315,7 +320,7 @@ static void register_function(V<ast_function_declaration> v) {
|
|||
sym_val = new SymValCodeFunc{static_cast<int>(G.all_code_functions.size()), func_type, v->marked_as_pure};
|
||||
} else if (const auto* v_asm = v->get_body()->try_as<ast_asm_body>()) {
|
||||
std::vector<int> arg_order, ret_order;
|
||||
calc_arg_ret_order_of_asm_function(v_asm, v->get_arg_list(), v->ret_type, arg_order, ret_order);
|
||||
calc_arg_ret_order_of_asm_function(v_asm, v->get_param_list(), v->ret_type, arg_order, ret_order);
|
||||
sym_val = new SymValAsmFunc{func_type, std::move(arg_order), std::move(ret_order), v->marked_as_pure};
|
||||
} else {
|
||||
v->error("Unexpected function body statement");
|
||||
|
@ -333,6 +338,8 @@ static void register_function(V<ast_function_declaration> v) {
|
|||
v->error(PSTRING() << "GET methods hash collision: `" << other->name() << "` and `" << static_cast<std::string>(func_name) << "` produce the same hash. Consider renaming one of these functions.");
|
||||
}
|
||||
}
|
||||
} else if (v->is_entrypoint) {
|
||||
sym_val->method_id = calculate_method_id_for_entrypoint(func_name);
|
||||
}
|
||||
if (v->marked_as_inline) {
|
||||
sym_val->flags |= SymValFunc::flagInline;
|
||||
|
@ -343,6 +350,9 @@ static void register_function(V<ast_function_declaration> v) {
|
|||
if (v->marked_as_get_method) {
|
||||
sym_val->flags |= SymValFunc::flagGetMethod;
|
||||
}
|
||||
if (v->is_entrypoint) {
|
||||
sym_val->flags |= SymValFunc::flagIsEntrypoint;
|
||||
}
|
||||
if (detect_if_function_just_wraps_another(v)) {
|
||||
sym_val->flags |= SymValFunc::flagWrapsAnotherF;
|
||||
}
|
||||
|
@ -368,21 +378,17 @@ static void iterate_through_file_symbols(const SrcFile* file) {
|
|||
|
||||
for (AnyV v : file->ast->as<ast_tolk_file>()->get_toplevel_declarations()) {
|
||||
switch (v->type) {
|
||||
case ast_include_statement:
|
||||
case ast_import_statement:
|
||||
// on `import "another-file.tolk"`, register symbols from that file at first
|
||||
// (for instance, it can calculate constants, which are used in init_val of constants in current file below import)
|
||||
iterate_through_file_symbols(v->as<ast_include_statement>()->file);
|
||||
iterate_through_file_symbols(v->as<ast_import_statement>()->file);
|
||||
break;
|
||||
|
||||
case ast_constant_declaration_list:
|
||||
for (AnyV v_decl : v->as<ast_constant_declaration_list>()->get_declarations()) {
|
||||
register_constant(v_decl->as<ast_constant_declaration>());
|
||||
}
|
||||
case ast_constant_declaration:
|
||||
register_constant(v->as<ast_constant_declaration>());
|
||||
break;
|
||||
case ast_global_var_declaration_list:
|
||||
for (AnyV v_decl : v->as<ast_global_var_declaration_list>()->get_declarations()) {
|
||||
register_global_var(v_decl->as<ast_global_var_declaration>());
|
||||
}
|
||||
case ast_global_var_declaration:
|
||||
register_global_var(v->as<ast_global_var_declaration>());
|
||||
break;
|
||||
case ast_function_declaration:
|
||||
register_function(v->as<ast_function_declaration>());
|
||||
|
|
|
@ -32,7 +32,6 @@ namespace tolk {
|
|||
|
||||
AllSrcFiles pipeline_discover_and_parse_sources(const std::string& stdlib_filename, const std::string& entrypoint_filename);
|
||||
|
||||
void pipeline_handle_pragmas(const AllSrcFiles&);
|
||||
void pipeline_register_global_symbols(const AllSrcFiles&);
|
||||
void pipeline_convert_ast_to_legacy_Expr_Op(const AllSrcFiles&);
|
||||
|
||||
|
|
|
@ -52,7 +52,6 @@ struct SrcFile {
|
|||
SrcFile &operator=(const SrcFile&) = delete;
|
||||
|
||||
bool is_stdlib_file() const { return file_id == 0; /* stdlib always exists, has no imports and parsed the first */ }
|
||||
bool is_entrypoint_file() const { return file_id == 1; /* after stdlib, the entrypoint file is parsed */ }
|
||||
|
||||
bool is_offset_valid(int offset) const;
|
||||
SrcPosition convert_offset(int offset) const;
|
||||
|
|
|
@ -24,12 +24,14 @@
|
|||
from all source files in the program, then also delete it here.
|
||||
*/
|
||||
#include "tolk.h"
|
||||
#include "tolk-version.h"
|
||||
#include "compiler-state.h"
|
||||
#include "td/utils/port/path.h"
|
||||
#include <getopt.h>
|
||||
#include <fstream>
|
||||
#include <utility>
|
||||
#include <sys/stat.h>
|
||||
#include <filesystem>
|
||||
#include "git.h"
|
||||
|
||||
using namespace tolk;
|
||||
|
@ -40,28 +42,54 @@ void usage(const char* progname) {
|
|||
"\tGenerates Fift TVM assembler code from a .tolk file\n"
|
||||
"-o<fif-filename>\tWrites generated code into specified .fif file instead of stdout\n"
|
||||
"-b<boc-filename>\tGenerate Fift instructions to save TVM bytecode into .boc file\n"
|
||||
"-s<stdlib.tolk>\tSpecify stdlib location (same as env TOLK_STDLIB; if unset, auto-discover)\n"
|
||||
"-O<level>\tSets optimization level (2 by default)\n"
|
||||
"-x<option-names>\tEnables experimental options, comma-separated\n"
|
||||
"-S\tDon't include stack layout comments into Fift output\n"
|
||||
"-e\tIncreases verbosity level (extra output into stderr)\n"
|
||||
"-v\tOutput version of Tolk and exit\n";
|
||||
std::exit(2);
|
||||
}
|
||||
|
||||
static std::string auto_discover_stdlib_location() {
|
||||
static bool stdlib_file_exists(std::filesystem::path& stdlib_tolk) {
|
||||
struct stat f_stat;
|
||||
stdlib_tolk = stdlib_tolk.lexically_normal();
|
||||
int res = stat(stdlib_tolk.c_str(), &f_stat);
|
||||
return res == 0 && S_ISREG(f_stat.st_mode);
|
||||
}
|
||||
|
||||
static std::string auto_discover_stdlib_location(const char* argv0) {
|
||||
// first, the user can specify env var that points directly to stdlib (useful for non-standard compiler locations)
|
||||
if (const char* env_var = getenv("TOLK_STDLIB")) {
|
||||
return env_var;
|
||||
}
|
||||
// this define is automatically set if just building this repo locally with cmake
|
||||
#ifdef STDLIB_TOLK_IF_BUILD_FROM_SOURCES
|
||||
return STDLIB_TOLK_IF_BUILD_FROM_SOURCES;
|
||||
#endif
|
||||
// this define is automatically set when compiling a linux package for distribution
|
||||
// (since binaries and smartcont/ folder are installed to a predefined path)
|
||||
// todo provide in cmake
|
||||
#ifdef STDLIB_TOLK_IF_BUILD_TO_PACKAGE
|
||||
return STDLIB_TOLK_IF_BUILD_TO_PACKAGE;
|
||||
|
||||
// if the user launches tolk compiler from a package installed (e.g. /usr/bin/tolk),
|
||||
// locate stdlib in /usr/share/ton/smartcont (this folder exists on package installation)
|
||||
// (note, that paths are not absolute, they are relative to the launched binary)
|
||||
// consider https://github.com/ton-blockchain/packages for actual paths
|
||||
std::filesystem::path executable_dir = std::filesystem::canonical(argv0).remove_filename();
|
||||
|
||||
#ifdef TD_DARWIN
|
||||
auto def_location = executable_dir / "../share/ton/ton/smartcont/stdlib.tolk";
|
||||
#elif TD_WINDOWS
|
||||
auto def_location = executable_dir / "smartcont/stdlib.tolk";
|
||||
#else // linux
|
||||
auto def_location = executable_dir / "../share/ton/smartcont/stdlib.tolk";
|
||||
#endif
|
||||
|
||||
if (stdlib_file_exists(def_location)) {
|
||||
return def_location;
|
||||
}
|
||||
|
||||
// so, the binary is not from a system package
|
||||
// maybe it's just built from sources? e.g. ~/ton/cmake-build-debug/tolk/tolk
|
||||
// then, check the ~/ton/crypto/smartcont folder
|
||||
auto near_when_built_from_sources = executable_dir / "../../crypto/smartcont/stdlib.tolk";
|
||||
if (stdlib_file_exists(near_when_built_from_sources)) {
|
||||
return near_when_built_from_sources;
|
||||
}
|
||||
|
||||
// no idea of where to find stdlib; let's show an error for the user, he should provide env var above
|
||||
return {};
|
||||
}
|
||||
|
||||
|
@ -120,7 +148,7 @@ public:
|
|||
|
||||
int main(int argc, char* const argv[]) {
|
||||
int i;
|
||||
while ((i = getopt(argc, argv, "o:b:s:O:Sevh")) != -1) {
|
||||
while ((i = getopt(argc, argv, "o:b:O:x:Sevh")) != -1) {
|
||||
switch (i) {
|
||||
case 'o':
|
||||
G.settings.output_filename = optarg;
|
||||
|
@ -128,12 +156,12 @@ int main(int argc, char* const argv[]) {
|
|||
case 'b':
|
||||
G.settings.boc_output_filename = optarg;
|
||||
break;
|
||||
case 's':
|
||||
G.settings.stdlib_filename = optarg;
|
||||
break;
|
||||
case 'O':
|
||||
G.settings.optimization_level = std::max(0, atoi(optarg));
|
||||
break;
|
||||
case 'x':
|
||||
G.settings.parse_experimental_options_cmd_arg(optarg);
|
||||
break;
|
||||
case 'S':
|
||||
G.settings.stack_layout_comments = false;
|
||||
break;
|
||||
|
@ -141,9 +169,9 @@ int main(int argc, char* const argv[]) {
|
|||
G.settings.verbosity++;
|
||||
break;
|
||||
case 'v':
|
||||
std::cout << "Tolk compiler v" << tolk_version << "\n";
|
||||
std::cout << "Build commit: " << GitMetadata::CommitSHA1() << "\n";
|
||||
std::cout << "Build date: " << GitMetadata::CommitDate() << "\n";
|
||||
std::cout << "Tolk compiler v" << TOLK_VERSION << std::endl;
|
||||
std::cout << "Build commit: " << GitMetadata::CommitSHA1() << std::endl;
|
||||
std::cout << "Build date: " << GitMetadata::CommitDate() << std::endl;
|
||||
std::exit(0);
|
||||
case 'h':
|
||||
default:
|
||||
|
@ -153,14 +181,12 @@ int main(int argc, char* const argv[]) {
|
|||
|
||||
StdCoutRedirectToFile redirect_cout(G.settings.output_filename);
|
||||
if (redirect_cout.is_failed()) {
|
||||
std::cerr << "Failed to create output file " << G.settings.output_filename << '\n';
|
||||
std::cerr << "Failed to create output file " << G.settings.output_filename << std::endl;
|
||||
return 2;
|
||||
}
|
||||
|
||||
// if stdlib wasn't specify as an option — locate it based on env
|
||||
if (G.settings.stdlib_filename.empty()) {
|
||||
G.settings.stdlib_filename = auto_discover_stdlib_location();
|
||||
}
|
||||
// locate stdlib.tolk based on env or default system paths
|
||||
G.settings.stdlib_filename = auto_discover_stdlib_location(argv[0]);
|
||||
if (G.settings.stdlib_filename.empty()) {
|
||||
std::cerr << "Failed to discover stdlib.tolk.\n"
|
||||
"Probably, you have a non-standard Tolk installation.\n"
|
||||
|
@ -168,11 +194,11 @@ int main(int argc, char* const argv[]) {
|
|||
return 2;
|
||||
}
|
||||
if (G.is_verbosity(2)) {
|
||||
std::cerr << "stdlib located at " << G.settings.stdlib_filename << '\n';
|
||||
std::cerr << "stdlib located at " << G.settings.stdlib_filename << std::endl;
|
||||
}
|
||||
|
||||
if (optind != argc - 1) {
|
||||
std::cerr << "invalid usage: should specify exactly one input file.tolk";
|
||||
std::cerr << "invalid usage: should specify exactly one input file.tolk" << std::endl;
|
||||
return 2;
|
||||
}
|
||||
|
||||
|
|
23
tolk/tolk-version.h
Normal file
23
tolk/tolk-version.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
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
|
||||
|
||||
namespace tolk {
|
||||
|
||||
constexpr const char* TOLK_VERSION = "0.6.0";
|
||||
|
||||
} // namespace tolk
|
|
@ -24,6 +24,7 @@
|
|||
from all source files in the program, then also delete it here.
|
||||
*/
|
||||
#include "tolk.h"
|
||||
#include "tolk-version.h"
|
||||
#include "compiler-state.h"
|
||||
#include "git.h"
|
||||
#include "td/utils/JsonBuilder.h"
|
||||
|
@ -41,12 +42,16 @@ td::Result<std::string> compile_internal(char *config_json) {
|
|||
TRY_RESULT(stdlib_tolk, td::get_json_object_string_field(config, "stdlibLocation", false));
|
||||
TRY_RESULT(stack_comments, td::get_json_object_bool_field(config, "withStackComments", true, false));
|
||||
TRY_RESULT(entrypoint_filename, td::get_json_object_string_field(config, "entrypointFileName", false));
|
||||
TRY_RESULT(experimental_options, td::get_json_object_string_field(config, "experimentalOptions", true));
|
||||
|
||||
G.settings.verbosity = 0;
|
||||
G.settings.optimization_level = std::max(0, opt_level);
|
||||
G.settings.stdlib_filename = stdlib_tolk;
|
||||
G.settings.stack_layout_comments = stack_comments;
|
||||
G.settings.entrypoint_filename = entrypoint_filename;
|
||||
if (!experimental_options.empty()) {
|
||||
G.settings.parse_experimental_options_cmd_arg(experimental_options.c_str());
|
||||
}
|
||||
|
||||
std::ostringstream outs, errs;
|
||||
std::cout.rdbuf(outs.rdbuf());
|
||||
|
@ -100,7 +105,7 @@ extern "C" {
|
|||
const char* version() {
|
||||
auto version_json = td::JsonBuilder();
|
||||
auto obj = version_json.enter_object();
|
||||
obj("tolkVersion", tolk_version);
|
||||
obj("tolkVersion", TOLK_VERSION);
|
||||
obj("tolkFiftLibCommitHash", GitMetadata::CommitSHA1());
|
||||
obj("tolkFiftLibCommitDate", GitMetadata::CommitDate());
|
||||
obj.leave();
|
||||
|
|
|
@ -32,11 +32,21 @@
|
|||
namespace tolk {
|
||||
|
||||
|
||||
void on_assertion_failed(const char *description, const char *file_name, int line_number) {
|
||||
std::string message = static_cast<std::string>("Assertion failed at ") + file_name + ":" + std::to_string(line_number) + ": " + description;
|
||||
#ifdef TOLK_DEBUG
|
||||
#ifdef __arm64__
|
||||
// when developing, it's handy when the debugger stops on assertion failure (stacktraces and watches are available)
|
||||
std::cerr << message << std::endl;
|
||||
__builtin_debugtrap();
|
||||
#endif
|
||||
#endif
|
||||
throw Fatal(std::move(message));
|
||||
}
|
||||
|
||||
int tolk_proceed(const std::string &entrypoint_filename) {
|
||||
define_builtins();
|
||||
lexer_init();
|
||||
G.pragma_allow_post_modification.always_on_and_deprecated("0.5.0");
|
||||
G.pragma_compute_asm_ltr.always_on_and_deprecated("0.5.0");
|
||||
|
||||
try {
|
||||
if (G.settings.stdlib_filename.empty()) {
|
||||
|
@ -48,7 +58,6 @@ int tolk_proceed(const std::string &entrypoint_filename) {
|
|||
|
||||
AllSrcFiles all_files = pipeline_discover_and_parse_sources(G.settings.stdlib_filename, entrypoint_filename);
|
||||
|
||||
pipeline_handle_pragmas(all_files);
|
||||
pipeline_register_global_symbols(all_files);
|
||||
pipeline_convert_ast_to_legacy_Expr_Op(all_files);
|
||||
|
||||
|
|
207
tolk/tolk.h
207
tolk/tolk.h
|
@ -16,6 +16,7 @@
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
#include "platform-utils.h"
|
||||
#include "src-file.h"
|
||||
#include "type-expr.h"
|
||||
#include "symtable.h"
|
||||
|
@ -26,12 +27,13 @@
|
|||
#include <stack>
|
||||
#include <iostream>
|
||||
|
||||
#define tolk_assert(expr) \
|
||||
(bool(expr) ? void(0) \
|
||||
: throw Fatal(PSTRING() << "Assertion failed at " << __FILE__ << ":" << __LINE__ << ": " << #expr))
|
||||
#define tolk_assert(expr) if(UNLIKELY(!(expr))) on_assertion_failed(#expr, __FILE__, __LINE__);
|
||||
|
||||
namespace tolk {
|
||||
|
||||
GNU_ATTRIBUTE_COLD GNU_ATTRIBUTE_NORETURN
|
||||
void on_assertion_failed(const char *description, const char *file_name, int line_number);
|
||||
|
||||
/*
|
||||
*
|
||||
* TYPE EXPRESSIONS
|
||||
|
@ -90,27 +92,23 @@ struct VarDescr {
|
|||
_NonZero = 128,
|
||||
_Pos = 256,
|
||||
_Neg = 512,
|
||||
_Bool = 1024,
|
||||
_Bit = 2048,
|
||||
_Finite = 4096,
|
||||
_Nan = 8192,
|
||||
_Even = 16384,
|
||||
_Odd = 32768,
|
||||
_Null = (1 << 16),
|
||||
_NotNull = (1 << 17)
|
||||
};
|
||||
static constexpr int ConstZero = _Int | _Zero | _Pos | _Neg | _Bool | _Bit | _Finite | _Even | _NotNull;
|
||||
static constexpr int ConstOne = _Int | _NonZero | _Pos | _Bit | _Finite | _Odd | _NotNull;
|
||||
static constexpr int ConstTrue = _Int | _NonZero | _Neg | _Bool | _Finite | _Odd | _NotNull;
|
||||
static constexpr int ValBit = ConstZero & ConstOne;
|
||||
static constexpr int ValBool = ConstZero & ConstTrue;
|
||||
static constexpr int FiniteInt = _Int | _Finite | _NotNull;
|
||||
static constexpr int FiniteUInt = FiniteInt | _Pos;
|
||||
static constexpr int ConstZero = _Const | _Int | _Zero | _Pos | _Neg | _Finite | _Even;
|
||||
static constexpr int ConstOne = _Const | _Int | _NonZero | _Pos | _Finite | _Odd;
|
||||
static constexpr int ConstTrue = _Const | _Int | _NonZero | _Neg | _Finite | _Odd;
|
||||
static constexpr int ValBit = _Int | _Pos | _Finite;
|
||||
static constexpr int ValBool = _Int | _Neg | _Finite;
|
||||
static constexpr int FiniteInt = _Int | _Finite;
|
||||
static constexpr int FiniteUInt = _Int | _Finite | _Pos;
|
||||
int val;
|
||||
td::RefInt256 int_const;
|
||||
std::string str_const;
|
||||
|
||||
VarDescr(var_idx_t _idx = -1, int _flags = 0, int _val = 0) : idx(_idx), flags(_flags), val(_val) {
|
||||
explicit VarDescr(var_idx_t _idx = -1, int _flags = 0, int _val = 0) : idx(_idx), flags(_flags), val(_val) {
|
||||
}
|
||||
bool operator<(var_idx_t other_idx) const {
|
||||
return idx < other_idx;
|
||||
|
@ -139,15 +137,6 @@ struct VarDescr {
|
|||
bool always_odd() const {
|
||||
return val & _Odd;
|
||||
}
|
||||
bool always_null() const {
|
||||
return val & _Null;
|
||||
}
|
||||
bool always_not_null() const {
|
||||
return val & _NotNull;
|
||||
}
|
||||
bool is_const() const {
|
||||
return val & _Const;
|
||||
}
|
||||
bool is_int_const() const {
|
||||
return (val & (_Int | _Const)) == (_Int | _Const) && int_const.not_null();
|
||||
}
|
||||
|
@ -260,7 +249,7 @@ class ListIterator {
|
|||
public:
|
||||
ListIterator() : ptr(nullptr) {
|
||||
}
|
||||
ListIterator(T* _ptr) : ptr(_ptr) {
|
||||
explicit ListIterator(T* _ptr) : ptr(_ptr) {
|
||||
}
|
||||
ListIterator& operator++() {
|
||||
ptr = ptr->next.get();
|
||||
|
@ -383,18 +372,6 @@ struct Op {
|
|||
const Op& last() const {
|
||||
return next ? next->last() : *this;
|
||||
}
|
||||
ListIterator<Op> begin() {
|
||||
return ListIterator<Op>{this};
|
||||
}
|
||||
ListIterator<Op> end() const {
|
||||
return ListIterator<Op>{};
|
||||
}
|
||||
ListIterator<const Op> cbegin() {
|
||||
return ListIterator<const Op>{this};
|
||||
}
|
||||
ListIterator<const Op> cend() const {
|
||||
return ListIterator<const Op>{};
|
||||
}
|
||||
};
|
||||
|
||||
inline ListIterator<Op> begin(const std::unique_ptr<Op>& op_list) {
|
||||
|
@ -405,14 +382,6 @@ inline ListIterator<Op> end(const std::unique_ptr<Op>& op_list) {
|
|||
return ListIterator<Op>{};
|
||||
}
|
||||
|
||||
inline ListIterator<const Op> cbegin(const Op* op_list) {
|
||||
return ListIterator<const Op>{op_list};
|
||||
}
|
||||
|
||||
inline ListIterator<const Op> cend(const Op* op_list) {
|
||||
return ListIterator<const Op>{};
|
||||
}
|
||||
|
||||
inline ListIterator<const Op> begin(const Op* op_list) {
|
||||
return ListIterator<const Op>{op_list};
|
||||
}
|
||||
|
@ -421,78 +390,11 @@ inline ListIterator<const Op> end(const Op* op_list) {
|
|||
return ListIterator<const Op>{};
|
||||
}
|
||||
|
||||
inline ListIterator<Op> begin(Op* op_list) {
|
||||
return ListIterator<Op>{op_list};
|
||||
}
|
||||
|
||||
inline ListIterator<Op> end(Op* op_list) {
|
||||
return ListIterator<Op>{};
|
||||
}
|
||||
|
||||
typedef std::tuple<TypeExpr*, SymDef*, SrcLocation> FormalArg;
|
||||
typedef std::vector<FormalArg> FormalArgList;
|
||||
|
||||
struct AsmOpList;
|
||||
|
||||
struct CodeBlob {
|
||||
enum { _ForbidImpure = 4 };
|
||||
int var_cnt, in_var_cnt, op_cnt;
|
||||
TypeExpr* ret_type;
|
||||
std::string name;
|
||||
SrcLocation loc;
|
||||
std::vector<TmpVar> vars;
|
||||
std::unique_ptr<Op> ops;
|
||||
std::unique_ptr<Op>* cur_ops;
|
||||
std::stack<std::unique_ptr<Op>*> cur_ops_stack;
|
||||
int flags = 0;
|
||||
bool require_callxargs = false;
|
||||
CodeBlob(std::string name, SrcLocation loc, TypeExpr* ret)
|
||||
: var_cnt(0), in_var_cnt(0), op_cnt(0), ret_type(ret), name(std::move(name)), loc(loc), cur_ops(&ops) {
|
||||
}
|
||||
template <typename... Args>
|
||||
Op& emplace_back(Args&&... args) {
|
||||
Op& res = *(*cur_ops = std::make_unique<Op>(args...));
|
||||
cur_ops = &(res.next);
|
||||
return res;
|
||||
}
|
||||
bool import_params(FormalArgList arg_list);
|
||||
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(true, var_type, nullptr, loc);
|
||||
}
|
||||
int split_vars(bool strict = false);
|
||||
bool compute_used_code_vars();
|
||||
bool compute_used_code_vars(std::unique_ptr<Op>& ops, const VarDescrList& var_info, bool edit) const;
|
||||
void print(std::ostream& os, int flags = 0) const;
|
||||
void push_set_cur(std::unique_ptr<Op>& new_cur_ops) {
|
||||
cur_ops_stack.push(cur_ops);
|
||||
cur_ops = &new_cur_ops;
|
||||
}
|
||||
void close_blk(SrcLocation location) {
|
||||
*cur_ops = std::make_unique<Op>(location, Op::_Nop);
|
||||
}
|
||||
void pop_cur() {
|
||||
cur_ops = cur_ops_stack.top();
|
||||
cur_ops_stack.pop();
|
||||
}
|
||||
void close_pop_cur(SrcLocation location) {
|
||||
close_blk(location);
|
||||
pop_cur();
|
||||
}
|
||||
void simplify_var_types();
|
||||
void prune_unreachable_code();
|
||||
void fwd_analyze();
|
||||
void mark_noreturn();
|
||||
void generate_code(AsmOpList& out_list, int mode = 0);
|
||||
void generate_code(std::ostream& os, int mode = 0, int indent = 0);
|
||||
|
||||
void on_var_modification(var_idx_t idx, SrcLocation here) const {
|
||||
for (auto& f : vars.at(idx).on_modification) {
|
||||
f(here);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
*
|
||||
* SYMBOL VALUES
|
||||
|
@ -512,13 +414,14 @@ struct SymVal : SymValBase {
|
|||
|
||||
struct SymValFunc : SymVal {
|
||||
enum SymValFlag {
|
||||
flagInline = 1, // function marked `inline`
|
||||
flagInlineRef = 2, // function marked `inline_ref`
|
||||
flagWrapsAnotherF = 4, // (T) thisF(...args) { return anotherF(...args); } (calls to thisF will be replaced)
|
||||
flagInline = 1, // marked `@inline`
|
||||
flagInlineRef = 2, // marked `@inline_ref`
|
||||
flagWrapsAnotherF = 4, // `fun thisF(...args) { return anotherF(...args); }` (calls to thisF will be inlined)
|
||||
flagUsedAsNonCall = 8, // used not only as `f()`, but as a 1-st class function (assigned to var, pushed to tuple, etc.)
|
||||
flagMarkedAsPure = 16, // declared as `pure`, can't call impure and access globals, unused invocations are optimized out
|
||||
flagBuiltinFunction = 32, // was created via `define_builtin_func()`, not from source code
|
||||
flagGetMethod = 64, // was declared via `get T func()`, method_id is auto-assigned
|
||||
flagGetMethod = 64, // was declared via `get func(): T`, method_id is auto-assigned
|
||||
flagIsEntrypoint = 128, // it's `main` / `onExternalMessage` / etc.
|
||||
};
|
||||
|
||||
td::RefInt256 method_id; // todo why int256? it's small
|
||||
|
@ -559,6 +462,9 @@ struct SymValFunc : SymVal {
|
|||
bool is_get_method() const {
|
||||
return flags & flagGetMethod;
|
||||
}
|
||||
bool is_entrypoint() const {
|
||||
return flags & flagIsEntrypoint;
|
||||
}
|
||||
};
|
||||
|
||||
struct SymValCodeFunc : SymValFunc {
|
||||
|
@ -626,7 +532,6 @@ struct Expr {
|
|||
_None,
|
||||
_Apply,
|
||||
_VarApply,
|
||||
_TypeApply,
|
||||
_MkTuple,
|
||||
_Tensor,
|
||||
_Const,
|
||||
|
@ -636,13 +541,12 @@ struct Expr {
|
|||
_Letop,
|
||||
_LetFirst,
|
||||
_Hole,
|
||||
_Type,
|
||||
_CondExpr,
|
||||
_SliceConst,
|
||||
};
|
||||
ExprCls cls;
|
||||
int val{0};
|
||||
enum { _IsType = 1, _IsRvalue = 2, _IsLvalue = 4, _IsImpure = 32 };
|
||||
enum { _IsRvalue = 2, _IsLvalue = 4, _IsImpure = 32 };
|
||||
int flags{0};
|
||||
SrcLocation here;
|
||||
td::RefInt256 intval;
|
||||
|
@ -681,12 +585,6 @@ struct Expr {
|
|||
bool is_lvalue() const {
|
||||
return flags & _IsLvalue;
|
||||
}
|
||||
bool is_type() const {
|
||||
return flags & _IsType;
|
||||
}
|
||||
bool is_type_apply() const {
|
||||
return cls == _TypeApply;
|
||||
}
|
||||
bool is_mktuple() const {
|
||||
return cls == _MkTuple;
|
||||
}
|
||||
|
@ -1449,6 +1347,65 @@ struct SymValAsmFunc : SymValFunc {
|
|||
bool compile(AsmOpList& dest, std::vector<VarDescr>& out, std::vector<VarDescr>& in, SrcLocation where) const;
|
||||
};
|
||||
|
||||
struct CodeBlob {
|
||||
enum { _ForbidImpure = 4 };
|
||||
int var_cnt, in_var_cnt, op_cnt;
|
||||
TypeExpr* ret_type;
|
||||
std::string name;
|
||||
SrcLocation loc;
|
||||
std::vector<TmpVar> vars;
|
||||
std::unique_ptr<Op> ops;
|
||||
std::unique_ptr<Op>* cur_ops;
|
||||
std::stack<std::unique_ptr<Op>*> cur_ops_stack;
|
||||
int flags = 0;
|
||||
bool require_callxargs = false;
|
||||
CodeBlob(std::string name, SrcLocation loc, TypeExpr* ret)
|
||||
: var_cnt(0), in_var_cnt(0), op_cnt(0), ret_type(ret), name(std::move(name)), loc(loc), cur_ops(&ops) {
|
||||
}
|
||||
template <typename... Args>
|
||||
Op& emplace_back(Args&&... args) {
|
||||
Op& res = *(*cur_ops = std::make_unique<Op>(args...));
|
||||
cur_ops = &(res.next);
|
||||
return res;
|
||||
}
|
||||
bool import_params(FormalArgList arg_list);
|
||||
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(true, var_type, nullptr, loc);
|
||||
}
|
||||
int split_vars(bool strict = false);
|
||||
bool compute_used_code_vars();
|
||||
bool compute_used_code_vars(std::unique_ptr<Op>& ops, const VarDescrList& var_info, bool edit) const;
|
||||
void print(std::ostream& os, int flags = 0) const;
|
||||
void push_set_cur(std::unique_ptr<Op>& new_cur_ops) {
|
||||
cur_ops_stack.push(cur_ops);
|
||||
cur_ops = &new_cur_ops;
|
||||
}
|
||||
void close_blk(SrcLocation location) {
|
||||
*cur_ops = std::make_unique<Op>(location, Op::_Nop);
|
||||
}
|
||||
void pop_cur() {
|
||||
cur_ops = cur_ops_stack.top();
|
||||
cur_ops_stack.pop();
|
||||
}
|
||||
void close_pop_cur(SrcLocation location) {
|
||||
close_blk(location);
|
||||
pop_cur();
|
||||
}
|
||||
void simplify_var_types();
|
||||
void prune_unreachable_code();
|
||||
void fwd_analyze();
|
||||
void mark_noreturn();
|
||||
void generate_code(AsmOpList& out_list, int mode = 0);
|
||||
void generate_code(std::ostream& os, int mode = 0, int indent = 0);
|
||||
|
||||
void on_var_modification(var_idx_t idx, SrcLocation here) const {
|
||||
for (auto& f : vars.at(idx).on_modification) {
|
||||
f(here);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// defined in builtins.cpp
|
||||
AsmOp exec_arg_op(std::string op, long long arg);
|
||||
AsmOp exec_arg_op(std::string op, long long arg, int args, int retv = 1);
|
||||
|
|
|
@ -14,7 +14,7 @@ struct TypeExpr {
|
|||
_Cell = tok_cell,
|
||||
_Slice = tok_slice,
|
||||
_Builder = tok_builder,
|
||||
_Cont = tok_cont,
|
||||
_Cont = tok_continuation,
|
||||
_Tuple = tok_tuple,
|
||||
};
|
||||
Kind constr;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue