Skip to content

Commit

Permalink
feat: local type inference for let statements (tact-lang#198)
Browse files Browse the repository at this point in the history
Type annotations for `let`-binders are optional now
  • Loading branch information
Gusarich authored Jun 14, 2024
1 parent 7afc87e commit c66a567
Show file tree
Hide file tree
Showing 16 changed files with 789 additions and 19 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- Trailing semicolons in struct and message declarations are optional now: PR [#395](https://github.com/tact-lang/tact/pull/395)
- Tests are refactored and renamed to convey the sense of what is being tested and to reduce the amount of merge conflicts during development: PR [#402](https://github.com/tact-lang/tact/pull/402)
- `let` statements can now be used without an explicit type declaration and determine the type automatically if it was not specified: PR [#198](https://github.com/tact-lang/tact/pull/198)
- The outdated TextMate-style grammar files for text editors have been removed (the most recent grammar files can be found in the [tact-sublime](https://github.com/tact-lang/tact-sublime) repo): PR [#404](https://github.com/tact-lang/tact/pull/404)
- The JSON schema for `tact.config.json` has been moved to the `json-schemas` project folder: PR [#404](https://github.com/tact-lang/tact/pull/404)

Expand Down
6 changes: 5 additions & 1 deletion src/generator/writers/writeFunction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,11 @@ export function writeStatement(
return;
} else if (f.kind === "statement_let") {
// Contract/struct case
const t = resolveTypeRef(ctx.ctx, f.type);
const t =
f.type === null
? getExpType(ctx.ctx, f.expression)
: resolveTypeRef(ctx.ctx, f.type);

if (t.kind === "ref") {
const tt = getType(ctx.ctx, t.name);
if (tt.kind === "contract" || tt.kind === "struct") {
Expand Down
3 changes: 1 addition & 2 deletions src/grammar/ast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -418,7 +418,7 @@ export type ASTStatementLet = {
kind: "statement_let";
id: number;
name: string;
type: ASTTypeRef;
type: ASTTypeRef | null;
expression: ASTExpression;
ref: ASTRef;
};
Expand Down Expand Up @@ -729,7 +729,6 @@ export function traverse(node: ASTNode, callback: (node: ASTNode) => void) {
//

if (node.kind === "statement_let") {
traverse(node.type, callback);
traverse(node.expression, callback);
}
if (node.kind === "statement_return") {
Expand Down
5 changes: 4 additions & 1 deletion src/grammar/clone.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export function cloneNode<T extends ASTNode>(src: T): T {
} else if (src.kind === "statement_let") {
return cloneASTNode({
...src,
type: cloneASTNode(src.type),
type: src.type ? cloneASTNode(src.type) : null,
expression: cloneNode(src.expression),
});
} else if (src.kind === "statement_condition") {
Expand Down Expand Up @@ -132,12 +132,14 @@ export function cloneNode<T extends ASTNode>(src: T): T {
} else if (src.kind === "def_function") {
return cloneASTNode({
...src,
return: src.return ? cloneASTNode(src.return) : null,
statements: src.statements ? src.statements.map(cloneNode) : null,
args: src.args.map(cloneNode),
});
} else if (src.kind === "def_native_function") {
return cloneASTNode({
...src,
return: src.return ? cloneASTNode(src.return) : null,
args: src.args.map(cloneNode),
});
} else if (src.kind === "def_receive") {
Expand All @@ -158,6 +160,7 @@ export function cloneNode<T extends ASTNode>(src: T): T {
} else if (src.kind === "def_constant") {
return cloneASTNode({
...src,
type: cloneASTNode(src.type),
value: src.value ? cloneNode(src.value) : src.value,
});
}
Expand Down
2 changes: 1 addition & 1 deletion src/grammar/grammar.ohm
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ Tact {

StatementBlock = "{" Statement* "}"

StatementLet = let id ":" Type "=" Expression ";"
StatementLet = let id (":" Type)? "=" Expression ";"

StatementReturn = return Expression? ";"

Expand Down
12 changes: 10 additions & 2 deletions src/grammar/grammar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -571,13 +571,21 @@ semantics.addOperation<ASTNode>("astOfDeclaration", {
semantics.addOperation<ASTNode>("astOfStatement", {
// TODO: process StatementBlock

StatementLet(_letKwd, id, _colon, type, _equals, expression, _semicolon) {
StatementLet(
_letKwd,
id,
_optColon,
optType,
_equals,
expression,
_semicolon,
) {
checkVariableName(id.sourceString, createRef(id));

return createNode({
kind: "statement_let",
name: id.sourceString,
type: type.astOfType(),
type: unwrapOptNode(optType, (t) => t.astOfType()),
expression: expression.astOfExpression(),
ref: createRef(this),
});
Expand Down
Loading

0 comments on commit c66a567

Please sign in to comment.