Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(cli): constant expression evaluation #462

Merged
merged 3 commits into from
Jun 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/tact.yml
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,10 @@ jobs:
run: |
! tact --config bin/test/fail.config.json
- name: CLI Test | Evaluate expression
run: |
tact -e '(1 + 2 * (pow(3,4) - 2) << 1 & 0x54 | 33 >> 1) * 2 + 2'
- name: Link Tact compiler
run: |
yarn link
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- `-e` / `--eval` CLI flags to evaluate constant Tact expressions: PR [#462](https://github.com/tact-lang/tact/pull/462)

### Changed

### Fixed
Expand Down
40 changes: 28 additions & 12 deletions bin/tact
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ meowModule.then(
--with-decompilation Full compilation followed by decompilation of produced binary code
--func Output intermediate FunC code and exit
--check Perform syntax and type checking, then exit
-e, --eval EXPRESSION Evaluate a Tact expression and exit
-v, --version Print Tact compiler version and exit
-h, --help Display this text and exit

Expand All @@ -39,18 +40,21 @@ meowModule.then(
shortFlag: "c",
type: "string",
isRequired: (flags, _) => {
// Require a config when the projects are specified AND version/help are not specified
if (
// Require a config when the projects are specified
// AND version/help are not specified
// AND eval is not specified
return (
flags.projects.length !== 0 &&
!flags.version &&
!flags.help
) {
return true;
}
// Don't require it otherwise
return false;
!flags.help &&
!flags.eval
);
},
},
eval: {
shortFlag: "e",
type: "string",
},
projects: { shortFlag: "p", type: "string", isMultiple: true },
withDecompilation: { type: "boolean", default: false },
func: { type: "boolean", default: false },
Expand All @@ -64,10 +68,7 @@ meowModule.then(

// Helper function to write less in following checks
const isEmptyConfigAndInput = () => {
if (cli.flags.config === undefined && cli.input.length === 0) {
return true;
}
return false;
return cli.flags.config === undefined && cli.input.length === 0;
};

// Show help regardless of other flags
Expand All @@ -80,6 +81,21 @@ meowModule.then(
cli.showVersion();
}

// Evaluate expression regardless of other flags
if (cli.flags.eval) {
const result = main.parseAndEvalExpression(cli.flags.eval);
switch (result.kind) {
case "ok": {
console.log(result.value);
process.exit(0);
}
case "error": {
console.log(result.message);
process.exit(30);
}
}
}

// Disallow specifying both config or Tact source file at the same time
if (cli.flags.config !== undefined && cli.input.length > 0) {
console.log(
Expand Down
9 changes: 9 additions & 0 deletions src/grammar/grammar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
ASTAugmentedAssignOperation,
ASTConstantAttribute,
ASTContractAttribute,
ASTExpression,
ASTFunctionAttribute,
ASTNode,
ASTProgram,
Expand Down Expand Up @@ -1189,6 +1190,14 @@ export function parse(
});
}

export function parseExpression(sourceCode: string): ASTExpression {
const matchResult = rawGrammar.match(sourceCode, "Expression");
if (matchResult.failed()) {
throwParseError(matchResult, "");
}
return semantics(matchResult).astOfExpression();
}

export function parseImports(
src: string,
path: string,
Expand Down
27 changes: 27 additions & 0 deletions src/interpreter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { evalConstantExpression } from "./constEval";
import { CompilerContext } from "./context";
import { TactConstEvalError, TactParseError } from "./errors";
import { parseExpression } from "./grammar/grammar";
import { Value } from "./types/types";

export type EvalResult =
| { kind: "ok"; value: Value }
| { kind: "error"; message: string };

export function parseAndEvalExpression(sourceCode: string): EvalResult {
try {
const ast = parseExpression(sourceCode);
const constEvalResult = evalConstantExpression(
ast,
new CompilerContext(),
);
return { kind: "ok", value: constEvalResult };
} catch (error) {
if (
error instanceof TactParseError ||
error instanceof TactConstEvalError
)
return { kind: "error", message: error.message };
throw error;
}
}
2 changes: 2 additions & 0 deletions src/node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,3 +122,5 @@ export async function run(args: {
}

export { createNodeFileSystem } from "./vfs/createNodeFileSystem";

export { parseAndEvalExpression } from "./interpreter";
Loading