From 26c4443dca62eb4de526cb6b1ae1dc5390938988 Mon Sep 17 00:00:00 2001 From: Cai Date: Thu, 25 Apr 2024 23:29:13 +0800 Subject: [PATCH] add if, while, do-while, for, break, continue statements --- parsers/cparser.pegjs | 37 +++---- src/ast/types.ts | 191 +++++++++++++++++++++++++++++++- src/interpreter/evaluators.ts | 113 ++++++++++++++++++- src/interpreter/instructions.ts | 96 ++++++++++++++-- src/interpreter/stack.ts | 24 +++- src/memory/memory.ts | 3 +- src/typing/env.ts | 14 +++ src/typing/main.ts | 117 ++++++++++++++++++- 8 files changed, 549 insertions(+), 46 deletions(-) diff --git a/parsers/cparser.pegjs b/parsers/cparser.pegjs index 142ca1c..c56f87a 100644 --- a/parsers/cparser.pegjs +++ b/parsers/cparser.pegjs @@ -1254,9 +1254,7 @@ Statement / CompoundStatement / ExpressionStatement / SelectionStatement - { throwNotImplemented("if/else and switch"); } / IterationStatement - { throwNotImplemented("while and for loops"); } / JumpStatement // (6.8.1) labeled-statement @@ -1299,17 +1297,14 @@ SelectionStatement )? { return makeNode("SelectionStatementIf", { - expr: a, - statement: b, - elseStatement: c + cond: a, + consequent: b, + alternative: c }); } / SWITCH LPAR a:Expression RPAR b:Statement { - return makeNode("SelectionStatementSwitch", { - expr: a, - statement: b - }); + throwNotImplemented("switch statements"); } // (6.8.5) iteration-statement @@ -1317,36 +1312,36 @@ IterationStatement = WHILE LPAR a:Expression RPAR b:Statement { return makeNode("IterationStatementWhile", { - expr: a, - statement: b + cond: a, + body: b }); } / DO a:Statement WHILE LPAR b:Expression RPAR SEMI { return makeNode("IterationStatementDoWhile", { - expr: b, - statement: a + cond: b, + body: a }); } / FOR LPAR a:Expression? SEMI b:Expression? SEMI c:Expression? RPAR d:Statement { return makeNode("IterationStatementFor", { - initExpr: a, + init: a, controlExpr: b, afterIterExpr: c, - statement: d + body: d }); } / FOR LPAR a:Declaration b:Expression? SEMI c:Expression? RPAR d:Statement { - return makeNode("IterationStatementForDeclaration", { - declaration: a, + return makeNode("IterationStatementFor", { + init: a, controlExpr: b, afterIterExpr: c, - statement: d + body: d }); } @@ -1355,11 +1350,9 @@ JumpStatement = GOTO a:Identifier SEMI { throwNotImplemented("goto"); } / CONTINUE SEMI - { throwNotImplemented("while loops"); } - // { return makeNode("JumpStatementContinue"); } + { return makeNode("JumpStatementContinue"); } / BREAK SEMI - { throwNotImplemented("while loops"); } - // { return makeNode("JumpStatementBreak"); } + { return makeNode("JumpStatementBreak"); } / RETURN a:Expression? SEMI { return makeNode("JumpStatementReturn", a); } diff --git a/src/ast/types.ts b/src/ast/types.ts index 5a8679f..2fe256f 100644 --- a/src/ast/types.ts +++ b/src/ast/types.ts @@ -5,6 +5,7 @@ import { ScalarType, TypeInfo, Void, + int, } from "../typing/types"; export interface PositionInfo { @@ -27,6 +28,12 @@ export type ASTNode = | InitDeclarator | CompoundStatement | JumpStatementReturn + | JumpStatementContinue + | JumpStatementBreak + | IterationStatementWhile + | IterationStatementDoWhile + | IterationStatementFor + | SelectionStatementIf | EmptyExpressionStatement | CommaOperator | CastExpressionNode @@ -60,6 +67,12 @@ export type TypedASTNode = | TypedefDeclaration | TypedCompoundStatement | TypedJumpStatementReturn + | JumpStatementBreak + | JumpStatementContinue + | TypedIterationStatementWhile + | TypedIterationStatementDoWhile + | TypedIterationStatementFor + | TypedSelectionStatementIf | EmptyExpressionStatement | TypedCommaOperator | TypedCastExpressionNode @@ -353,19 +366,34 @@ export type TypedBlockItem = | TypedDeclaration | TypedefDeclaration; -export type Statement = CompoundStatement | ExpressionStatement | JumpStatement; +export type Statement = + | CompoundStatement + | ExpressionStatement + | JumpStatement + | IterationStatement + | SelectionStatement; export type TypedStatement = | TypedCompoundStatement | TypedExpressionStatement - | TypedJumpStatement; + | TypedJumpStatement + | TypedIterationStatement + | TypedSelectionStatement; -export type JumpStatement = JumpStatementReturn; +export type JumpStatement = + | JumpStatementReturn + | JumpStatementContinue + | JumpStatementBreak; export const isJumpStatement = (i: BaseNode): i is JumpStatement => - isJumpStatementReturn(i); + isJumpStatementReturn(i) || + isJumpStatementContinue(i) || + isJumpStatementBreak(i); -export type TypedJumpStatement = TypedJumpStatementReturn; +export type TypedJumpStatement = + | TypedJumpStatementReturn + | JumpStatementContinue + | JumpStatementBreak; export interface JumpStatementReturn extends BaseNode { type: "JumpStatementReturn"; @@ -380,6 +408,141 @@ export interface TypedJumpStatementReturn extends BaseNode { value: TypedExpression | null; } +export interface JumpStatementContinue extends BaseNode { + type: "JumpStatementContinue"; +} + +export const isJumpStatementContinue = ( + i: BaseNode, +): i is JumpStatementContinue => i.type === "JumpStatementContinue"; + +export interface JumpStatementBreak extends BaseNode { + type: "JumpStatementBreak"; +} + +export const isJumpStatementBreak = (i: BaseNode): i is JumpStatementBreak => + i.type === "JumpStatementBreak"; + +export type IterationStatement = + | IterationStatementWhile + | IterationStatementDoWhile + | IterationStatementFor; + +export const isIterationStatement = (i: BaseNode): i is IterationStatement => + isIterationStatementWhile(i) || + isIterationStatementDoWhile(i) || + isIterationStatementFor(i); + +export type TypedIterationStatement = + | TypedIterationStatementWhile + | TypedIterationStatementDoWhile + | TypedIterationStatementFor; + +export const isTypedIterationStatement = ( + i: TypedStatement, +): i is TypedIterationStatement => + isTypedIterationStatementWhile(i) || + isTypedIterationStatementDoWhile(i) || + isTypedIterationStatementFor(i); + +export interface IterationStatementWhile extends BaseNode { + type: "IterationStatementWhile"; + cond: Expression; + body: Statement; +} + +export const isIterationStatementWhile = ( + i: BaseNode, +): i is IterationStatementWhile => i.type === "IterationStatementWhile"; + +export interface TypedIterationStatementWhile extends BaseNode { + type: "IterationStatementWhile"; + cond: TypedExpression; + body: TypedStatement; +} + +export const isTypedIterationStatementWhile = ( + i: TypedStatement, +): i is TypedIterationStatementWhile => i.type === "IterationStatementWhile"; + +export interface IterationStatementDoWhile extends BaseNode { + type: "IterationStatementDoWhile"; + cond: Expression; + body: Statement; +} + +export const isIterationStatementDoWhile = ( + i: BaseNode, +): i is IterationStatementDoWhile => i.type === "IterationStatementDoWhile"; + +export interface TypedIterationStatementDoWhile extends BaseNode { + type: "IterationStatementDoWhile"; + cond: TypedExpression; + body: TypedStatement; +} + +export const isTypedIterationStatementDoWhile = ( + i: TypedStatement, +): i is TypedIterationStatementDoWhile => + i.type === "IterationStatementDoWhile"; + +export interface IterationStatementFor extends BaseNode { + type: "IterationStatementFor"; + init: Expression | Declaration | null; + controlExpr: Expression | null; + afterIterExpr: Expression | null; + body: Statement; +} + +export const isIterationStatementFor = ( + i: BaseNode, +): i is IterationStatementFor => i.type === "IterationStatementFor"; + +export interface TypedIterationStatementFor extends BaseNode { + type: "IterationStatementFor"; + init: TypedExpression | null; + controlExpr: TypedExpression | null; + afterIterExpr: TypedExpression | null; + body: TypedStatement; +} + +export const isTypedIterationStatementFor = ( + i: TypedStatement, +): i is TypedIterationStatementFor => i.type === "IterationStatementFor"; + +export type SelectionStatement = SelectionStatementIf; + +export const isSelectionStatement = (i: BaseNode): i is SelectionStatement => + isSelectionStatementIf(i); + +export type TypedSelectionStatement = TypedSelectionStatementIf; + +export const isTypedSelectionStatement = ( + i: TypedStatement, +): i is TypedSelectionStatement => isTypedSelectionStatementIf(i); + +export interface SelectionStatementIf extends BaseNode { + type: "SelectionStatementIf"; + cond: Expression; + consequent: Statement; + alternative: Statement | null; +} + +export const isSelectionStatementIf = ( + i: BaseNode, +): i is SelectionStatementIf => i.type === "SelectionStatementIf"; + +export interface TypedSelectionStatementIf extends BaseNode { + type: "SelectionStatementIf"; + cond: TypedExpression; + consequent: TypedStatement; + alternative: TypedStatement | null; +} + +export const isTypedSelectionStatementIf = ( + i: TypedStatement, +): i is TypedSelectionStatementIf => i.type === "SelectionStatementIf"; + export interface ExpressionStatement extends BaseNode { type: "ExpressionStatement"; value: Expression | EmptyExpressionStatement; @@ -791,6 +954,24 @@ export const isTypedPrimaryExprConstant = ( expr: TypedExpression, ): expr is TypedPrimaryExprConstant => expr.type === "PrimaryExprConstant"; +export const TYPED_CONSTANT_ONE: TypedPrimaryExprConstant = { + type: "PrimaryExprConstant", + start: { offset: 0, line: 0, column: 0 }, + end: { offset: 1, line: 0, column: 1 }, + src: "1", + typeInfo: int(), + lvalue: false, + value: { + type: "IntegerConstant", + start: { offset: 0, line: 0, column: 0 }, + end: { offset: 1, line: 0, column: 1 }, + src: "1", + typeInfo: int(), + lvalue: false, + value: BigInt(1), + }, +}; + export interface PrimaryExprString extends BaseNode { type: "PrimaryExprString"; value: string[]; diff --git a/src/interpreter/evaluators.ts b/src/interpreter/evaluators.ts index 7a7b3e7..4758e71 100644 --- a/src/interpreter/evaluators.ts +++ b/src/interpreter/evaluators.ts @@ -49,6 +49,11 @@ import { isTypedUnaryExpressionNode, TypedUnaryExpressionSizeof, isEmptyExpressionStatement, + TypedSelectionStatementIf, + TypedIterationStatementDoWhile, + TypedIterationStatementWhile, + TypedIterationStatementFor, + TYPED_CONSTANT_ONE, } from "../ast/types"; import { ArithmeticConversionInstruction, @@ -57,24 +62,33 @@ import { BranchInstruction, CallInstruction, CastInstruction, + ForInstruction, Instruction, InstructionType, PushInstruction, UnaryOpInstruction, + WhileInstruction, arithmeticConversionInstruction, arraySubscriptInstruction, assignInstruction, binaryOpInstruction, branchInstruction, + breakMarkInstruction, callInstruction, castInstruction, + continueMarkInstruction, exitBlockInstruction, + forInstruction, + isBreakMarkInstruction, + isContinueMarkInstruction, + isExitBlockInstruction, isMarkInstruction, markInstruction, popInstruction, pushInstruction, returnInstruction, unaryOpInstruction, + whileInstruction, } from "./instructions"; import { Type, isFunction } from "../typing/types"; import { Runtime } from "./runtime"; @@ -90,6 +104,7 @@ import { RuntimeStack } from "./stack"; import { SHRT_SIZE } from "../constants"; import { checkSimpleAssignmentConstraint, getMember } from "../typing/utils"; import { NO_EFFECTIVE_TYPE } from "./effectiveTypeTable"; +import { AgendaItem } from "./agenda"; export const ASTNodeEvaluator: { [NodeType in TypedASTNode["type"]]: ( @@ -181,6 +196,12 @@ export const ASTNodeEvaluator: { rt.agenda.push(returnInstruction()); if (expr) rt.agenda.push(expr); }, + JumpStatementBreak: (rt: Runtime) => { + jumpTill(rt, isBreakMarkInstruction); + }, + JumpStatementContinue: (rt: Runtime) => { + jumpTill(rt, isContinueMarkInstruction); + }, ExpressionStatement: (rt: Runtime, { value }: TypedExpressionStatement) => { if (isEmptyExpressionStatement(value)) return; if (!isVoid(value.typeInfo)) rt.agenda.push(popInstruction()); @@ -469,6 +490,44 @@ export const ASTNodeEvaluator: { if (evaluateAsLvalue) rt.agenda.pushAsLvalue(expr); else rt.agenda.push(expr); }, + SelectionStatementIf: ( + rt: Runtime, + { cond, consequent, alternative }: TypedSelectionStatementIf, + ) => { + rt.agenda.push(branchInstruction(consequent, alternative)); + rt.agenda.push(cond); + }, + IterationStatementDoWhile: ( + rt: Runtime, + { cond, body }: TypedIterationStatementDoWhile, + ) => { + rt.agenda.push(breakMarkInstruction()); + rt.agenda.push(whileInstruction(cond, body)); + rt.agenda.push(cond); + rt.agenda.push(continueMarkInstruction()); + rt.agenda.push(body); + }, + IterationStatementWhile: ( + rt: Runtime, + { cond, body }: TypedIterationStatementWhile, + ) => { + rt.agenda.push(breakMarkInstruction()); + rt.agenda.push(whileInstruction(cond, body)); + rt.agenda.push(cond); + }, + IterationStatementFor: ( + rt: Runtime, + { init, controlExpr, afterIterExpr, body }: TypedIterationStatementFor, + ) => { + rt.agenda.push(breakMarkInstruction()); + controlExpr = controlExpr === null ? TYPED_CONSTANT_ONE : controlExpr; + rt.agenda.push(forInstruction(controlExpr, body, afterIterExpr)); + rt.agenda.push(controlExpr); + if (init !== null) { + rt.agenda.push(popInstruction()); + rt.agenda.push(init); + } + }, }; export const instructionEvaluator: { @@ -851,8 +910,7 @@ export const instructionEvaluator: { } }, [InstructionType.RETURN]: (rt: Runtime) => { - while (!isMarkInstruction(rt.agenda.peek())) rt.agenda.pop(); - rt.agenda.pop(); + jumpTill(rt, isMarkInstruction) const block = rt.symbolTable.exitBlock(); Object.values(block).forEach((addr) => { const t = rt.effectiveTypeTable.get(addr); @@ -875,11 +933,52 @@ export const instructionEvaluator: { rt.config.endianness, ); if (n === BigInt(0)) { - rt.agenda.push(exprIfFalse); + if (exprIfFalse !== null) rt.agenda.push(exprIfFalse); } else { rt.agenda.push(exprIfTrue); } }, + [InstructionType.WHILE]: (rt: Runtime, { cond, body }: WhileInstruction) => { + const o = rt.stash.pop(); + if (!(isTemporaryObject(o) && isScalarType(o.typeInfo))) + throw new Error("condition should be of scalar type"); + const n = bytesToBigint( + o.bytes, + isSigned(o.typeInfo), + rt.config.endianness, + ); + if (n !== BigInt(0)) { + rt.agenda.push(whileInstruction(cond, body)); + rt.agenda.push(cond); + rt.agenda.push(continueMarkInstruction()); + rt.agenda.push(body); + } + }, + [InstructionType.FOR]: ( + rt: Runtime, + { cond, body, afterIter }: ForInstruction, + ) => { + const o = rt.stash.pop(); + if (!(isTemporaryObject(o) && isScalarType(o.typeInfo))) + throw new Error("condition should be of scalar type"); + const n = bytesToBigint( + o.bytes, + isSigned(o.typeInfo), + rt.config.endianness, + ); + if (n !== BigInt(0)) { + rt.agenda.push(forInstruction(cond, body, afterIter)); + rt.agenda.push(cond); + if (afterIter) { + rt.agenda.push(popInstruction()) + rt.agenda.push(afterIter); + } + rt.agenda.push(continueMarkInstruction()); + rt.agenda.push(body); + } + }, + [InstructionType.BREAK_MARK]: () => {}, + [InstructionType.CONTINUE_MARK]: () => {}, [InstructionType.ARITHMETIC_CONVERSION]: ( rt: Runtime, { typeInfo }: ArithmeticConversionInstruction, @@ -1101,3 +1200,11 @@ const applyImplicitConversions = (t: TypeInfo): TypeInfo => { } return t; }; + +const jumpTill = (rt: Runtime, pred: (i: AgendaItem) => boolean): void => { + while (!pred(rt.agenda.peek())) { + const t = rt.agenda.pop(); + if (isExitBlockInstruction(t)) instructionEvaluator[t.type](rt, t); + } + rt.agenda.pop(); +} \ No newline at end of file diff --git a/src/interpreter/instructions.ts b/src/interpreter/instructions.ts index 8863f77..108b88b 100644 --- a/src/interpreter/instructions.ts +++ b/src/interpreter/instructions.ts @@ -1,4 +1,9 @@ -import { BinaryOperator, TypedExpression, UnaryOperator } from "../ast/types"; +import { + BinaryOperator, + TypedExpression, + TypedStatement, + UnaryOperator, +} from "../ast/types"; import { ArithmeticType, ScalarType, Void } from "../typing/types"; import { AgendaItem, isInstruction } from "./agenda"; import { StashItem } from "./stash"; @@ -17,7 +22,11 @@ export enum InstructionType { ARITHMETIC_CONVERSION = "ArithmeticConversion", CAST = "Cast", ARRAY_SUBSCRIPT = "ArraySubscript", - EXIT_BLOCK ="ExitBlock" + EXIT_BLOCK = "ExitBlock", + WHILE = "While", + FOR = "For", + BREAK_MARK = "BreakMark", + CONTINUE_MARK = "ContinueMark", } export interface BaseInstruction { @@ -80,6 +89,34 @@ export const markInstruction = (): MarkInstruction => ({ type: InstructionType.MARK, }); +export interface BreakMarkInstruction extends BaseInstruction { + type: InstructionType.BREAK_MARK; +} + +export const breakMarkInstruction = (): BreakMarkInstruction => ({ + type: InstructionType.BREAK_MARK, +}); + +export interface ContinueMarkInstruction extends BaseInstruction { + type: InstructionType.CONTINUE_MARK; +} + +export const continueMarkInstruction = (): ContinueMarkInstruction => ({ + type: InstructionType.CONTINUE_MARK, +}); + +export function isBreakMarkInstruction( + i: AgendaItem, +): i is BreakMarkInstruction { + return isInstruction(i) && i.type == InstructionType.BREAK_MARK; +} + +export function isContinueMarkInstruction( + i: AgendaItem, +): i is ContinueMarkInstruction { + return isInstruction(i) && i.type == InstructionType.CONTINUE_MARK; +} + export function isMarkInstruction(i: AgendaItem): i is MarkInstruction { return isInstruction(i) && i.type == InstructionType.MARK; } @@ -118,13 +155,13 @@ export const isReturnInstruction = (i: AgendaItem): i is ReturnInstruction => export interface BranchInstruction extends BaseInstruction { type: InstructionType.BRANCH; - exprIfTrue: TypedExpression; - exprIfFalse: TypedExpression; + exprIfTrue: TypedExpression | TypedStatement; + exprIfFalse: TypedExpression | TypedStatement | null; } export const branchInstruction = ( - exprIfTrue: TypedExpression, - exprIfFalse: TypedExpression, + exprIfTrue: TypedExpression | TypedStatement, + exprIfFalse: TypedExpression | TypedStatement | null, ): BranchInstruction => ({ type: InstructionType.BRANCH, exprIfTrue, @@ -168,7 +205,48 @@ export interface ExitBlockInstruction extends BaseInstruction { type: InstructionType.EXIT_BLOCK; } -export const exitBlockInstruction = () :ExitBlockInstruction => ({type:InstructionType.EXIT_BLOCK}); +export const exitBlockInstruction = (): ExitBlockInstruction => ({ + type: InstructionType.EXIT_BLOCK, +}); + +export function isExitBlockInstruction( + i: AgendaItem, +): i is ExitBlockInstruction { + return isInstruction(i) && i.type == InstructionType.EXIT_BLOCK; +} + +export interface WhileInstruction extends BaseInstruction { + type: InstructionType.WHILE; + cond: TypedExpression; + body: TypedStatement; +} + +export const whileInstruction = ( + cond: TypedExpression, + body: TypedStatement, +): WhileInstruction => ({ + type: InstructionType.WHILE, + cond, + body, +}); + +export interface ForInstruction extends BaseInstruction { + type: InstructionType.FOR; + cond: TypedExpression; + body: TypedStatement; + afterIter: TypedExpression | null; +} + +export const forInstruction = ( + cond: TypedExpression, + body: TypedStatement, + afterIter: TypedExpression | null, +): ForInstruction => ({ + type: InstructionType.FOR, + cond, + body, + afterIter, +}); export type Instruction = | UnaryOpInstruction @@ -185,3 +263,7 @@ export type Instruction = | CastInstruction | ArraySubscriptInstruction | ExitBlockInstruction + | WhileInstruction + | ForInstruction + | BreakMarkInstruction + | ContinueMarkInstruction; diff --git a/src/interpreter/stack.ts b/src/interpreter/stack.ts index 5d6bd05..e4fd4c7 100644 --- a/src/interpreter/stack.ts +++ b/src/interpreter/stack.ts @@ -3,6 +3,8 @@ import { TypedCompoundStatement, isTypedCompoundStatement, isTypedDeclaration, + isTypedIterationStatement, + isTypedSelectionStatement, isTypedefDeclaration, } from "../ast/types"; import { ObjectTypeInfo, ParameterTypeAndIdentifier } from "../typing/types"; @@ -88,6 +90,12 @@ export class RuntimeStack extends Stack { const identifierPrefix: string[] = []; const scan = (stmts: TypedCompoundStatement): void => { let blockNo = 0; + const scanBlock = (b: TypedCompoundStatement) => { + identifierPrefix.push("block" + blockNo); + scan(b); + identifierPrefix.pop(); + blockNo++; + }; stmts.value.forEach((i) => { if (isTypedDeclaration(i)) { i.declaratorList.forEach((j) => { @@ -101,11 +109,17 @@ export class RuntimeStack extends Stack { res[qid] = { address: ptr, typeInfo }; ptr += typeInfo.size; }); - } else if (!isTypedefDeclaration(i) && isTypedCompoundStatement(i)) { - identifierPrefix.push("block" + blockNo); - scan(i); - identifierPrefix.pop(); - blockNo++; + } else if (!isTypedefDeclaration(i)) { + if (isTypedCompoundStatement(i)) scanBlock(i); + else if (isTypedSelectionStatement(i)) { + if (isTypedCompoundStatement(i.consequent)) scanBlock(i.consequent); + if (i.alternative && isTypedCompoundStatement(i.alternative)) + scanBlock(i.alternative); + } else if ( + isTypedIterationStatement(i) && + isTypedCompoundStatement(i.body) + ) + scanBlock(i.body); } }); }; diff --git a/src/memory/memory.ts b/src/memory/memory.ts index 6a643b0..9e20161 100644 --- a/src/memory/memory.ts +++ b/src/memory/memory.ts @@ -174,8 +174,6 @@ export class Memory { } private checkStrictAliasing(address: number, t: ObjectTypeInfo): boolean { - if (isChar(t) || isUnsignedChar(t) || isSignedChar(t)) return true; - let et: EffectiveTypeTableEntry; try { et = this.effectiveTypeTable.get(address); @@ -184,6 +182,7 @@ export class Memory { } if (et === NO_EFFECTIVE_TYPE) return true; + if (isChar(t) || isUnsignedChar(t) || isSignedChar(t)) return true; if (t.isCompatible(et)) return true; if (isSignedIntegerType(t) && getUnsignedVersion(t).isCompatible(et)) diff --git a/src/typing/env.ts b/src/typing/env.ts index ec62722..a410771 100644 --- a/src/typing/env.ts +++ b/src/typing/env.ts @@ -13,10 +13,12 @@ const TAG_PREFIX = "tag::"; export class TypeEnv { private env: Record[]; public readonly aggTypes: AggregateType[]; + private _inLoopBody: boolean; constructor() { this.env = [{}]; this.aggTypes = []; + this._inLoopBody = false; for (const [identifier, f] of Object.entries(BUILTIN_FUNCTIONS)) { this.addIdentifierTypeInfo(identifier, f.type); } @@ -31,6 +33,18 @@ export class TypeEnv { this.env.pop(); } + enterLoopBody(): void { + this._inLoopBody = true; + } + + exitLoopBody(): void { + this._inLoopBody = false; + } + + get inLoopBody(): boolean { + return this._inLoopBody; + } + getIdentifierTypeInfo(id: Identifier, isTypedef: boolean = false): TypeInfo { for (let i = this.env.length - 1; i >= 0; i--) { if (id in this.env[i]) { diff --git a/src/typing/main.ts b/src/typing/main.ts index bde6cfc..36375b5 100644 --- a/src/typing/main.ts +++ b/src/typing/main.ts @@ -2,10 +2,22 @@ import { BaseNode, CastExpressionNode, InitializerList, + IterationStatement, + IterationStatementDoWhile, + IterationStatementFor, + IterationStatementWhile, + SelectionStatement, + SelectionStatementIf, TypeSpecifier, TypedCastExpressionNode, TypedDesignator, TypedInitializerList, + TypedIterationStatement, + TypedIterationStatementDoWhile, + TypedIterationStatementFor, + TypedIterationStatementWhile, + TypedSelectionStatement, + TypedSelectionStatementIf, TypedUnaryExpressionDecr, TypedUnaryExpressionSizeof, Typedef, @@ -16,6 +28,11 @@ import { isCastExpressionNode, isInitializerList, isIntegerConstant, + isIterationStatement, + isIterationStatementDoWhile, + isIterationStatementWhile, + isJumpStatementReturn, + isSelectionStatement, isStorageClassSpecifier, isTypeName, isTypeSpecifier, @@ -447,6 +464,8 @@ const typeStatement = (t: Statement, env: TypeEnv): TypedStatement => return res; } if (isJumpStatement(t)) return typeJumpStatement(t, env); + if (isIterationStatement(t)) return typeIterationStatement(t, env); + if (isSelectionStatement(t)) return typeSelectionStatement(t, env); return typeExpressionStatement(t, env); }); @@ -455,8 +474,9 @@ const typeJumpStatement = ( env: TypeEnv, ): TypedJumpStatement => typeCheck(t, () => { - // if (isJumpStatementReturn(t)) - return typeJumpStatementReturn(t, env); + if (isJumpStatementReturn(t)) return typeJumpStatementReturn(t, env); + if (!env.inLoopBody) throw "break/continue outside of a loop body"; + return t; }); const typeJumpStatementReturn = ( @@ -490,6 +510,99 @@ const typeJumpStatementReturn = ( return { ...t, value: expr }; }); +const typeIterationStatement = ( + t: IterationStatement, + env: TypeEnv, +): TypedIterationStatement => + typeCheck(t, () => { + if (isIterationStatementWhile(t)) + return typeIterationStatementWhile(t, env); + if (isIterationStatementDoWhile(t)) + return typeIterationStatementDoWhile(t, env); + return typeIterationStatementFor(t, env); + }); + +const typeIterationStatementWhile = ( + t: IterationStatementWhile, + env: TypeEnv, +): TypedIterationStatementWhile => + typeCheck(t, () => { + const cond = typeExpression(t.cond, env); + if (!isScalarType(cond.typeInfo)) + throw "controlling expression of while statement should have scalar type"; + env.enterLoopBody(); + const body = typeStatement(t.body, env); + env.exitLoopBody(); + return { ...t, cond, body }; + }); + +const typeIterationStatementDoWhile = ( + t: IterationStatementDoWhile, + env: TypeEnv, +): TypedIterationStatementDoWhile => + typeCheck(t, () => { + const cond = typeExpression(t.cond, env); + if (!isScalarType(cond.typeInfo)) + throw "controlling expression of do while statement should have scalar type"; + env.enterLoopBody(); + const body = typeStatement(t.body, env); + env.exitLoopBody(); + return { ...t, cond, body }; + }); + +const typeIterationStatementFor = ( + t: IterationStatementFor, + env: TypeEnv, +): TypedIterationStatementFor => + typeCheck(t, () => { + let init; + if (t.init === null) init = null; + else if (isDeclaration(t.init)) { + throw "declaration in for statement not supported, consider moving it outside instead"; + // init = typeDeclaration(t.init, env); + // if (isTypedefDeclaration(init)) + // throw "typedef in declaration part of for statement"; + } else init = typeExpression(t.init, env); + + let controlExpr = null; + if (t.controlExpr !== null) { + controlExpr = typeExpression(t.controlExpr, env); + if (!isScalarType(controlExpr.typeInfo)) + throw "controlling expression of for statement should have scalar type"; + } + + const afterIterExpr = + t.afterIterExpr === null ? null : typeExpression(t.afterIterExpr, env); + + env.enterLoopBody(); + const body = typeStatement(t.body, env); + env.exitLoopBody(); + + return { ...t, init, controlExpr, afterIterExpr, body }; + }); + +const typeSelectionStatement = ( + t: SelectionStatement, + env: TypeEnv, +): TypedSelectionStatement => + typeCheck(t, () => { + return typeSelectionStatementIf(t, env); + }); + +const typeSelectionStatementIf = ( + t: SelectionStatementIf, + env: TypeEnv, +): TypedSelectionStatementIf => + typeCheck(t, () => { + const cond = typeExpression(t.cond, env); + if (!isScalarType(cond.typeInfo)) + throw "controlling expression of if statement should have scalar type"; + const consequent = typeStatement(t.consequent, env); + const alternative = + t.alternative === null ? null : typeStatement(t.alternative, env); + return { ...t, cond, consequent, alternative }; + }); + const typeExpressionStatement = ( t: ExpressionStatement, env: TypeEnv,