1
0
Fork 0
mirror of https://github.com/ton-blockchain/ton synced 2025-03-09 15:40:10 +00:00

[Tolk] Get rid of ~tilda with mutate and self methods

This is a very big change.
If FunC has `.methods()` and `~methods()`, Tolk has only dot,
one and only way to call a `.method()`.
A method may mutate an object, or may not.
It's a behavioral and semantic difference from FunC.

- `cs.loadInt(32)` modifies a slice and returns an integer
- `b.storeInt(x, 32)` modifies a builder
- `b = b.storeInt()` also works, since it not only modifies, but returns
- chained methods also work, they return `self`
- everything works exactly as expected, similar to JS
- no runtime overhead, exactly same Fift instructions
- custom methods are created with ease
- tilda `~` does not exist in Tolk at all
This commit is contained in:
tolk-vm 2024-10-31 11:18:54 +04:00
parent 12ff28ac94
commit d9dba320cc
No known key found for this signature in database
GPG key ID: 7905DD7FE0324B12
85 changed files with 2710 additions and 1965 deletions

View file

@ -131,7 +131,8 @@ static AnyV maybe_replace_eq_null_with_isNull_call(V<ast_binary_operator> v) {
auto v_ident = createV<ast_identifier>(v->loc, "__isNull"); // built-in function
AnyV v_null = v->get_lhs()->type == ast_null_keyword ? v->get_rhs() : v->get_lhs();
AnyV v_isNull = createV<ast_function_call>(v->loc, v_ident, createV<ast_tensor>(v->loc, {v_null}));
AnyV v_arg = createV<ast_argument>(v->loc, v_null, false);
AnyV v_isNull = createV<ast_function_call>(v->loc, v_ident, createV<ast_argument_list>(v->loc, {v_arg}));
if (v->tok == tok_neq) {
v_isNull = createV<ast_unary_operator>(v->loc, "!", tok_logical_not, v_isNull);
}
@ -165,7 +166,7 @@ static TypeExpr* parse_type1(Lexer& lex, V<ast_genericsT_list> genericsT_list) {
return TypeExpr::new_atomic(TypeExpr::_Builder);
case tok_continuation:
lex.next();
return TypeExpr::new_atomic(TypeExpr::_Cont);
return TypeExpr::new_atomic(TypeExpr::_Continutaion);
case tok_tuple:
lex.next();
return TypeExpr::new_atomic(TypeExpr::_Tuple);
@ -177,6 +178,8 @@ static TypeExpr* parse_type1(Lexer& lex, V<ast_genericsT_list> genericsT_list) {
return TypeExpr::new_tensor({});
case tok_bool:
lex.error("bool type is not supported yet");
case tok_self:
lex.error("`self` type can be used only as a return type of a function (enforcing it to be chainable)");
case tok_identifier:
if (int idx = genericsT_list ? genericsT_list->lookup_idx(lex.cur_str()) : -1; idx != -1) {
lex.next();
@ -229,13 +232,27 @@ static TypeExpr* parse_type(Lexer& lex, V<ast_genericsT_list> genericsT_list) {
AnyV parse_expr(Lexer& lex);
static AnyV parse_parameter(Lexer& lex, V<ast_genericsT_list> genericsT_list) {
static AnyV parse_parameter(Lexer& lex, V<ast_genericsT_list> genericsT_list, bool is_first) {
SrcLocation loc = lex.cur_location();
// argument name (or underscore for an unnamed parameter)
// optional keyword `mutate` meaning that a function will mutate a passed argument (like passed by reference)
bool declared_as_mutate = false;
bool is_param_self = false;
if (lex.tok() == tok_mutate) {
lex.next();
declared_as_mutate = true;
}
// parameter name (or underscore for an unnamed parameter)
std::string_view param_name;
if (lex.tok() == tok_identifier) {
param_name = lex.cur_str();
} else if (lex.tok() == tok_self) {
if (!is_first) {
lex.error("`self` can only be the first parameter");
}
param_name = "self";
is_param_self = true;
} else if (lex.tok() != tok_underscore) {
lex.unexpected("parameter name");
}
@ -245,8 +262,14 @@ static AnyV parse_parameter(Lexer& lex, V<ast_genericsT_list> genericsT_list) {
// parameter type after colon, also mandatory (even explicit ":auto")
lex.expect(tok_colon, "`: <parameter_type>`");
TypeExpr* param_type = parse_type(lex, genericsT_list);
if (declared_as_mutate && !param_type->has_fixed_width()) {
throw ParseError(loc, "`mutate` parameter must be strictly typed");
}
if (is_param_self && !param_type->has_fixed_width()) {
throw ParseError(loc, "`self` parameter must be strictly typed");
}
return createV<ast_parameter>(loc, v_ident, param_type);
return createV<ast_parameter>(loc, v_ident, param_type, declared_as_mutate);
}
static AnyV parse_global_var_declaration(Lexer& lex, const std::vector<V<ast_annotation>>& annotations) {
@ -301,21 +324,52 @@ static AnyV parse_constant_declaration(Lexer& lex, const std::vector<V<ast_annot
return createV<ast_constant_declaration>(loc, v_ident, declared_type, init_value);
}
static AnyV parse_parameter_list(Lexer& lex, V<ast_genericsT_list> genericsT_list) {
// "parameters" are at function declaration: `fun f(param1: int, mutate param2: slice)`
static V<ast_parameter_list> parse_parameter_list(Lexer& lex, V<ast_genericsT_list> genericsT_list) {
SrcLocation loc = lex.cur_location();
std::vector<AnyV> params;
lex.expect(tok_oppar, "parameter list");
if (lex.tok() != tok_clpar) {
params.push_back(parse_parameter(lex, genericsT_list));
params.push_back(parse_parameter(lex, genericsT_list, true));
while (lex.tok() == tok_comma) {
lex.next();
params.push_back(parse_parameter(lex, genericsT_list));
params.push_back(parse_parameter(lex, genericsT_list, false));
}
}
lex.expect(tok_clpar, "`)`");
return createV<ast_parameter_list>(loc, std::move(params));
}
// "arguments" are at function call: `f(arg1, mutate arg2)`
static AnyV parse_argument(Lexer& lex) {
SrcLocation loc = lex.cur_location();
// keyword `mutate` is necessary when a parameter is declared `mutate` (to make mutation obvious for the reader)
bool passed_as_mutate = false;
if (lex.tok() == tok_mutate) {
lex.next();
passed_as_mutate = true;
}
AnyV expr = parse_expr(lex);
return createV<ast_argument>(loc, expr, passed_as_mutate);
}
static V<ast_argument_list> parse_argument_list(Lexer& lex) {
SrcLocation loc = lex.cur_location();
std::vector<AnyV> args;
lex.expect(tok_oppar, "`(`");
if (lex.tok() != tok_clpar) {
args.push_back(parse_argument(lex));
while (lex.tok() == tok_comma) {
lex.next();
args.push_back(parse_argument(lex));
}
}
lex.expect(tok_clpar, "`)`");
return createV<ast_argument_list>(loc, std::move(args));
}
// parse (expr) / [expr] / identifier / number
static AnyV parse_expr100(Lexer& lex) {
SrcLocation loc = lex.cur_location();
@ -384,6 +438,10 @@ static AnyV parse_expr100(Lexer& lex) {
lex.next();
return createV<ast_null_keyword>(loc);
}
case tok_self: {
lex.next();
return createV<ast_self_keyword>(loc);
}
case tok_identifier: {
std::string_view str_val = lex.cur_str();
lex.next();
@ -400,48 +458,25 @@ static AnyV parse_expr100(Lexer& lex) {
}
}
// parse E(expr)
// parse E(args)
static AnyV parse_expr90(Lexer& lex) {
AnyV res = parse_expr100(lex);
if (lex.tok() == tok_oppar) {
lex.next();
SrcLocation loc = lex.cur_location();
std::vector<AnyV> args;
if (lex.tok() != tok_clpar) {
args.push_back(parse_expr(lex));
while (lex.tok() == tok_comma) {
lex.next();
args.push_back(parse_expr(lex));
}
}
lex.expect(tok_clpar, "`)`");
return createV<ast_function_call>(res->loc, res, createV<ast_tensor>(loc, std::move(args)));
return createV<ast_function_call>(res->loc, res, parse_argument_list(lex));
}
return res;
}
// parse E .method ~method E (left-to-right)
// parse E.method(...) (left-to-right)
static AnyV parse_expr80(Lexer& lex) {
AnyV lhs = parse_expr90(lex);
while (lex.tok() == tok_identifier && (lex.cur_str()[0] == '.' || lex.cur_str()[0] == '~')) {
while (lex.tok() == tok_dot) {
SrcLocation loc = lex.cur_location();
lex.next();
lex.check(tok_identifier, "method name");
std::string_view method_name = lex.cur_str();
lex.next();
SrcLocation loc = lex.cur_location();
std::vector<AnyV> args;
lex.expect(tok_oppar, "`(`");
if (lex.tok() != tok_clpar) {
args.push_back(parse_expr(lex));
while (lex.tok() == tok_comma) {
lex.next();
args.push_back(parse_expr(lex));
}
}
lex.expect(tok_clpar, "`)`");
lhs = createV<ast_dot_tilde_call>(lhs->loc, method_name, lhs, createV<ast_tensor>(loc, std::move(args)));
lhs = createV<ast_dot_method_call>(loc, method_name, lhs, parse_argument_list(lex));
}
return lhs;
}
@ -586,11 +621,11 @@ AnyV parse_expr(Lexer& lex) {
AnyV parse_statement(Lexer& lex);
static AnyV parse_var_declaration_lhs(Lexer& lex) {
static AnyV parse_var_declaration_lhs(Lexer& lex, bool is_immutable) {
SrcLocation loc = lex.cur_location();
if (lex.tok() == tok_oppar) {
lex.next();
AnyV first = parse_var_declaration_lhs(lex);
AnyV first = parse_var_declaration_lhs(lex, is_immutable);
if (lex.tok() == tok_clpar) {
lex.next();
return createV<ast_parenthesized_expr>(loc, first);
@ -598,17 +633,17 @@ static AnyV parse_var_declaration_lhs(Lexer& lex) {
std::vector<AnyV> args(1, first);
while (lex.tok() == tok_comma) {
lex.next();
args.push_back(parse_var_declaration_lhs(lex));
args.push_back(parse_var_declaration_lhs(lex, is_immutable));
}
lex.expect(tok_clpar, "`)`");
return createV<ast_tensor>(loc, std::move(args));
}
if (lex.tok() == tok_opbracket) {
lex.next();
std::vector<AnyV> args(1, parse_var_declaration_lhs(lex));
std::vector<AnyV> args(1, parse_var_declaration_lhs(lex, is_immutable));
while (lex.tok() == tok_comma) {
lex.next();
args.push_back(parse_var_declaration_lhs(lex));
args.push_back(parse_var_declaration_lhs(lex, is_immutable));
}
lex.expect(tok_clbracket, "`]`");
return createV<ast_tensor_square>(loc, std::move(args));
@ -625,7 +660,7 @@ static AnyV parse_var_declaration_lhs(Lexer& lex) {
lex.next();
marked_as_redef = true;
}
return createV<ast_local_var>(loc, v_ident, declared_type, marked_as_redef);
return createV<ast_local_var>(loc, v_ident, declared_type, is_immutable, marked_as_redef);
}
if (lex.tok() == tok_underscore) {
TypeExpr* declared_type = nullptr;
@ -634,21 +669,17 @@ static AnyV parse_var_declaration_lhs(Lexer& lex) {
lex.next();
declared_type = parse_type(lex, nullptr);
}
return createV<ast_local_var>(loc, createV<ast_underscore>(loc), declared_type, false);
return createV<ast_local_var>(loc, createV<ast_underscore>(loc), declared_type, true, false);
}
lex.unexpected("variable name");
}
static AnyV parse_local_vars_declaration(Lexer& lex) {
SrcLocation loc = lex.cur_location();
bool immutable = lex.tok() == tok_val;
bool is_immutable = lex.tok() == tok_val;
lex.next();
if (immutable) {
lex.error("immutable variables are not supported yet");
}
AnyV lhs = parse_var_declaration_lhs(lex);
AnyV lhs = parse_var_declaration_lhs(lex, is_immutable);
if (lex.tok() != tok_assign) {
lex.error("variables declaration must be followed by assignment: `var xxx = ...`");
}
@ -885,10 +916,10 @@ static AnyV parse_asm_func_body(Lexer& lex, V<ast_parameter_list> param_list) {
std::vector<int> arg_order, ret_order;
if (lex.tok() == tok_oppar) {
lex.next();
while (lex.tok() == tok_identifier || lex.tok() == tok_int_const) {
while (lex.tok() == tok_identifier || lex.tok() == tok_self) {
int arg_idx = param_list->lookup_idx(lex.cur_str());
if (arg_idx == -1) {
lex.unexpected("argument name");
lex.unexpected("parameter name");
}
arg_order.push_back(arg_idx);
lex.next();
@ -1006,17 +1037,44 @@ static AnyV parse_function_declaration(Lexer& lex, const std::vector<V<ast_annot
genericsT_list = parse_genericsT_list(lex)->as<ast_genericsT_list>();
}
V<ast_parameter_list> param_list = parse_parameter_list(lex, genericsT_list)->as<ast_parameter_list>();
V<ast_parameter_list> v_param_list = parse_parameter_list(lex, genericsT_list)->as<ast_parameter_list>();
bool accepts_self = !v_param_list->empty() && v_param_list->get_param(0)->get_identifier()->name == "self";
int n_mutate_params = v_param_list->get_mutate_params_count();
TypeExpr* ret_type = nullptr;
bool returns_self = false;
if (lex.tok() == tok_colon) { // : <ret_type> (if absent, it means "auto infer", not void)
lex.next();
ret_type = parse_type(lex, genericsT_list);
if (lex.tok() == tok_self) {
if (!accepts_self) {
lex.error("only a member function can return `self` (which accepts `self` first parameter)");
}
lex.next();
returns_self = true;
ret_type = TypeExpr::new_unit();
} else {
ret_type = parse_type(lex, genericsT_list);
}
}
if (is_entrypoint && (is_get_method || genericsT_list || !annotations.empty())) {
if (is_entrypoint && (is_get_method || genericsT_list || n_mutate_params || accepts_self)) {
throw ParseError(loc, "invalid declaration of a reserved function");
}
if (is_get_method && (genericsT_list || n_mutate_params || accepts_self)) {
throw ParseError(loc, "get methods can't have `mutate` and `self` params");
}
if (n_mutate_params) {
std::vector<TypeExpr*> ret_tensor_items;
ret_tensor_items.reserve(1 + n_mutate_params);
for (AnyV v_param : v_param_list->get_params()) {
if (v_param->as<ast_parameter>()->declared_as_mutate) {
ret_tensor_items.emplace_back(v_param->as<ast_parameter>()->param_type);
}
}
ret_tensor_items.emplace_back(ret_type ? ret_type : TypeExpr::new_hole());
ret_type = TypeExpr::new_tensor(std::move(ret_tensor_items));
}
AnyV v_body = nullptr;
@ -1030,17 +1088,19 @@ static AnyV parse_function_declaration(Lexer& lex, const std::vector<V<ast_annot
if (!ret_type) {
lex.error("asm function must specify return type");
}
v_body = parse_asm_func_body(lex, param_list);
v_body = parse_asm_func_body(lex, v_param_list);
} else {
lex.unexpected("{ function body }");
}
auto f_declaration = createV<ast_function_declaration>(loc, v_ident, param_list, v_body);
auto f_declaration = createV<ast_function_declaration>(loc, v_ident, v_param_list, v_body);
f_declaration->ret_type = ret_type ? ret_type : TypeExpr::new_hole();
f_declaration->is_entrypoint = is_entrypoint;
f_declaration->genericsT_list = genericsT_list;
f_declaration->marked_as_get_method = is_get_method;
f_declaration->marked_as_builtin = v_body->type == ast_empty;
f_declaration->accepts_self = accepts_self;
f_declaration->returns_self = returns_self;
for (auto v_annotation : annotations) {
switch (v_annotation->kind) {
@ -1054,7 +1114,7 @@ static AnyV parse_function_declaration(Lexer& lex, const std::vector<V<ast_annot
f_declaration->marked_as_pure = true;
break;
case AnnotationKind::method_id:
if (is_get_method || genericsT_list || is_entrypoint) {
if (is_get_method || genericsT_list || is_entrypoint || n_mutate_params || accepts_self) {
v_annotation->error("@method_id can be specified only for regular functions");
}
f_declaration->method_id = v_annotation->get_arg()->get_item(0)->as<ast_int_const>();