Skip to content

Commit f1562ff

Browse files
committed
feat: new asm parser
WIP WIP WIP
1 parent 6320faa commit f1562ff

12 files changed

+343
-3126
lines changed

src/generator/writers/writeFunction.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import { resolveFuncTupleType } from "./resolveFuncTupleType";
2323
import { ops } from "./ops";
2424
import { freshIdentifier } from "./freshIdentifier";
2525
import { idTextErr, throwInternalCompilerError } from "../../errors";
26-
import { ppAsmShuffle } from "../../prettyPrinter";
26+
import { ppAsmShuffle, ppAsmExpressionsBlock } from "../../prettyPrinter";
2727

2828
export function writeCastedExpression(
2929
expression: AstExpression,
@@ -581,7 +581,8 @@ export function writeFunction(f: FunctionDescription, ctx: WriterContext) {
581581
};
582582
ctx.asm(
583583
ppAsmShuffle(asmShuffleEscaped),
584-
fAst.instructions.join(" "),
584+
ppAsmExpressionsBlock(fAst.expressions),
585+
// fAst.instructions.join(" "),
585586
);
586587
});
587588
if (f.isMutating) {

src/grammar/ast.ts

+108-3
Original file line numberDiff line numberDiff line change
@@ -57,17 +57,120 @@ export type AstAsmFunctionDef = {
5757
name: AstId;
5858
return: AstType | null;
5959
params: AstTypedParameter[];
60-
instructions: AstAsmInstruction[];
60+
expressions: AstAsmExpression[];
6161
id: number;
6262
loc: SrcInfo;
6363
};
6464

65-
export type AstAsmInstruction = string;
6665
export type AstAsmShuffle = {
6766
args: AstId[];
6867
ret: AstNumber[];
6968
};
7069

70+
// NOTE: should it be added to AstNode?
71+
export type AstAsmExpression =
72+
| AstAsmExpressionDef
73+
| AstAsmExpressionList
74+
| AstAsmPrimitive;
75+
76+
/** `{ AstAsmExpression* } : AstAsmInstruction` */
77+
export type AstAsmExpressionDef = {
78+
kind: "asm_expr_def";
79+
expressions: AstAsmExpression[];
80+
name: AstAsmInstruction;
81+
loc: SrcInfo;
82+
};
83+
84+
/** `{ AstAsmExpression* }` */
85+
export type AstAsmExpressionList = {
86+
kind: "asm_expr_list";
87+
expressions: AstAsmExpression[];
88+
loc: SrcInfo;
89+
};
90+
91+
export type AstAsmPrimitive =
92+
| AstAsmString
93+
| AstAsmHex
94+
| AstAsmBin
95+
| AstAsmControlReg
96+
| AstAsmStackReg
97+
| AstAsmNumber
98+
| AstAsmInstruction;
99+
100+
/** `"..."` */
101+
export type AstAsmString = {
102+
kind: "asm_string";
103+
value: string;
104+
loc: SrcInfo;
105+
};
106+
107+
/** `x{babe...cafe_}` */
108+
export type AstAsmHex = {
109+
kind: "asm_hex";
110+
/**
111+
* Stores everything inside braces `{}` as is
112+
*
113+
* NOTE: May (?) be changed to bigint or even removed in favor of changed AstAsmNumber
114+
*/
115+
value: string;
116+
/** Does it have an _ right before the } or not? */
117+
isPadded: boolean;
118+
loc: SrcInfo;
119+
};
120+
121+
/** `b{0101...0101}` */
122+
export type AstAsmBin = {
123+
kind: "asm_bin";
124+
/**
125+
* Stores everything inside braces `{}` as is
126+
*
127+
* NOTE: May (?) be changed to bigint or even removed in favor of changed AstAsmNumber
128+
*/
129+
value: string;
130+
loc: SrcInfo;
131+
};
132+
133+
/** `c0`, `c1`, ..., `c15` */
134+
export type AstAsmControlReg = {
135+
kind: "asm_control_reg";
136+
value: bigint;
137+
loc: SrcInfo;
138+
};
139+
140+
/**
141+
* `s0`, `s1`, ..., `s15`
142+
* or `i s()`,
143+
* where i ∈ [0, 255]
144+
*/
145+
export type AstAsmStackReg = {
146+
kind: "asm_stack_reg";
147+
value: bigint;
148+
/**
149+
* Is it either of `s0`, `s1`, ..., `s15`?
150+
* If not, then it's `i s()`, where i ∈ [0, 255]
151+
*/
152+
isLiteral: boolean;
153+
loc: SrcInfo;
154+
};
155+
156+
/** NOTE: can later be aliased to Tact number literals */
157+
export type AstAsmNumber = {
158+
kind: "asm_number";
159+
value: bigint;
160+
loc: SrcInfo;
161+
};
162+
163+
export function astAsmNumberToString(n: AstAsmNumber): string {
164+
return n.value.toString(10);
165+
}
166+
167+
/** `MYCODE`, `ADDRSHIFT#MOD`, `IF:`, `XCHG3_l`, `2SWAP`, `-ROT`, etc. */
168+
export type AstAsmInstruction = {
169+
kind: "asm_instruction";
170+
text: string;
171+
loc: SrcInfo;
172+
};
173+
71174
export type AstFunctionDecl = {
72175
kind: "function_decl";
73176
attributes: AstFunctionAttribute[];
@@ -507,7 +610,9 @@ export type AstFuncId = {
507610
loc: SrcInfo;
508611
};
509612

510-
export function idText(ident: AstId | AstFuncId | AstTypeId): string {
613+
export function idText(
614+
ident: AstId | AstFuncId | AstTypeId | AstAsmInstruction,
615+
): string {
511616
return ident.text;
512617
}
513618

src/grammar/compare.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ import {
5151
AstNode,
5252
AstFuncId,
5353
AstAsmFunctionDef,
54-
AstAsmInstruction,
54+
AstAsmExpression,
5555
AstDestructMapping,
5656
AstStatementDestruct,
5757
} from "./ast";
@@ -850,8 +850,8 @@ export class AstComparator {
850850
}
851851

852852
private compareAsmInstructions(
853-
instructions1: AstAsmInstruction[],
854-
instructions2: AstAsmInstruction[],
853+
instructions1: AstAsmExpression[],
854+
instructions2: AstAsmExpression[],
855855
): boolean {
856856
if (instructions1.length !== instructions2.length) {
857857
return false;

src/grammar/grammar.ohm

+25-16
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ Tact {
2222
ModuleFunction = FunctionDefinition
2323
| AsmFunction
2424

25-
AsmFunction = "asm" AsmShuffle? FunctionAttribute* fun id Parameters (":" Type)? "{" AsmInstruction* "}"
25+
AsmFunction = "asm" AsmShuffle? FunctionAttribute* fun id Parameters (":" Type)? "{" AsmExpression* "}"
2626

2727
ModuleConstant = ConstantDefinition
2828

@@ -99,21 +99,30 @@ Tact {
9999
| external "(" Parameter? ")" "{" Statement* "}" --externalRegular
100100
| external "(" stringLiteral ")" "{" Statement* "}" --externalComment
101101

102-
AsmInstruction = "[" &#whiteSpace AsmInstruction* "]" &#whiteSpace --internal
103-
| "{" &#whiteSpace AsmInstruction* "}" &#whiteSpace --list
104-
| "({)" &#whiteSpace AsmInstruction* "(})" &#whiteSpace --listNoStateCheck
105-
| ("abort\"" | ".\"" | "+\"" | "\"") (~"\"" any)* "\"" &#whiteSpace --string
106-
| "'" &#whiteSpace asmInstruction --tick
107-
| "char" &#whiteSpace (~whiteSpace any) &#whiteSpace --char
108-
| ("x{" | "B{") ~#whiteSpace hexDigit* ~#whiteSpace ("_" ~#whiteSpace)? "}" &#whiteSpace --hexLiteral
109-
| "b{" ~#whiteSpace binDigit* ~#whiteSpace "}" &#whiteSpace --binLiteral
110-
| asmInstruction --custom
111-
112-
// Instructions exclude some begin and end words to ensure correct parse
113-
asmInstruction = ~(("[" | "]" | "{" | "}" | "({)" | "(})") ~asmWord) asmWord
114-
115-
// A chunk of non-whitespace characters forms a word in Fift
116-
asmWord = (~whiteSpace any)+
102+
// FIXME: prettyPrinter.ts → writeFunction.ts, hash.ts, compare.ts
103+
AsmExpression = "{" AsmExpression* "}" ":" asmInstruction --definition
104+
| "{" AsmExpression* "}" --list
105+
| asmPrimitive
106+
107+
// Various instructions not forming a block
108+
asmPrimitive = "\"" (~"\"" any)* "\"" --string
109+
| "x{" hexDigit* "_"? "}" --hex
110+
| "b{" binDigit* "}" --bin
111+
| "c" digit digit? --controlRegister
112+
| "s" digit digit? --stackRegister
113+
| digit digit? digit? whiteSpace+ "s()" --stackRegister255
114+
| "-"? digit+ --number
115+
| asmInstruction --custom
116+
117+
// To ensure correct parse,
118+
// 1. instructions cannot contain braces
119+
// 2. and cannot be braces themselves
120+
asmInstruction = ~(("{" | "}") ~asmWord) asmWord
121+
122+
// Additionally, the minimal word length is 2 characters,
123+
// not including the optional minus at the start
124+
asmWord = "-"? asmWordPart asmWordPart+
125+
asmWordPart = (letterAscii | digit | "_" | "#" | ":")
117126

118127
Statement = StatementLet
119128
| StatementBlock

src/grammar/grammar.spec.ts

+1-15
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { parse } from "./grammar";
2-
import { AstModule, SrcInfo, __DANGER_resetNodeId } from "./ast";
2+
import { SrcInfo, __DANGER_resetNodeId } from "./ast";
33
import { loadCases } from "../utils/loadCases";
44

55
expect.addSnapshotSerializer({
@@ -12,20 +12,6 @@ describe("grammar", () => {
1212
__DANGER_resetNodeId();
1313
});
1414

15-
// Test parsing of known Fift projects, wrapped in asm functions of Tact
16-
for (const r of loadCases(__dirname + "/test-asm/")) {
17-
it("should parse " + r.name, () => {
18-
const parsed: AstModule | undefined = parse(
19-
r.code,
20-
"<unknown>",
21-
"user",
22-
);
23-
24-
// Don't produce snapshots
25-
expect(parsed).toBeDefined();
26-
});
27-
}
28-
2915
for (const r of loadCases(__dirname + "/test/")) {
3016
it("should parse " + r.name, () => {
3117
expect(parse(r.code, "<unknown>", "user")).toMatchSnapshot();

0 commit comments

Comments
 (0)