1
0
Fork 0
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:
tolk-vm 2024-10-31 11:11:41 +04:00
parent 5a3e3595d6
commit e2edadba92
No known key found for this signature in database
GPG key ID: 7905DD7FE0324B12
133 changed files with 8196 additions and 2605 deletions

View file

@ -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

View file

@ -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;

View file

@ -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

View file

@ -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");

View file

@ -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");

View file

@ -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");

View file

@ -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;
}

View file

@ -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<>

View file

@ -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));

View file

@ -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 {

View file

@ -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

View file

@ -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; }
};

View file

@ -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) {

View file

@ -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() {

View file

@ -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;
};

View file

@ -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()) {

View file

@ -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);
}
}
}

View file

@ -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);
}
}

View file

@ -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";
}

View file

@ -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

View file

@ -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>());

View file

@ -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&);

View file

@ -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;

View file

@ -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
View 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

View file

@ -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();

View file

@ -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);

View file

@ -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);

View file

@ -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;