Skip to content

Commit c53d0c8

Browse files
committed
Remove loop lowering setup in favour of custom compilation paths for for/while loops
1 parent 4ef8622 commit c53d0c8

10 files changed

Lines changed: 184 additions & 309 deletions

File tree

packages/cashc/src/ast/AST.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,8 @@ export class WhileNode extends ControlStatementNode {
206206
}
207207

208208
export class ForNode extends ControlStatementNode {
209+
symbolTable?: SymbolTable;
210+
209211
constructor(
210212
public init: VariableDefinitionNode | AssignNode,
211213
public condition: ExpressionNode,

packages/cashc/src/compiler.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import CashScriptParser from './grammar/CashScriptParser.js';
1212
import SymbolTableTraversal from './semantic/SymbolTableTraversal.js';
1313
import TypeCheckTraversal from './semantic/TypeCheckTraversal.js';
1414
import EnsureFinalRequireTraversal from './semantic/EnsureFinalRequireTraversal.js';
15-
import LoopLoweringTraversal from './semantic/LoopLoweringTraversal.js';
1615

1716
export const DEFAULT_COMPILER_OPTIONS: CompilerOptions = {
1817
enforceFunctionParameterTypes: true,
@@ -24,9 +23,6 @@ export function compileString(code: string, compilerOptions: CompilerOptions = {
2423
// Lexing + parsing
2524
let ast = parseCode(code);
2625

27-
// Loop lowering
28-
ast = ast.accept(new LoopLoweringTraversal()) as Ast;
29-
3026
// Semantic analysis
3127
ast = ast.accept(new SymbolTableTraversal()) as Ast;
3228
ast = ast.accept(new TypeCheckTraversal()) as Ast;

packages/cashc/src/generation/GenerateTargetTraversal.ts

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ import {
4949
ConsoleStatementNode,
5050
SliceNode,
5151
DoWhileNode,
52+
WhileNode,
53+
ForNode,
5254
} from '../ast/AST.js';
5355
import AstTraversal from '../ast/AstTraversal.js';
5456
import { GlobalFunction, Class } from '../ast/Globals.js';
@@ -450,6 +452,69 @@ export default class GenerateTargetTraversalWithLocation extends AstTraversal {
450452
return node;
451453
}
452454

455+
visitWhile(node: WhileNode): Node {
456+
this.scopeDepth += 1;
457+
this.emit(Op.OP_BEGIN, { location: node.location, positionHint: PositionHint.START });
458+
459+
node.condition = this.visit(node.condition);
460+
this.emit(Op.OP_DUP, { location: node.condition.location, positionHint: PositionHint.END });
461+
this.pushToStack('(value)');
462+
this.emit(Op.OP_TOALTSTACK, { location: node.condition.location, positionHint: PositionHint.END });
463+
this.popFromStack();
464+
465+
this.popFromStack();
466+
this.emit(Op.OP_IF, { location: node.block.location, positionHint: PositionHint.START });
467+
468+
const bodyStackDepth = this.stack.length;
469+
node.block = this.visit(node.block) as BlockNode;
470+
this.removeScopedVariables(bodyStackDepth, node.block);
471+
472+
this.emit(Op.OP_ENDIF, { location: node.block.location, positionHint: PositionHint.END });
473+
this.emit(Op.OP_FROMALTSTACK, { location: node.block.location, positionHint: PositionHint.END });
474+
this.pushToStack('(value)');
475+
this.emit(Op.OP_NOT, { location: node.location, positionHint: PositionHint.END });
476+
this.emit(Op.OP_UNTIL, { location: node.location, positionHint: PositionHint.END });
477+
this.popFromStack();
478+
479+
this.scopeDepth -= 1;
480+
481+
return node;
482+
}
483+
484+
visitFor(node: ForNode): Node {
485+
const forScopeStackDepth = this.stack.length;
486+
node.init = this.visit(node.init) as VariableDefinitionNode | AssignNode;
487+
488+
this.scopeDepth += 1;
489+
this.emit(Op.OP_BEGIN, { location: node.location, positionHint: PositionHint.START });
490+
491+
node.condition = this.visit(node.condition);
492+
this.emit(Op.OP_DUP, { location: node.condition.location, positionHint: PositionHint.END });
493+
this.pushToStack('(value)');
494+
this.emit(Op.OP_TOALTSTACK, { location: node.condition.location, positionHint: PositionHint.END });
495+
this.popFromStack();
496+
497+
this.popFromStack();
498+
this.emit(Op.OP_IF, { location: node.block.location, positionHint: PositionHint.START });
499+
500+
const bodyStackDepth = this.stack.length;
501+
node.block = this.visit(node.block) as BlockNode;
502+
node.update = this.visit(node.update) as AssignNode;
503+
this.removeScopedVariables(bodyStackDepth, node.block);
504+
505+
this.emit(Op.OP_ENDIF, { location: node.block.location, positionHint: PositionHint.END });
506+
this.emit(Op.OP_FROMALTSTACK, { location: node.block.location, positionHint: PositionHint.END });
507+
this.pushToStack('(value)');
508+
this.emit(Op.OP_NOT, { location: node.location, positionHint: PositionHint.END });
509+
this.emit(Op.OP_UNTIL, { location: node.location, positionHint: PositionHint.END });
510+
this.popFromStack();
511+
512+
this.scopeDepth -= 1;
513+
this.removeScopedVariables(forScopeStackDepth, node);
514+
515+
return node;
516+
}
517+
453518
removeScopedVariables(depthBeforeScope: number, node: Node): void {
454519
const dropCount = this.stack.length - depthBeforeScope;
455520
for (let i = 0; i < dropCount; i += 1) {

packages/cashc/src/semantic/EnsureFinalRequireTraversal.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import {
88
BranchNode,
99
ConsoleStatementNode,
1010
DoWhileNode,
11+
WhileNode,
12+
ForNode,
1113
} from '../ast/AST.js';
1214
import AstTraversal from '../ast/AstTraversal.js';
1315
import { EmptyContractError, EmptyFunctionError, FinalRequireStatementError } from '../Errors.js';
@@ -51,8 +53,12 @@ function ensureFinalStatementIsRequire(statements: StatementNode[] = []): void {
5153
return;
5254
}
5355

54-
// TODO: Revisit this later, for now we allow do-while loops to not have a require() at the end
55-
if (finalStatement instanceof DoWhileNode) {
56+
// TODO: Revisit this later, for now we allow loops to not have a require() at the end
57+
if (
58+
finalStatement instanceof DoWhileNode
59+
|| finalStatement instanceof WhileNode
60+
|| finalStatement instanceof ForNode
61+
) {
5662
// ensureFinalStatementIsRequire(finalStatement.block.statements);
5763
return;
5864
}

packages/cashc/src/semantic/LoopLoweringTraversal.ts

Lines changed: 0 additions & 83 deletions
This file was deleted.

packages/cashc/src/semantic/SymbolTableTraversal.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
TupleAssignmentNode,
1515
ConsoleStatementNode,
1616
ConsoleParameterNode,
17+
ForNode,
1718
} from '../ast/AST.js';
1819
import AstTraversal from '../ast/AstTraversal.js';
1920
import { SymbolTable, Symbol, SymbolType } from '../ast/SymbolTable.js';
@@ -98,6 +99,24 @@ export default class SymbolTableTraversal extends AstTraversal {
9899
return node;
99100
}
100101

102+
visitFor(node: ForNode): Node {
103+
node.symbolTable = new SymbolTable(this.symbolTables[0]);
104+
this.symbolTables.unshift(node.symbolTable);
105+
106+
node.init = this.visit(node.init) as VariableDefinitionNode | AssignNode;
107+
node.condition = this.visit(node.condition);
108+
node.update = this.visit(node.update) as AssignNode;
109+
node.block = this.visit(node.block);
110+
111+
const unusedSymbols = node.symbolTable.unusedSymbols();
112+
if (unusedSymbols.length !== 0) {
113+
throw new UnusedVariableError(unusedSymbols[0]);
114+
}
115+
116+
this.symbolTables.shift();
117+
return node;
118+
}
119+
101120
visitVariableDefinition(node: VariableDefinitionNode): Node {
102121
if (this.symbolTables[0].get(node.name)) {
103122
throw new VariableRedefinitionError(node);

packages/cashc/src/semantic/TypeCheckTraversal.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ import {
2929
SliceNode,
3030
IntLiteralNode,
3131
DoWhileNode,
32+
WhileNode,
33+
ForNode,
3234
} from '../ast/AST.js';
3335
import AstTraversal from '../ast/AstTraversal.js';
3436
import {
@@ -113,6 +115,30 @@ export default class TypeCheckTraversal extends AstTraversal {
113115
return node;
114116
}
115117

118+
visitWhile(node: WhileNode): Node {
119+
node.condition = this.visit(node.condition);
120+
node.block = this.visit(node.block);
121+
122+
if (!implicitlyCastable(node.condition.type, PrimitiveType.BOOL)) {
123+
throw new TypeError(node.condition, node.condition.type, PrimitiveType.BOOL);
124+
}
125+
126+
return node;
127+
}
128+
129+
visitFor(node: ForNode): Node {
130+
node.init = this.visit(node.init) as AssignNode | VariableDefinitionNode;
131+
node.condition = this.visit(node.condition);
132+
node.update = this.visit(node.update) as AssignNode;
133+
node.block = this.visit(node.block);
134+
135+
if (!implicitlyCastable(node.condition.type, PrimitiveType.BOOL)) {
136+
throw new TypeError(node.condition, node.condition.type, PrimitiveType.BOOL);
137+
}
138+
139+
return node;
140+
}
141+
116142
visitCast(node: CastNode): Node {
117143
node.expression = this.visit(node.expression);
118144

0 commit comments

Comments
 (0)