diff --git a/node_modules b/node_modules new file mode 120000 index 00000000..bccda6c5 --- /dev/null +++ b/node_modules @@ -0,0 +1 @@ +/Users/csmith/git/ChadScript/node_modules \ No newline at end of file diff --git a/src/codegen/expressions/arrow-functions.ts b/src/codegen/expressions/arrow-functions.ts index 7bf909c1..91824540 100644 --- a/src/codegen/expressions/arrow-functions.ts +++ b/src/codegen/expressions/arrow-functions.ts @@ -18,6 +18,10 @@ import { BlockStatement, FunctionParameter, SourceLocation, + BinaryNode, + ReturnStatement, + IfStatement, + Expression, } from "../../ast/types.js"; import { ClosureAnalyzer, @@ -272,7 +276,7 @@ export class ArrowFunctionExpressionGenerator extends BaseGenerator { return "string"; } if (bodyTyped.type === "binary") { - const binExpr = body as { type: string; op: string; left: unknown; right: unknown }; + const binExpr = body as BinaryNode; if (binExpr.op === "+") { const leftType = this.inferReturnTypeFromBody(binExpr.left as ArrowFunctionNode["body"]); const rightType = this.inferReturnTypeFromBody(binExpr.right as ArrowFunctionNode["body"]); @@ -310,42 +314,38 @@ export class ArrowFunctionExpressionGenerator extends BaseGenerator { } } if (bodyTyped.type === "block") { - const blockTyped = body as { type: string; statements: unknown[] }; + const blockTyped = body as BlockStatement; const blockStatements = blockTyped.statements; if (blockStatements) { for (let i = 0; i < blockStatements.length; i++) { const stmt = blockStatements[i]; - const stmtTyped = stmt as { type: string; value: unknown }; - if (stmtTyped.type === "return" && stmtTyped.value) { - const returnValue = stmtTyped.value; - const returnValueTyped = returnValue as { type: string }; - if (returnValueTyped.type === "object") { - return "object"; - } - if ( - returnValueTyped.type === "string" || - returnValueTyped.type === "string_literal" || - returnValueTyped.type === "template_literal" - ) { - return "string"; + const stmtBase = stmt as { type: string }; + if (stmtBase.type === "return") { + const stmtTyped = stmt as ReturnStatement; + if (stmtTyped.value) { + const returnValueTyped = stmtTyped.value as { type: string }; + if (returnValueTyped.type === "object") { + return "object"; + } + if ( + returnValueTyped.type === "string" || + returnValueTyped.type === "string_literal" || + returnValueTyped.type === "template_literal" + ) { + return "string"; + } } } - if (stmtTyped.type === "if") { - const ifStmt = stmt as { - type: string; - condition: unknown; - thenBlock: unknown; - elseBlock: unknown; - }; - const thenBlock = ifStmt.thenBlock as { type: string; statements: unknown[] }; + if (stmtBase.type === "if") { + const ifStmt = stmt as IfStatement; + const thenBlock = ifStmt.thenBlock; const thenBlockStatements = thenBlock ? thenBlock.statements : null; if (thenBlockStatements) { for (let j = 0; j < thenBlockStatements.length; j++) { const innerStmt = thenBlockStatements[j]; - const innerStmtTyped = innerStmt as { type: string; value: unknown }; + const innerStmtTyped = innerStmt as ReturnStatement; if (innerStmtTyped.type === "return" && innerStmtTyped.value) { - const innerReturnValue = innerStmtTyped.value; - const innerReturnValueTyped = innerReturnValue as { type: string }; + const innerReturnValueTyped = innerStmtTyped.value as { type: string }; if (innerReturnValueTyped.type === "object") { return "object"; } diff --git a/src/codegen/expressions/conditionals.ts b/src/codegen/expressions/conditionals.ts index 4f99a2d5..025d2f13 100644 --- a/src/codegen/expressions/conditionals.ts +++ b/src/codegen/expressions/conditionals.ts @@ -9,7 +9,7 @@ * - Phi node to merge results */ -import { ConditionalExpressionNode, Expression } from "../../ast/types.js"; +import { ConditionalExpressionNode, Expression, ArrayNode } from "../../ast/types.js"; import { IGeneratorContext } from "../infrastructure/generator-context.js"; export class ConditionalExpressionGenerator { @@ -69,7 +69,7 @@ export class ConditionalExpressionGenerator { this.emit(`${falseLabel}:`); const savedExpectedType = this.ctx.getExpectedArrayElementType(); - const falseExprTyped = expr.alternate as { type: string; elements?: Expression[] }; + const falseExprTyped = expr.alternate as ArrayNode; if ( falseExprTyped.type === "array" && (!falseExprTyped.elements || falseExprTyped.elements.length === 0) diff --git a/src/codegen/expressions/literals.ts b/src/codegen/expressions/literals.ts index 78d963e2..14e0dcaf 100644 --- a/src/codegen/expressions/literals.ts +++ b/src/codegen/expressions/literals.ts @@ -227,8 +227,8 @@ export class LiteralExpressionGenerator { throw new Error("new RegExp() requires at least 1 argument"); } - const patternArg = args[0] as { type: string; value?: string }; - const flagsArg = args.length > 1 ? (args[1] as { type: string; value?: string }) : null; + const patternArg = args[0] as StringNode; + const flagsArg = args.length > 1 ? (args[1] as StringNode) : null; let flags = ""; if (flagsArg && flagsArg.type === "string" && flagsArg.value !== undefined) { diff --git a/src/codegen/expressions/method-calls/class-dispatch.ts b/src/codegen/expressions/method-calls/class-dispatch.ts index efbd082e..56ee3794 100644 --- a/src/codegen/expressions/method-calls/class-dispatch.ts +++ b/src/codegen/expressions/method-calls/class-dispatch.ts @@ -8,6 +8,7 @@ import { InterfaceField, FunctionNode, FunctionParameter, + IndexAccessNode, } from "../../../ast/types.js"; import type { MethodCallGeneratorContext } from "../method-calls.js"; @@ -672,7 +673,7 @@ export function handleClassMethods( } } } else if (exprObjBase.type === "index_access") { - const indexAccess = expr.object as { type: string; object: Expression; index: Expression }; + const indexAccess = expr.object as IndexAccessNode; const baseExpr = indexAccess.object; const baseExprBase = baseExpr as ExprBase; if (baseExprBase.type === "member_access") { diff --git a/src/codegen/expressions/method-calls/console.ts b/src/codegen/expressions/method-calls/console.ts index e408694d..b713d7cc 100644 --- a/src/codegen/expressions/method-calls/console.ts +++ b/src/codegen/expressions/method-calls/console.ts @@ -1,4 +1,4 @@ -import { Expression, MethodCallNode } from "../../../ast/types.js"; +import { Expression, MethodCallNode, StringNode } from "../../../ast/types.js"; import type { MethodCallGeneratorContext } from "../method-calls.js"; function emitPrint( @@ -163,16 +163,15 @@ function emitSingleArg( arg: Expression, params: string[], ): void { - const argTyped = arg as { type: string; value: string | number }; - - if (argTyped.type === "string") { - const strValue = argTyped.value as string; + if (arg.type === "string") { + const strNode = arg as StringNode; + const strValue = strNode.value; const strConstPtr = ctx.stringGen.doCreateStringConstant(strValue); emitPrintStrNoNl(ctx, useStderr, strConstPtr); return; } - if (argTyped.type === "number") { + if (arg.type === "number") { const argValue = ctx.generateExpression(arg, params); emitPrintNumNoNl(ctx, useStderr, argValue); return; diff --git a/src/codegen/expressions/method-calls/string-methods.ts b/src/codegen/expressions/method-calls/string-methods.ts index 81e376fd..8b8e51c2 100644 --- a/src/codegen/expressions/method-calls/string-methods.ts +++ b/src/codegen/expressions/method-calls/string-methods.ts @@ -546,7 +546,7 @@ export function handleReplace( const replaceArg = expr.args[1]; if (searchArg.type === "regex") { - const regexNode = searchArg as { type: string; pattern: string; flags: string }; + const regexNode = searchArg as RegexNode; const isGlobal = regexNode.flags.indexOf("g") !== -1; const searchStr = ctx.stringGen.doGenerateGlobalString(regexNode.pattern); const replaceStr = ctx.generateExpression(replaceArg, params); diff --git a/src/codegen/expressions/operators/binary.ts b/src/codegen/expressions/operators/binary.ts index b9fe834b..590f0836 100644 --- a/src/codegen/expressions/operators/binary.ts +++ b/src/codegen/expressions/operators/binary.ts @@ -1,4 +1,4 @@ -import { Expression, SourceLocation } from "../../../ast/types.js"; +import { Expression, SourceLocation, NumberNode } from "../../../ast/types.js"; import type { IStringGenerator } from "../../infrastructure/generator-context.js"; interface ControlFlowGeneratorLike { @@ -142,7 +142,7 @@ export class BinaryExpressionGenerator { } private isKnownInteger(expr: Expression): boolean { - const exprTyped = expr as { type: string; value?: number }; + const exprTyped = expr as NumberNode; if (exprTyped.type === "number" && typeof exprTyped.value === "number") { return Number.isInteger(exprTyped.value); } diff --git a/src/codegen/expressions/operators/unary.ts b/src/codegen/expressions/operators/unary.ts index 6f2093b3..d20aaa0c 100644 --- a/src/codegen/expressions/operators/unary.ts +++ b/src/codegen/expressions/operators/unary.ts @@ -71,7 +71,7 @@ export class UnaryExpressionGenerator { if (operand.type !== "variable") { return this.ctx.emitError("Post-increment/decrement requires a variable operand"); } - const operandVar = operand as { type: string; name: string }; + const operandVar = operand as VariableNode; const varName = operandVar.name; const allocaReg = this.ctx.getVariableAlloca(varName); if (!allocaReg) { @@ -115,7 +115,7 @@ export class UnaryExpressionGenerator { if (operand.type !== "variable") { return this.ctx.emitError("Pre-increment/decrement requires a variable operand"); } - const operandVarPre = operand as { type: string; name: string }; + const operandVarPre = operand as VariableNode; const varName = operandVarPre.name; const allocaReg = this.ctx.getVariableAlloca(varName); if (!allocaReg) { diff --git a/src/codegen/expressions/templates.ts b/src/codegen/expressions/templates.ts index d6593625..528e1441 100644 --- a/src/codegen/expressions/templates.ts +++ b/src/codegen/expressions/templates.ts @@ -14,7 +14,7 @@ * - With interpolation: `Hello ${name}` -> concatenate "Hello " with name value */ -import { Expression, TemplateLiteralNode } from "../../ast/types.js"; +import { Expression, TemplateLiteralNode, StringNode } from "../../ast/types.js"; import { IGeneratorContext } from "../infrastructure/generator-context.js"; import { createStringConstant, @@ -59,7 +59,7 @@ export class TemplateLiteralGenerator { } if (expr.parts.length === 1) { - const firstPart = expr.parts[0] as { type: string; value?: string }; + const firstPart = expr.parts[0] as StringNode; if (firstPart.type === "string") { return this.ctx.stringGen.doCreateStringConstant(firstPart.value || ""); } @@ -72,7 +72,7 @@ export class TemplateLiteralGenerator { const part = expr.parts[_tpi]; let partValue: string; - const partAsObj = part as { type: string; value?: string }; + const partAsObj = part as StringNode; if (partAsObj.type === "string") { partValue = this.ctx.stringGen.doCreateStringConstant(partAsObj.value || ""); } else { diff --git a/src/codegen/infrastructure/closure-analyzer.ts b/src/codegen/infrastructure/closure-analyzer.ts index 10fff85c..0dd2c09f 100644 --- a/src/codegen/infrastructure/closure-analyzer.ts +++ b/src/codegen/infrastructure/closure-analyzer.ts @@ -295,20 +295,15 @@ export class ClosureAnalyzer { this.addReferencedVar(s.name); this.walkExpression(s.value); } else if (stmtType === "expression_statement") { - const s = stmt as { type: string; expression: Expression }; + const s = stmt as ExprStmtNode; this.walkExpression(s.expression); } else if (stmtType === "return") { - const s = stmt as { type: string; value: Expression | null }; + const s = stmt as ReturnNode; if (s.value) { this.walkExpression(s.value); } } else if (stmtType === "if") { - const s = stmt as { - type: string; - condition: Expression; - consequent: BlockStatement; - alternate: Statement | BlockStatement | null; - }; + const s = stmt as IfNode; this.walkExpression(s.condition); if (s.consequent) { this.walkBlock(s.consequent); @@ -322,17 +317,11 @@ export class ClosureAnalyzer { } } } else if (stmtType === "while") { - const s = stmt as { type: string; condition: Expression; body: BlockStatement }; + const s = stmt as WhileNode; this.walkExpression(s.condition); this.walkBlock(s.body); } else if (stmtType === "for") { - const s = stmt as { - type: string; - init: Statement | null; - condition: Expression | null; - update: Statement | Expression | null; - body: BlockStatement; - }; + const s = stmt as ForNode; if (s.init) this.walkStatement(s.init); if (s.condition) this.walkExpression(s.condition); if (s.update) { @@ -345,25 +334,12 @@ export class ClosureAnalyzer { } this.walkBlock(s.body); } else if (stmtType === "for_of") { - const s = stmt as { - type: string; - variableKind: string; - variableName: string; - destructuredNames: string[] | null; - iterable: Expression; - body: BlockStatement; - }; + const s = stmt as ForOfNode; this.declaredVars.add(s.variableName); this.walkExpression(s.iterable); this.walkBlock(s.body); } else if (stmtType === "try") { - const tryStmt = stmt as { - type: string; - tryBlock: BlockStatement; - catchParam: string | null; - catchBody: BlockStatement | null; - finallyBlock: BlockStatement | null; - }; + const tryStmt = stmt as TryNode; this.walkBlock(tryStmt.tryBlock); if (tryStmt.catchBody !== null) { this.walkBlock(tryStmt.catchBody); @@ -372,19 +348,19 @@ export class ClosureAnalyzer { this.walkBlock(tryStmt.finallyBlock); } } else if (stmtType === "method_call") { - const s = stmt as { type: string; object: Expression; method: string; args: Expression[] }; + const s = stmt as MethodCallExpr; this.walkExpression(s.object); for (let _ai = 0; _ai < s.args.length; _ai++) { this.walkExpression(s.args[_ai]); } } else if (stmtType === "call") { - const s = stmt as { type: string; name: string; args: Expression[] }; + const s = stmt as CallExpr; this.addReferencedVar(s.name); for (let _ai = 0; _ai < s.args.length; _ai++) { this.walkExpression(s.args[_ai]); } } else if (stmtType === "await") { - const s = stmt as { type: string; argument: Expression }; + const s = stmt as AwaitExpr; this.walkExpression(s.argument); } } @@ -394,47 +370,47 @@ export class ClosureAnalyzer { const exprType = exprTyped.type; if (exprType === "variable") { - const e = expr as { type: string; name: string }; + const e = expr as VariableExpr; this.addReferencedVar(e.name); } else if (exprType === "binary") { - const e = expr as { type: string; op: string; left: Expression; right: Expression }; + const e = expr as BinaryExpr; this.walkExpression(e.left); this.walkExpression(e.right); } else if (exprType === "unary") { - const e = expr as { type: string; op: string; operand: Expression }; + const e = expr as UnaryExpr; this.walkExpression(e.operand); } else if (exprType === "call") { - const e = expr as { type: string; name: string; args: Expression[] }; + const e = expr as CallExpr; this.addReferencedVar(e.name); for (let _ai = 0; _ai < e.args.length; _ai++) { this.walkExpression(e.args[_ai]); } } else if (exprType === "method_call") { - const e = expr as { type: string; object: Expression; method: string; args: Expression[] }; + const e = expr as MethodCallExpr; this.walkExpression(e.object); for (let _ai2 = 0; _ai2 < e.args.length; _ai2++) { this.walkExpression(e.args[_ai2]); } } else if (exprType === "member_access") { - const e = expr as { type: string; object: Expression; property: string }; + const e = expr as MemberAccessExpr; this.walkExpression(e.object); } else if (exprType === "index_access") { - const e = expr as { type: string; object: Expression; index: Expression }; + const e = expr as IndexAccessExpr; this.walkExpression(e.object); this.walkExpression(e.index); } else if (exprType === "array") { - const e = expr as { type: string; elements: Expression[] }; + const e = expr as ArrayExpr; for (let _eli = 0; _eli < e.elements.length; _eli++) { this.walkExpression(e.elements[_eli]); } } else if (exprType === "object") { - const e = expr as { type: string; properties: ObjectProperty[] }; + const e = expr as ObjectExpr; for (let i = 0; i < e.properties.length; i++) { const prop = e.properties[i] as ObjectProperty; this.walkExpression(prop.value); } } else if (exprType === "template_literal") { - const e = expr as { type: string; parts: (string | Expression)[] }; + const e = expr as TemplateLiteralExpr; for (let _pti = 0; _pti < e.parts.length; _pti++) { const part = e.parts[_pti]; const partAsObj = part as { type: string }; @@ -443,7 +419,7 @@ export class ClosureAnalyzer { } } } else if (exprType === "arrow_function") { - const e = expr as { type: string; params: string[]; body: Expression | BlockStatement }; + const e = expr as ArrowFunctionExpr; const savedDeclaredVars = this.declaredVars; this.declaredVars = new Set(); for (let _ppi = 0; _ppi < e.params.length; _ppi++) { @@ -457,20 +433,15 @@ export class ClosureAnalyzer { } this.declaredVars = savedDeclaredVars; } else if (exprType === "conditional") { - const e = expr as { - type: string; - condition: Expression; - consequent: Expression; - alternate: Expression; - }; + const e = expr as ConditionalExpr; this.walkExpression(e.condition); this.walkExpression(e.consequent); this.walkExpression(e.alternate); } else if (exprType === "await") { - const e = expr as { type: string; argument: Expression }; + const e = expr as AwaitExpr; this.walkExpression(e.argument); } else if (exprType === "new") { - const e = expr as { type: string; className: string; args: Expression[] }; + const e = expr as NewExpr; for (let _nai = 0; _nai < e.args.length; _nai++) { this.walkExpression(e.args[_nai]); } diff --git a/src/codegen/infrastructure/type-inference.ts b/src/codegen/infrastructure/type-inference.ts index 155dcab3..552983ab 100644 --- a/src/codegen/infrastructure/type-inference.ts +++ b/src/codegen/infrastructure/type-inference.ts @@ -1759,7 +1759,7 @@ export class TypeInference { if (e.type === "method_call" && expr.method === "parse" && expr.typeParameter) { const objBase = expr.object as ExprBase; if (objBase && objBase.type === "variable") { - const varNode = expr.object as { type: string; name: string }; + const varNode = expr.object as VariableNode; if (varNode.name === "JSON") { const tp = expr.typeParameter; if (tp !== "number[]" && tp !== "string" && tp !== "number" && tp !== "boolean") { @@ -1817,7 +1817,7 @@ export class TypeInference { let e = expr as ExprBase; let indexExpr: Expression = expr; if (e.type === "type_assertion") { - const assertion = expr as { type: string; expression: Expression; assertedType: string }; + const assertion = expr as TypeAssertionNode; if (assertion.expression) { indexExpr = assertion.expression; e = assertion.expression as ExprBase; diff --git a/src/codegen/infrastructure/variable-allocator.ts b/src/codegen/infrastructure/variable-allocator.ts index a7781feb..91018f9f 100644 --- a/src/codegen/infrastructure/variable-allocator.ts +++ b/src/codegen/infrastructure/variable-allocator.ts @@ -2474,7 +2474,7 @@ export class VariableAllocator { if (idxObjBase.type !== "member_access") return null; - const memberAccess = indexExpr.object as { type: string; object: Expression; property: string }; + const memberAccess = indexExpr.object as MemberAccessNode; if (!memberAccess || !memberAccess.object || !memberAccess.property) return null; const propertyName = memberAccess.property; @@ -2531,15 +2531,15 @@ export class VariableAllocator { } private resolveNestedMemberArrayType(memberAccess: MemberAccessNode): string | null { - const ma = memberAccess as { type: string; object: Expression; property: string }; - const objectType = this.resolveMemberAccessObjectType(ma.object); + const objectType = this.resolveMemberAccessObjectType(memberAccess.object); if (!objectType) return null; - const classFieldInfo = this.ctx.classGenGetFieldInfo(objectType, ma.property); + const classFieldInfo = this.ctx.classGenGetFieldInfo(objectType, memberAccess.property); const classFieldTsType = classFieldInfo ? (classFieldInfo as { index: number; type: string; tsType: string }).tsType : null; - const fieldType = this.getInterfaceFieldTypeByName(objectType, ma.property) || classFieldTsType; + const fieldType = + this.getInterfaceFieldTypeByName(objectType, memberAccess.property) || classFieldTsType; if (!fieldType) return null; const arrayParsed = parseArrayTypeString(fieldType); diff --git a/src/codegen/llvm-generator.ts b/src/codegen/llvm-generator.ts index f8ae5a4a..589c65f6 100644 --- a/src/codegen/llvm-generator.ts +++ b/src/codegen/llvm-generator.ts @@ -34,6 +34,9 @@ import { BinaryNode, SourceLocation, FunctionParameter, + ReturnStatement, + StringNode, + MemberAccessNode, } from "../ast/types.js"; import { BaseGenerator, SymbolKind, SymbolTable } from "./infrastructure/base-generator.js"; import { @@ -3290,7 +3293,7 @@ export class LLVMGenerator extends BaseGenerator implements IGeneratorContext { this.handleSimpleAssignmentWithFields(stmtName, stmtValue as Expression, params); } } else if (stmtType === "return") { - const stmt = stmtRaw as { type: string; value: Expression | null }; + const stmt = stmtRaw as ReturnStatement; if (!stmt.value) { // Return without value - use default based on return type if (this.currentFunctionReturnType === "void") { @@ -3324,7 +3327,7 @@ export class LLVMGenerator extends BaseGenerator implements IGeneratorContext { if (objLit.properties && objLit.properties.length > 0) { const firstProp = objLit.properties[0]; if (firstProp.key === "type" && firstProp.value) { - const propValue = firstProp.value as { type: string; value?: string }; + const propValue = firstProp.value as StringNode; if (propValue.type === "string" && propValue.value) { discriminantValue = propValue.value; } @@ -3751,7 +3754,7 @@ export class LLVMGenerator extends BaseGenerator implements IGeneratorContext { expr = body; } if (!expr || expr.type !== "method_call") return null; - const mc = expr as { type: string; object: Expression; method: string }; + const mc = expr as MethodCallNode; if ((mc.object as { type: string }).type !== "variable") return null; const varName = (mc.object as VariableNode).name; const className = this.symbolTable.getConcreteClass(varName); diff --git a/src/codegen/statements/control-flow.ts b/src/codegen/statements/control-flow.ts index b5b13181..11043468 100644 --- a/src/codegen/statements/control-flow.ts +++ b/src/codegen/statements/control-flow.ts @@ -18,6 +18,12 @@ import { SwitchCase, StringNode, TryStatement, + WhileStatement, + DoWhileStatement, + AssignmentStatement, + ThrowStatement, + ArrayNode, + ForStatement, } from "../../ast/types.js"; import { IGeneratorContext } from "../infrastructure/generator-context.js"; import { @@ -203,7 +209,7 @@ export class ControlFlowGenerator { throw new Error("Expected while statement"); } - const whileStmt = stmt as { type: string; condition: Expression; body: BlockStatement }; + const whileStmt = stmt as WhileStatement; // Generate unique labels const condLabel = this.nextLabel("while_cond"); @@ -245,7 +251,7 @@ export class ControlFlowGenerator { throw new Error("Expected do_while statement"); } - const doWhileStmt = stmt as { type: string; condition: Expression; body: BlockStatement }; + const doWhileStmt = stmt as DoWhileStatement; const bodyLabel = this.nextLabel("dowhile_body"); const condLabel = this.nextLabel("dowhile_cond"); @@ -314,7 +320,7 @@ export class ControlFlowGenerator { this.emit(`${allocaReg} = alloca double`); this.ctx.emitStore("double", dblValue, allocaReg); } else if (initBase.type === "assignment") { - const initAssign = forStmt.init as { type: string; name: string; value: Expression }; + const initAssign = forStmt.init as AssignmentStatement; let value = this.ctx.generateExpression(initAssign.value, params); const allocaReg = this.ctx.getVariableAlloca(initAssign.name); if (!allocaReg) { @@ -368,7 +374,7 @@ export class ControlFlowGenerator { // Update block this.ctx.emitLabel(updateLabel); if (forStmt.update) { - const updateTyped = forStmt.update as { type: string; name: string; value: Expression }; + const updateTyped = forStmt.update as AssignmentStatement; const updateType = updateTyped.type; if (updateType === "assignment") { const updateName = updateTyped.name; @@ -713,7 +719,7 @@ export class ControlFlowGenerator { } if (iterable.type === "member_access") { - const memberAccess = iterable as { type: string; object: Expression; property: string }; + const memberAccess = iterable as MemberAccessNode; const memberAccessObjBase = memberAccess.object as ExprBase; if (memberAccessObjBase.type === "variable") { const varName = (memberAccess.object as VariableNode).name; @@ -907,10 +913,9 @@ export class ControlFlowGenerator { private getChainedMemberAccessArrayInfo( memberAccess: MemberAccessNode, ): ObjectArrayMetadata | null { - const ma = memberAccess as { type: string; object: Expression; property: string }; - const propName = ma.property; + const propName = memberAccess.property; - const baseTypeName = this.resolveMemberAccessChainType(ma.object); + const baseTypeName = this.resolveMemberAccessChainType(memberAccess.object); if (!baseTypeName) { return null; } @@ -1404,7 +1409,7 @@ export class ControlFlowGenerator { throw new Error("Expected throw statement"); } - const throwStmt = stmt as { type: string; argument: Expression }; + const throwStmt = stmt as ThrowStatement; let msgVal: string = "null"; if (throwStmt.argument) { @@ -1549,7 +1554,7 @@ export class ControlFlowGenerator { this.ctx.emitLabel(evalRightLabel); const savedExpectedType = this.ctx.getExpectedArrayElementType(); - const rightTyped = right as { type: string; elements?: Expression[] }; + const rightTyped = right as ArrayNode; if (rightTyped.type === "array" && (!rightTyped.elements || rightTyped.elements.length === 0)) { if (savedExpectedType === null) { if (leftType === "%StringArray*") { @@ -1779,12 +1784,11 @@ export class ControlFlowGenerator { } if (!memberAccess || !literalValue) return null; - const ma = memberAccess as { type: string; object: Expression; property: string }; - if (ma.property !== "type") return null; - const maObjBase = ma.object as ExprBase; + if (memberAccess.property !== "type") return null; + const maObjBase = memberAccess.object as ExprBase; if (maObjBase.type !== "variable") return null; - const varName = (ma.object as VariableNode).name; + const varName = (memberAccess.object as VariableNode).name; const objMeta = this.ctx.symbolTable.getObjectMetadata(varName); if (!objMeta) return null; diff --git a/src/codegen/stdlib/child-process.ts b/src/codegen/stdlib/child-process.ts index f8ca1c4a..e54af8ca 100644 --- a/src/codegen/stdlib/child-process.ts +++ b/src/codegen/stdlib/child-process.ts @@ -16,7 +16,7 @@ export class ChildProcessGenerator { canHandle(expr: MethodCallNode): boolean { const exprObjBase = expr.object as ExprBase; if (exprObjBase.type !== "variable") return false; - const varNode = expr.object as { type: string; name: string }; + const varNode = expr.object as VariableNode; if (varNode.name !== "child_process" && varNode.name !== "cp") return false; const supported = ["execSync", "spawnSync", "exec", "spawn"]; return supported.indexOf(expr.method) !== -1; diff --git a/src/codegen/stdlib/console.ts b/src/codegen/stdlib/console.ts index 0aa1d839..d0247049 100644 --- a/src/codegen/stdlib/console.ts +++ b/src/codegen/stdlib/console.ts @@ -62,7 +62,7 @@ export class ConsoleGenerator { // Handle console.log(value) - print first argument // For simplicity, we only handle one argument at a time const arg = args[0]; - const argTyped = arg as { type: string; name: string }; + const argTyped = arg as VariableNode; const argValue = this.ctx.generateExpression(arg, params); const isString = this.ctx.isStringExpression(arg); diff --git a/src/codegen/stdlib/crypto.ts b/src/codegen/stdlib/crypto.ts index 92ae6d75..f0924534 100644 --- a/src/codegen/stdlib/crypto.ts +++ b/src/codegen/stdlib/crypto.ts @@ -1,4 +1,4 @@ -import { MethodCallNode } from "../../ast/types.js"; +import { MethodCallNode, VariableNode } from "../../ast/types.js"; interface ExprBase { type: string; @@ -12,7 +12,7 @@ export class CryptoGenerator { canHandle(expr: MethodCallNode): boolean { const exprObjBase = expr.object as ExprBase; if (exprObjBase.type !== "variable") return false; - const varNode = expr.object as { type: string; name: string }; + const varNode = expr.object as VariableNode; if (varNode.name !== "crypto") return false; const supported = [ "sha256", diff --git a/src/codegen/stdlib/date.ts b/src/codegen/stdlib/date.ts index ba4abee1..1bcaacad 100644 --- a/src/codegen/stdlib/date.ts +++ b/src/codegen/stdlib/date.ts @@ -1,5 +1,5 @@ // Date codegen — Date.now() (static) and Date instance methods (getTime, getFullYear, etc.) -import { MethodCallNode } from "../../ast/types.js"; +import { MethodCallNode, VariableNode } from "../../ast/types.js"; interface ExprBase { type: string; @@ -13,7 +13,7 @@ export class DateGenerator { canHandle(expr: MethodCallNode): boolean { const exprObjBase = expr.object as ExprBase; if (exprObjBase.type !== "variable") return false; - const varNode = expr.object as { type: string; name: string }; + const varNode = expr.object as VariableNode; if (varNode.name !== "Date") return false; return expr.method === "now"; } diff --git a/src/codegen/stdlib/fs.ts b/src/codegen/stdlib/fs.ts index 0590ddac..be1ac0a5 100644 --- a/src/codegen/stdlib/fs.ts +++ b/src/codegen/stdlib/fs.ts @@ -1,4 +1,4 @@ -import { MethodCallNode } from "../../ast/types.js"; +import { MethodCallNode, VariableNode } from "../../ast/types.js"; interface ExprBase { type: string; @@ -26,7 +26,7 @@ export class FilesystemGenerator { canHandle(expr: MethodCallNode): boolean { const exprObjBase = expr.object as ExprBase; if (exprObjBase.type !== "variable") return false; - const varNode = expr.object as { type: string; name: string }; + const varNode = expr.object as VariableNode; if (varNode.name !== "fs") return false; const supported = [ "readFileSync", diff --git a/src/codegen/stdlib/json.ts b/src/codegen/stdlib/json.ts index 7b757ac8..d9ed2b92 100644 --- a/src/codegen/stdlib/json.ts +++ b/src/codegen/stdlib/json.ts @@ -5,6 +5,9 @@ import { ArrayNode, TypeAssertionNode, VariableNode, + NumberNode, + IndexAccessNode, + MemberAccessNode, } from "../../ast/types.js"; import { stringifyObjectArrayLiteral, stringifyObjectArrayWithMeta } from "./json-array.js"; @@ -43,7 +46,7 @@ export class JsonGenerator { canHandle(expr: MethodCallNode): boolean { const exprObjBase = expr.object as ExprBase; if (exprObjBase.type !== "variable") return false; - const varNode = expr.object as { type: string; name: string }; + const varNode = expr.object as VariableNode; if (varNode.name !== "JSON") return false; return expr.method === "parse" || expr.method === "stringify"; } @@ -484,7 +487,7 @@ export class JsonGenerator { private getSpaces(expr: MethodCallNode): number { if (expr.args.length < 3) return 0; - const spaceArg = expr.args[2] as { type: string; value?: number }; + const spaceArg = expr.args[2] as NumberNode; if (spaceArg.type === "number" && typeof spaceArg.value === "number") { return spaceArg.value; } @@ -552,7 +555,7 @@ export class JsonGenerator { // getRawInterfaceType returns the element type for arrays, which would // cause stringifyInterface to treat the array pointer as a single object. if (arg.type === "variable") { - const varNode = arg as { type: string; name: string }; + const varNode = arg as VariableNode; const elementType = this.ctx.symbolTable.getObjectArrayElementType(varNode.name); if (elementType) { return this.stringifyObjectArray(arg, params, elementType, spaces); @@ -578,7 +581,7 @@ export class JsonGenerator { return this.stringifyNumber(arg, params); } if (arg.type === "variable") { - const varNode = arg as { type: string; name: string }; + const varNode = arg as VariableNode; if ( this.ctx.symbolTable.isNumber(varNode.name) || this.ctx.symbolTable.isBoolean(varNode.name) @@ -604,7 +607,7 @@ export class JsonGenerator { private resolveInterfaceType(arg: Expression): string | null { if (arg.type === "variable") { - const varNode = arg as { type: string; name: string }; + const varNode = arg as VariableNode; const fromSymbol = this.ctx.symbolTable.getInterfaceType(varNode.name) || this.ctx.symbolTable.getRawInterfaceType(varNode.name) || @@ -618,10 +621,10 @@ export class JsonGenerator { return null; } if (arg.type === "index_access") { - const indexAccess = arg as { type: string; object: Expression; index: Expression }; + const indexAccess = arg as IndexAccessNode; const objExpr = indexAccess.object; if (objExpr && objExpr.type === "variable") { - const varObj = objExpr as { type: string; name: string }; + const varObj = objExpr as VariableNode; const arrayName = varObj.name; if (arrayName) { const elemType = this.ctx.symbolTable.getRawInterfaceType(arrayName); @@ -633,7 +636,7 @@ export class JsonGenerator { } } if (arg.type === "member_access") { - const memberAccess = arg as { type: string; object: Expression; property: string }; + const memberAccess = arg as MemberAccessNode; const objType = this.resolveInterfaceType(memberAccess.object); if (objType && this.ctx.interfaceStructGenHasInterface(objType)) { const fieldCount = this.ctx.interfaceStructGenGetFieldCount(objType); diff --git a/src/codegen/stdlib/math.ts b/src/codegen/stdlib/math.ts index e672988c..7998faa8 100644 --- a/src/codegen/stdlib/math.ts +++ b/src/codegen/stdlib/math.ts @@ -1,4 +1,4 @@ -import { MethodCallNode } from "../../ast/types.js"; +import { MethodCallNode, VariableNode } from "../../ast/types.js"; interface ExprBase { type: string; @@ -29,7 +29,7 @@ export class MathGenerator { canHandle(expr: MethodCallNode): boolean { const exprObjBase = expr.object as ExprBase; if (exprObjBase.type !== "variable") return false; - const varNode = expr.object as { type: string; name: string }; + const varNode = expr.object as VariableNode; if (varNode.name !== "Math") return false; return this.getSupportedMethods().indexOf(expr.method) !== -1; } diff --git a/src/codegen/stdlib/path.ts b/src/codegen/stdlib/path.ts index 9dd06c24..7042fcc3 100644 --- a/src/codegen/stdlib/path.ts +++ b/src/codegen/stdlib/path.ts @@ -1,4 +1,4 @@ -import { MethodCallNode } from "../../ast/types.js"; +import { MethodCallNode, VariableNode } from "../../ast/types.js"; interface ExprBase { type: string; @@ -24,7 +24,7 @@ export class PathGenerator { canHandle(expr: MethodCallNode): boolean { const exprObjBase = expr.object as ExprBase; if (exprObjBase.type !== "variable") return false; - const varNode = expr.object as { type: string; name: string }; + const varNode = expr.object as VariableNode; if (varNode.name !== "path") return false; const supported = ["resolve", "dirname", "basename", "join", "extname", "isAbsolute"]; return supported.indexOf(expr.method) !== -1; diff --git a/src/codegen/stdlib/process.ts b/src/codegen/stdlib/process.ts index 2a39f157..5703edb1 100644 --- a/src/codegen/stdlib/process.ts +++ b/src/codegen/stdlib/process.ts @@ -1,4 +1,4 @@ -import { MethodCallNode } from "../../ast/types.js"; +import { MethodCallNode, VariableNode } from "../../ast/types.js"; interface ExprBase { type: string; @@ -25,7 +25,7 @@ export class ProcessGenerator { canHandle(expr: MethodCallNode): boolean { const exprObjBase = expr.object as ExprBase; if (exprObjBase.type !== "variable") return false; - const varNode = expr.object as { type: string; name: string }; + const varNode = expr.object as VariableNode; if (varNode.name !== "process") return false; return expr.method === "exit"; } diff --git a/src/codegen/stdlib/sqlite.ts b/src/codegen/stdlib/sqlite.ts index a77e4fd3..74d61982 100644 --- a/src/codegen/stdlib/sqlite.ts +++ b/src/codegen/stdlib/sqlite.ts @@ -1,4 +1,4 @@ -import { MethodCallNode, Expression, ArrayNode } from "../../ast/types.js"; +import { MethodCallNode, Expression, ArrayNode, VariableNode } from "../../ast/types.js"; interface ExprBase { type: string; @@ -12,7 +12,7 @@ export class SqliteGenerator { canHandle(expr: MethodCallNode): boolean { const exprObjBase = expr.object as ExprBase; if (exprObjBase.type !== "variable") return false; - const varNode = expr.object as { type: string; name: string }; + const varNode = expr.object as VariableNode; if (varNode.name !== "sqlite") return false; const supported = ["open", "exec", "get", "getRow", "all", "query", "close"]; return supported.indexOf(expr.method) !== -1; diff --git a/src/codegen/types/collections/array.ts b/src/codegen/types/collections/array.ts index 8db4bd5f..d29a577f 100644 --- a/src/codegen/types/collections/array.ts +++ b/src/codegen/types/collections/array.ts @@ -3,7 +3,7 @@ * New array methods should go in the appropriate submodule, NOT this file. */ -import { Expression, MethodCallNode } from "../../../ast/types.js"; +import { Expression, MethodCallNode, ArrayNode } from "../../../ast/types.js"; import { IGeneratorContext } from "../../infrastructure/generator-context.js"; // Array sub-modules @@ -39,7 +39,7 @@ export class ArrayGenerator { generateArrayLiteral(expr: Expression, params: string[]): string { // Check for spread elements — spread goes to combine.ts, regular to literal.ts - const arrExpr = expr as { type: string; elements: Expression[] }; + const arrExpr = expr as ArrayNode; if (arrExpr.elements) { for (let i = 0; i < arrExpr.elements.length; i++) { const el = arrExpr.elements[i] as { type: string }; diff --git a/src/codegen/types/collections/array/combine.ts b/src/codegen/types/collections/array/combine.ts index 15616296..4aca96dc 100644 --- a/src/codegen/types/collections/array/combine.ts +++ b/src/codegen/types/collections/array/combine.ts @@ -1,7 +1,12 @@ // Array combine operations: join, slice, concat, spread literals // Extracted from array.ts to reduce file size. Uses IGeneratorContext for IR emission. -import { Expression, MethodCallNode, VariableNode } from "../../../../ast/types.js"; +import { + Expression, + MethodCallNode, + VariableNode, + SpreadElementNode, +} from "../../../../ast/types.js"; import { IGeneratorContext, loadArrayMeta } from "./context.js"; interface ExprBase { @@ -63,7 +68,7 @@ export function generateArrayLiteralWithSpread( gen.emit(`${newTotal} = add i32 ${totalLen}, ${meta.length}`); totalLen = newTotal; } else if (el.type === "spread_element") { - const spreadArg = (arrExpr.elements[i] as { type: string; argument: Expression }).argument; + const spreadArg = (arrExpr.elements[i] as SpreadElementNode).argument; const arrPtr = gen.generateExpression(spreadArg, params); const meta = loadArrayMeta(gen, arrPtr); const newTotal = gen.nextTemp(); @@ -136,7 +141,7 @@ export function generateArrayLiteralWithSpread( gen.emitLabel(endLabel); } else if (el.type === "spread_element") { - const spreadArg = (arrExpr.elements[i] as { type: string; argument: Expression }).argument; + const spreadArg = (arrExpr.elements[i] as SpreadElementNode).argument; const srcArrPtr = gen.generateExpression(spreadArg, params); const srcMeta = loadArrayMeta(gen, srcArrPtr); @@ -223,7 +228,7 @@ function generateStringArrayLiteralWithSpread( gen.setVariableType(ptr, "%Array*"); spreadSources.push({ index: i, ptr: ptr }); } else if (el.type === "spread_element") { - const spreadArg = (arrExpr.elements[i] as { type: string; argument: Expression }).argument; + const spreadArg = (arrExpr.elements[i] as SpreadElementNode).argument; const ptr = gen.generateExpression(spreadArg, params); spreadSources.push({ index: i, ptr }); } else { diff --git a/src/codegen/types/collections/map.ts b/src/codegen/types/collections/map.ts index fa0cd0e4..70fadc02 100644 --- a/src/codegen/types/collections/map.ts +++ b/src/codegen/types/collections/map.ts @@ -2,7 +2,7 @@ // Uses structured IR builders where possible; raw emit() for inbounds GEP, // alloca, arithmetic, fcmp, ptrtoint, sext, zext, shl, and, memcpy. -import { Expression, MethodCallNode, MapEntry } from "../../../ast/types.js"; +import { Expression, MethodCallNode, MapEntry, MapNode } from "../../../ast/types.js"; import { IGeneratorContext } from "../../infrastructure/generator-context.js"; // ============================================ @@ -34,7 +34,7 @@ export class MapGenerator { } // sizeof(double) = 8 bytes generateMapLiteral(expr: Expression, params: string[]): string { - const mapExpr = expr as { type: string; entries: MapEntry[] }; + const mapExpr = expr as MapNode; if (mapExpr.type !== "map") { throw new Error("Expected map literal"); } diff --git a/src/codegen/types/collections/set.ts b/src/codegen/types/collections/set.ts index bf84fa77..20298401 100644 --- a/src/codegen/types/collections/set.ts +++ b/src/codegen/types/collections/set.ts @@ -1,4 +1,4 @@ -import { Expression, MethodCallNode } from "../../../ast/types.js"; +import { Expression, MethodCallNode, SetNode, NumberNode } from "../../../ast/types.js"; import { IGeneratorContext } from "../../infrastructure/generator-context.js"; // ============================================ @@ -29,7 +29,7 @@ export class SetGenerator { } // sizeof(double) = 8 bytes generateSetLiteral(expr: Expression, params: string[]): string { - const setExpr = expr as { type: string; values: Expression[] }; + const setExpr = expr as SetNode; if (setExpr.type !== "set") { throw new Error("Expected set literal"); } @@ -69,7 +69,7 @@ export class SetGenerator { const seen: Set = new Set(); let actualIndex = 0; for (let i = 0; i < setExpr.values.length; i++) { - const valueExprTyped = setExpr.values[i] as { type: string; value: number }; + const valueExprTyped = setExpr.values[i] as NumberNode; // For literal numbers, we can dedupe at compile time if (valueExprTyped.type === "number") {