From 40f118baf895339d4d7cf2465acd136e91ecc3a6 Mon Sep 17 00:00:00 2001 From: Novus Nota <68142933+novusnota@users.noreply.github.com> Date: Fri, 22 Nov 2024 15:35:24 +0100 Subject: [PATCH] feat: enhanced the syntax, simplified the grammar --- src/grammar/ast.ts | 30 +++-------- src/grammar/grammar.ohm | 46 +++++++++------- src/grammar/grammar.ts | 96 ++++++++++------------------------ src/types/resolveExpression.ts | 2 +- 4 files changed, 64 insertions(+), 110 deletions(-) diff --git a/src/grammar/ast.ts b/src/grammar/ast.ts index bd3fd2b92..b0a2d0264 100644 --- a/src/grammar/ast.ts +++ b/src/grammar/ast.ts @@ -68,25 +68,7 @@ export type AstAsmShuffle = { }; // NOTE: should it be added to AstNode? -export type AstAsmExpression = - | AstAsmExpressionDef - | AstAsmExpressionList - | AstAsmPrimitive; - -/** `{ AstAsmExpression* } : AstAsmInstruction` */ -export type AstAsmExpressionDef = { - kind: "asm_expr_def"; - expressions: AstAsmExpression[]; - name: AstAsmInstruction; - loc: SrcInfo; -}; - -/** `{ AstAsmExpression* }` */ -export type AstAsmExpressionList = { - kind: "asm_expr_list"; - expressions: AstAsmExpression[]; - loc: SrcInfo; -}; +export type AstAsmExpression = AstAsmPrimitive; export type AstAsmPrimitive = | AstAsmString @@ -138,16 +120,16 @@ export type AstAsmControlReg = { }; /** - * `s0`, `s1`, ..., `s15` - * or `i s()`, - * where i ∈ [0, 255] + * `s0`, `s1`, ..., `s255` */ export type AstAsmStackReg = { kind: "asm_stack_reg"; value: bigint; /** - * Is it either of `s0`, `s1`, ..., `s15`? - * If not, then it's `i s()`, where i ∈ [0, 255] + * Whether there's a single literal for expressing this stack register in FunC/Fift? + * + * Single literals are written like `s0`, `s1`, ..., `s15`, + * while other stack registers are expressed as `i s()`, where i ∈ [0, 255] */ isLiteral: boolean; loc: SrcInfo; diff --git a/src/grammar/grammar.ohm b/src/grammar/grammar.ohm index c8eca7ea2..5422fd286 100644 --- a/src/grammar/grammar.ohm +++ b/src/grammar/grammar.ohm @@ -99,30 +99,40 @@ Tact { | external "(" Parameter? ")" "{" Statement* "}" --externalRegular | external "(" stringLiteral ")" "{" Statement* "}" --externalComment - // FIXME: prettyPrinter.ts → writeFunction.ts, hash.ts, compare.ts - AsmExpression = "{" AsmExpression* "}" ":" asmInstruction --definition - | "{" AsmExpression* "}" --list + // FIXME: prettyPrinter.ts + writeFunction.ts, hash.ts, compare.ts + AsmExpression = "{" (~"}" any)* "}" --listWithHelpfulErrorMessage // used only for error messages | asmPrimitive - // Various instructions not forming a block - asmPrimitive = "\"" (~"\"" any)* "\"" --string - | "x{" hexDigit* "_"? "}" --hex - | "b{" binDigit* "}" --bin - | "c" digit digit? --controlRegister - | "s" digit digit? --stackRegister - | digit digit? digit? whiteSpace+ "s()" --stackRegister255 - | "-"? digit+ --number - | asmInstruction --custom + // Various primitives and instructions not forming a block + asmPrimitive = asmString + | asmHex + | asmBin + | asmControlReg + | asmStackReg + | asmStackReg256 + | asmNumber + | asmInstruction + + // It's here mostly to provide better error messages + // for people coming from FunC/Fift background + asmString (string) = "\"" (~"\"" any)* "\"" + asmHex (hex bitstring) = "x{" hexDigit* "_"? "}" + asmBin (binary bitstring) = "b{" binDigit* "}" + asmControlReg (control register) = "c" digit digit? + asmStackReg (stack register) = "s" digit digit? digit? + // It's here mostly to provide better error messages + // for people coming from FunC/Fift background, + // gently guiding them towards simpler s0...s255 syntax + asmStackReg256 (indexed stack register) = digit digit? digit? whiteSpace+ "s()" + asmNumber (number) = "-"? digit+ // To ensure correct parse, // 1. instructions cannot contain braces // 2. and cannot be braces themselves - asmInstruction = ~(("{" | "}") ~asmWord) asmWord - - // Additionally, the minimal word length is 2 characters, - // not including the optional minus at the start - asmWord = "-"? asmWordPart asmWordPart+ - asmWordPart = (letterAscii | digit | "_" | "#" | ":") + asmInstruction (an instruction) = ~(("{" | "}") ~asmWord) asmWord + asmWord = "-"? asmWordPart+ + // NOTE: We can limit this further to letterAsciiUC, then add optional "l"? by the end. + asmWordPart = letterAscii | digit | "_" | "#" | ":" Statement = StatementLet | StatementBlock diff --git a/src/grammar/grammar.ts b/src/grammar/grammar.ts index 589d31f13..5557b5152 100644 --- a/src/grammar/grammar.ts +++ b/src/grammar/grammar.ts @@ -515,37 +515,32 @@ semantics.addOperation("astOfAsmExpression", { AsmExpression(expression) { return expression.astOfAsmExpression(); }, - AsmExpression_definition(_lbrace, expressions, _rbrace, _colon, name) { - return { - kind: "asm_expr_def", - expressions: expressions.children.map((e) => - e.astOfAsmExpression(), - ), - name: name.astOfAsmExpression(), - loc: createRef(this), - }; - }, - AsmExpression_list(_lbrace, expressions, _rbrace) { - return { - kind: "asm_expr_list", - expressions: expressions.children.map((e) => - e.astOfAsmExpression(), - ), - loc: createRef(this), - }; + AsmExpression_listWithHelpfulErrorMessage(_lbrace, _any, _rbrace) { + throwSyntaxError( + 'The Fift\'s word list syntax "{ ... }" is not supported', + createRef(this), + ); }, asmPrimitive(primitive) { return primitive.astOfAsmExpression(); }, - asmPrimitive_string(_startQuotationMark, string, _endQuotationMark) { - // TODO(grammar.ts): perform checks for inner contents of the string + asmString(_startQuotationMark, string, _endQuotationMark) { + // A best-effor check to inform the user of errors in translation + // of TVM instructions between FunC (or other languages) and Tact + const src = string.sourceString; + if (src.match(/^-?[A-Z0-9_#:]+l?$/) !== null) { + throwSyntaxError( + `The instruction "${src}" doesn't exist, did you mean ${src}?`, + createRef(this), + ); + } return { kind: "asm_string", value: string.sourceString, loc: createRef(this), }; }, - asmPrimitive_hex(_prefix, digits, optUnderscore, _rbrace) { + asmHex(_prefix, digits, optUnderscore, _rbrace) { const length = digits.numChildren; const underscore = unwrapOptNode(optUnderscore, (t) => t.sourceString); if (length > 128) { @@ -561,7 +556,7 @@ semantics.addOperation("astOfAsmExpression", { loc: createRef(this), }; }, - asmPrimitive_bin(_prefix, digits, _rbrace) { + asmBin(_prefix, digits, _rbrace) { const length = digits.numChildren; if (length > 128) { throwSyntaxError( @@ -575,7 +570,7 @@ semantics.addOperation("astOfAsmExpression", { loc: createRef(this), }; }, - asmPrimitive_controlRegister(_prefix, digit, optDigit) { + asmControlReg(_prefix, digit, optDigit) { const secondDigit = unwrapOptNode(optDigit, (t) => t); let number = bigintOfIntLiteral(digit); if (secondDigit !== null) { @@ -593,32 +588,7 @@ semantics.addOperation("astOfAsmExpression", { loc: createRef(this), }; }, - asmPrimitive_stackRegister(_prefix, digit, optDigit) { - const secondDigit = unwrapOptNode(optDigit, (t) => t); - let number = bigintOfIntLiteral(digit); - if (secondDigit !== null) { - number = number * 10n + bigintOfIntLiteral(secondDigit); - } - if (number > 15) { - throwSyntaxError( - `The stack register literal s${number.toString(10)} doesn't exist. Perhaps you meant to write "${number.toString(10)} s()" instead?`, - createRef(this), - ); - } - return { - kind: "asm_stack_reg", - isLiteral: true, - value: number, - loc: createRef(this), - }; - }, - asmPrimitive_stackRegister255( - digit, - optSecondDigit, - optThirdDigit, - _ws, - _sParentheses, - ) { + asmStackReg(_prefix, digit, optSecondDigit, optThirdDigit) { const secondDigit = unwrapOptNode(optSecondDigit, (t) => t); const thirdDigit = unwrapOptNode(optThirdDigit, (t) => t); let number = bigintOfIntLiteral(digit); @@ -635,23 +605,26 @@ semantics.addOperation("astOfAsmExpression", { } else if (secondDigit !== null) { number = number * 10n + bigintOfIntLiteral(secondDigit); } - if (number > 255) { + if (number > 255n || number < 0n) { throwSyntaxError( - `The stack register ${number.toString(10)} s() doesn't exist`, + `The stack register s${number.toString(10)} doesn't exist`, createRef(this), ); } return { kind: "asm_stack_reg", - isLiteral: false, + isLiteral: number < 16n ? true : false, value: number, loc: createRef(this), }; }, - // TODO: write the last things here, then fix the asm generation - // TODO: then, fix the prettyPrinter -- leave stuff as is, don't print - // TODO: hash.ts and compare.ts too, somehow... - asmPrimitive_number(optNeg, digits) { + asmStackReg256(digit, optSecondDigit, optThirdDigit, _ws, _literal) { + throwSyntaxError( + `The "i s()" syntax is deprecated in favor of using one of the "s0", "s1", ..., "s255" stack registers`, + createRef(this), + ); + }, + asmNumber(optNeg, digits) { const negate = unwrapOptNode(optNeg, (t) => t); let number = bigintOfIntLiteral(digits); if (negate !== null) { @@ -663,17 +636,6 @@ semantics.addOperation("astOfAsmExpression", { loc: createRef(this), }; }, - // function astOfNumber(node: Node): AstNode { - // return createAstNode({ - // kind: "number", - // base: baseOfIntLiteral(node), - // value: bigintOfIntLiteral(node), - // loc: createRef(node), - // }); - // } - asmPrimitive_custom(instruction) { - return instruction.astOfAsmExpression(); - }, }); semantics.addOperation("astOfFunctionAttributes", { diff --git a/src/types/resolveExpression.ts b/src/types/resolveExpression.ts index 2500cffca..ba38570a0 100644 --- a/src/types/resolveExpression.ts +++ b/src/types/resolveExpression.ts @@ -498,7 +498,7 @@ function resolveStaticCall( ) !== undefined ) { throwCompilationError( - `Static function ${idTextErr(exp.function)} does not exist. Perhaps you meant to call ".${idText(exp.function)}(...)" extension function?`, + `Static function ${idTextErr(exp.function)} does not exist. Perhaps you meant to call ".${idText(exp.function)}(...)" or "self.${idText(exp.function)}(...)"?`, exp.loc, ); }