diff --git a/.prettierignore b/.prettierignore index 0f43ea4..dfacc2b 100644 --- a/.prettierignore +++ b/.prettierignore @@ -2,4 +2,3 @@ node_modules dist src/ast -src/ec-evaluator diff --git a/src/ec-evaluator/components.ts b/src/ec-evaluator/components.ts index 7728606..920ddb2 100644 --- a/src/ec-evaluator/components.ts +++ b/src/ec-evaluator/components.ts @@ -1,7 +1,7 @@ -import { UnannType } from "../ast/types/classes"; -import { DECLARED_BUT_NOT_YET_ASSIGNED, GLOBAL_FRAME, OBJECT_CLASS } from "./constants"; -import * as errors from "./errors"; -import * as struct from "./structCreator"; +import { UnannType } from '../ast/types/classes' +import { DECLARED_BUT_NOT_YET_ASSIGNED, GLOBAL_FRAME, OBJECT_CLASS } from './constants' +import * as errors from './errors' +import * as struct from './structCreator' import { Class, Closure, @@ -11,181 +11,181 @@ import { StashItem, Value, VarValue, - Variable, -} from "./types"; -import { Stack } from "./utils"; + Variable +} from './types' +import { Stack } from './utils' /** * Components of CSE Machine. */ -export class Control extends Stack {}; -export class Stash extends Stack {}; +export class Control extends Stack {} +export class Stash extends Stack {} export class Environment { - private _global: EnvNode; - private _current: EnvNode; - private _objects: Object[] = []; + private _global: EnvNode + private _current: EnvNode + private _objects: Object[] = [] constructor() { - const node = new EnvNode(GLOBAL_FRAME); - this._global = node; - this._current = node; + const node = new EnvNode(GLOBAL_FRAME) + this._global = node + this._current = node } get global() { - return this._global; + return this._global } get current() { - return this._current; + return this._current } get objects() { - return this._objects; + return this._objects } set current(node: EnvNode) { - this._current = node; + this._current = node } extendEnv(fromEnv: EnvNode, name: string) { // Create new environemnt. - const node = new EnvNode(name); - node.parent = fromEnv; - fromEnv.addChild(node); + const node = new EnvNode(name) + node.parent = fromEnv + fromEnv.addChild(node) // Set current environment. - this._current = node; + this._current = node } createObj(c: Class): Object { // Create new environment. - const node = new EnvNode(OBJECT_CLASS); + const node = new EnvNode(OBJECT_CLASS) // Create new object. - const obj = struct.objStruct(node, c); + const obj = struct.objStruct(node, c) // Add to objects arr. - this._objects.push(obj); + this._objects.push(obj) // Set current environment. - this._current = node; + this._current = node - return obj; + return obj } restoreEnv(toEnv: EnvNode) { - this._current = toEnv; + this._current = toEnv } declareVariable(name: Name, type: UnannType) { - const variable = struct.varStruct(type, name, DECLARED_BUT_NOT_YET_ASSIGNED); - this._current.setVariable(name, variable); + const variable = struct.varStruct(type, name, DECLARED_BUT_NOT_YET_ASSIGNED) + this._current.setVariable(name, variable) } defineVariable(name: Name, type: UnannType, value: VarValue) { - const variable = struct.varStruct(type, name, value); - this._current.setVariable(name, variable); + const variable = struct.varStruct(type, name, value) + this._current.setVariable(name, variable) } getName(name: Name): Variable | Class { - return this._current.getName(name); + return this._current.getName(name) } getVariable(name: Name): Variable { - return this._current.getVariable(name); + return this._current.getVariable(name) } defineMtdOrCon(name: Name, method: Closure) { - this._current.setMtdOrCon(name, method); + this._current.setMtdOrCon(name, method) } defineClass(name: Name, c: Class) { - this._global.setClass(name, c); + this._global.setClass(name, c) } getClass(name: Name): Class { - return this._global.getClass(name); + return this._global.getClass(name) } -}; +} // Frame with metadata, e.g., parent/children, name. export class EnvNode { - private _frame: Frame = new Frame(); - private _parent: EnvNode; - private _children: EnvNode[] = []; + private _frame: Frame = new Frame() + private _parent: EnvNode + private _children: EnvNode[] = [] constructor(readonly name: string) {} get frame() { - return this._frame; + return this._frame } get parent(): EnvNode { - return this._parent; + return this._parent } set parent(parent: EnvNode) { - this._parent = parent; + this._parent = parent } get children() { - return this._children; + return this._children } addChild(child: EnvNode) { - this._children.push(child); + this._children.push(child) } setVariable(name: Name, value: Variable) { if (this._frame.has(name)) { - throw new errors.VariableRedeclarationError(name); + throw new errors.VariableRedeclarationError(name) } - this._frame.set(name, value); + this._frame.set(name, value) } getName(name: Name): Variable | Class { if (this._frame.has(name)) { - return this._frame.get(name) as Variable | Class; + return this._frame.get(name) as Variable | Class } if (this._parent) { - return this._parent.getName(name); + return this._parent.getName(name) } - throw new errors.UndeclaredNameError(name); + throw new errors.UndeclaredNameError(name) } getVariable(name: Name): Variable { if (this._frame.has(name)) { - return this._frame.get(name) as Variable; + return this._frame.get(name) as Variable } if (this._parent) { - return this._parent.getVariable(name); + return this._parent.getVariable(name) } - throw new errors.UndeclaredVariableError(name); + throw new errors.UndeclaredVariableError(name) } setMtdOrCon(name: Name, value: Closure) { if (this._frame.has(name)) { - throw new errors.MtdOrConRedeclarationError(name); + throw new errors.MtdOrConRedeclarationError(name) } - this._frame.set(name, value); + this._frame.set(name, value) } setClass(name: Name, value: Class) { if (this._frame.has(name)) { - throw new errors.ClassRedeclarationError(name); + throw new errors.ClassRedeclarationError(name) } - this._frame.set(name, value); + this._frame.set(name, value) } getClass(name: Name): Class { if (this._frame.has(name)) { - return this._frame.get(name) as Class; + return this._frame.get(name) as Class } if (this._parent) { - return this._parent.getClass(name); + return this._parent.getClass(name) } - throw new errors.UndeclaredClassError(name); + throw new errors.UndeclaredClassError(name) } } diff --git a/src/ec-evaluator/constants.ts b/src/ec-evaluator/constants.ts index 5a5ce44..602246a 100644 --- a/src/ec-evaluator/constants.ts +++ b/src/ec-evaluator/constants.ts @@ -1,12 +1,12 @@ -import { symStruct } from "./structCreator"; +import { symStruct } from './structCreator' -export const STEP_LIMIT = 100000; -export const DECLARED_BUT_NOT_YET_ASSIGNED = symStruct("Used to implement block scope"); +export const STEP_LIMIT = 100000 +export const DECLARED_BUT_NOT_YET_ASSIGNED = symStruct('Used to implement block scope') -export const THIS_KEYWORD = "this"; -export const SUPER_KEYWORD = "super"; +export const THIS_KEYWORD = 'this' +export const SUPER_KEYWORD = 'super' -export const OBJECT_CLASS = "Object"; +export const OBJECT_CLASS = 'Object' -export const GLOBAL_FRAME = "global"; -export const BLOCK_FRAME = "block"; +export const GLOBAL_FRAME = 'global' +export const BLOCK_FRAME = 'block' diff --git a/src/ec-evaluator/errors.ts b/src/ec-evaluator/errors.ts index 28352a4..495b3a0 100644 --- a/src/ec-evaluator/errors.ts +++ b/src/ec-evaluator/errors.ts @@ -1,153 +1,162 @@ -import { Type } from "./types"; +import { Type } from './types' export enum ErrorType { - SYNTAX = "Syntax", - RUNTIME = "Runtime", + SYNTAX = 'Syntax', + RUNTIME = 'Runtime' } export interface SourceError { - type: ErrorType; - explain(): string; + type: ErrorType + explain(): string } export class SyntaxError implements SourceError { - type = ErrorType.SYNTAX; + type = ErrorType.SYNTAX - constructor(private errMsg: string) {}; + constructor(private errMsg: string) {} explain(): string { - return `SyntaxError: ${this.errMsg}`; - }; -}; + return `SyntaxError: ${this.errMsg}` + } +} export class RuntimeError implements SourceError { - type = ErrorType.RUNTIME; - - constructor(private msg: string = "") {}; + type = ErrorType.RUNTIME + + constructor(private msg: string = '') {} explain(): string { - return `RuntimeError${this.msg ? ": " + this.msg : ""}`; - }; -}; + return `RuntimeError${this.msg ? ': ' + this.msg : ''}` + } +} export class UndeclaredVariableError extends RuntimeError { constructor(private name: string) { - super(); - }; + super() + } public explain() { - return `${super.explain()}: Name ${this.name} not declared.`; + return `${super.explain()}: Name ${this.name} not declared.` } } export class UnassignedVariableError extends RuntimeError { constructor(private name: string) { - super(); - }; + super() + } public explain() { - return `${super.explain()}: Name ${this.name} not definitely assigned.`; + return `${super.explain()}: Name ${this.name} not definitely assigned.` } } export class VariableRedeclarationError extends RuntimeError { constructor(private name: string) { - super(); - }; + super() + } public explain() { - return `${super.explain()}: Name ${this.name} redeclared.`; + return `${super.explain()}: Name ${this.name} redeclared.` } } export class UndeclaredMethodError extends RuntimeError { constructor(private name: string) { - super(); - }; + super() + } public explain() { - return `${super.explain()}: Method ${this.name} not declared.`; + return `${super.explain()}: Method ${this.name} not declared.` } } export class MtdOrConRedeclarationError extends RuntimeError { constructor(private name: string) { - super(); - }; + super() + } public explain() { - return `${super.explain()}: Method/Constructor ${this.name} redeclared.`; + return `${super.explain()}: Method/Constructor ${this.name} redeclared.` } } export class UndeclaredClassError extends RuntimeError { constructor(private name: string) { - super(); - }; + super() + } public explain() { - return `${super.explain()}: Class ${this.name} not declared.`; + return `${super.explain()}: Class ${this.name} not declared.` } } export class ClassRedeclarationError extends RuntimeError { constructor(private name: string) { - super(); - }; + super() + } public explain() { - return `${super.explain()}: Class ${this.name} redeclared.`; + return `${super.explain()}: Class ${this.name} redeclared.` } } export class UndeclaredNameError extends RuntimeError { constructor(private name: string) { - super(); - }; + super() + } public explain() { - return `${super.explain()}: Name ${this.name} not declared.`; + return `${super.explain()}: Name ${this.name} not declared.` } } export class ResOverloadError extends RuntimeError { - constructor(private name: string, private argTypes: Type[]) { - super(); - }; + constructor( + private name: string, + private argTypes: Type[] + ) { + super() + } public explain() { - return `${super.explain()}: Overloading resolution of method ${this.name} with argument type${this.argTypes.length > 1 ? "s" : ""} ${this.argTypes.map(t => t.type).join(", ")} failed.`; + return `${super.explain()}: Overloading resolution of method ${this.name} with argument type${this.argTypes.length > 1 ? 's' : ''} ${this.argTypes.map(t => t.type).join(', ')} failed.` } } export class ResOverloadAmbiguousError extends RuntimeError { - constructor(private name: string, private argTypes: Type[]) { - super(); - }; + constructor( + private name: string, + private argTypes: Type[] + ) { + super() + } public explain() { - return `${super.explain()}: Overloading resolution of method ${this.name} with argument type${this.argTypes.length > 1 ? "s" : ""} ${this.argTypes.map(t => t.type).join(", ")} is ambiguous.`; + return `${super.explain()}: Overloading resolution of method ${this.name} with argument type${this.argTypes.length > 1 ? 's' : ''} ${this.argTypes.map(t => t.type).join(', ')} is ambiguous.` } } export class ResConOverloadError extends RuntimeError { - constructor(private name: string, private argTypes: Type[]) { - super(); - }; + constructor( + private name: string, + private argTypes: Type[] + ) { + super() + } public explain() { - return `${super.explain()}: Overloading resolution of constructor ${this.name} with argument type${this.argTypes.length > 1 ? "s" : ""} ${this.argTypes.map(t => t.type).join(", ")} failed.`; + return `${super.explain()}: Overloading resolution of constructor ${this.name} with argument type${this.argTypes.length > 1 ? 's' : ''} ${this.argTypes.map(t => t.type).join(', ')} failed.` } } export class NullPointerException extends RuntimeError { public explain() { - return `Accessing instance field/method of null value.`; + return `Accessing instance field/method of null value.` } } export class NoMainMtdError extends RuntimeError { public explain() { - return `public static void main(String[] args) is not defined in any class.`; + return `public static void main(String[] args) is not defined in any class.` } } diff --git a/src/ec-evaluator/index.ts b/src/ec-evaluator/index.ts index c133dcb..9fbf01b 100644 --- a/src/ec-evaluator/index.ts +++ b/src/ec-evaluator/index.ts @@ -1,38 +1,35 @@ -import { parse } from "../ast/parser"; -import { Control, Environment, Stash } from "./components"; -import { STEP_LIMIT } from "./constants"; -import { RuntimeError } from "./errors"; -import { evaluate } from "./interpreter"; -import { Context, Error, Finished, Result } from "./types"; +import { parse } from '../ast/parser' +import { Control, Environment, Stash } from './components' +import { STEP_LIMIT } from './constants' +import { RuntimeError } from './errors' +import { evaluate } from './interpreter' +import { Context, Error, Finished, Result } from './types' -export * from './components'; -export * from './errors'; -export * from './types'; -export { isInstr, isNode } from './utils'; +export * from './components' +export * from './errors' +export * from './types' +export { isInstr, isNode } from './utils' -export const runECEvaluator = ( - code: string, - targetStep: number = STEP_LIMIT, -): Promise => { - const context = createContext(); +export const runECEvaluator = (code: string, targetStep: number = STEP_LIMIT): Promise => { + const context = createContext() try { // parse() may throw SyntaxError. - const compilationUnit = parse(code); + const compilationUnit = parse(code) - context.control.push(compilationUnit); + context.control.push(compilationUnit) // evaluate() may throw RuntimeError - const value = evaluate(context, targetStep); + const value = evaluate(context, targetStep) return new Promise((resolve, _) => { - resolve({ status: 'finished', context, value } as Finished); - }); + resolve({ status: 'finished', context, value } as Finished) + }) } catch (e) { // Possible interpreting language error thrown, so conversion to RuntimeError may be required. - const error = e.type ? e : new RuntimeError(e.message); - context.errors.push(error); + const error = e.type ? e : new RuntimeError(e.message) + context.errors.push(error) return new Promise((resolve, _) => { - resolve({ status: 'error', context } as Error); - }); + resolve({ status: 'error', context } as Error) + }) } } @@ -43,5 +40,5 @@ export const createContext = (): Context => ({ stash: new Stash(), environment: new Environment(), - totalSteps: STEP_LIMIT, -}); + totalSteps: STEP_LIMIT +}) diff --git a/src/ec-evaluator/instrCreator.ts b/src/ec-evaluator/instrCreator.ts index d6aa6b3..d00376a 100644 --- a/src/ec-evaluator/instrCreator.ts +++ b/src/ec-evaluator/instrCreator.ts @@ -1,6 +1,6 @@ -import { Node } from "../ast/types/ast"; -import { Expression } from "../ast/types/blocks-and-statements"; -import { EnvNode } from "./components"; +import { Node } from '../ast/types/ast' +import { Expression } from '../ast/types/blocks-and-statements' +import { EnvNode } from './components' import { AssmtInstr, BinOpInstr, @@ -18,139 +18,96 @@ import { ResOverloadInstr, ResOverrideInstr, ResTypeContInstr, - ResTypeInstr, -} from "./types"; + ResTypeInstr +} from './types' -export const assmtInstr = ( - srcNode: Node, -): AssmtInstr => ({ +export const assmtInstr = (srcNode: Node): AssmtInstr => ({ instrType: InstrType.ASSIGNMENT, - srcNode, -}); + srcNode +}) -export const binOpInstr = ( - symbol: string, - srcNode: Node, -): BinOpInstr => ({ +export const binOpInstr = (symbol: string, srcNode: Node): BinOpInstr => ({ instrType: InstrType.BINARY_OP, symbol, - srcNode, -}); + srcNode +}) -export const popInstr = ( - srcNode: Node, -): Instr => ({ +export const popInstr = (srcNode: Node): Instr => ({ instrType: InstrType.POP, - srcNode, -}); + srcNode +}) -export const invInstr = ( - arity: number, - srcNode: Node, -): InvInstr => ({ +export const invInstr = (arity: number, srcNode: Node): InvInstr => ({ instrType: InstrType.INVOCATION, arity, - srcNode, -}); + srcNode +}) -export const envInstr = ( - env: EnvNode, - srcNode: Node, -): EnvInstr => ({ +export const envInstr = (env: EnvNode, srcNode: Node): EnvInstr => ({ instrType: InstrType.ENV, env, - srcNode, -}); + srcNode +}) -export const markerInstr = ( - srcNode: Node, -): MarkerInstr => ({ +export const markerInstr = (srcNode: Node): MarkerInstr => ({ instrType: InstrType.MARKER, - srcNode, -}); + srcNode +}) -export const resetInstr = ( - srcNode: Node, -): Instr => ({ +export const resetInstr = (srcNode: Node): Instr => ({ instrType: InstrType.RESET, - srcNode, -}); + srcNode +}) -export const evalVarInstr = ( - symbol: string, - srcNode: Node, -): EvalVarInstr => ({ +export const evalVarInstr = (symbol: string, srcNode: Node): EvalVarInstr => ({ instrType: InstrType.EVAL_VAR, srcNode, - symbol, -}); + symbol +}) -export const resInstr = ( - name: string, - srcNode: Node, -): ResInstr => ({ +export const resInstr = (name: string, srcNode: Node): ResInstr => ({ instrType: InstrType.RES, srcNode, - name, -}); + name +}) -export const derefInstr = ( - srcNode: Node, -): DerefInstr => ({ +export const derefInstr = (srcNode: Node): DerefInstr => ({ instrType: InstrType.DEREF, - srcNode, -}); + srcNode +}) -export const newInstr = ( - c: Class, - srcNode: Node, -): NewInstr => ({ +export const newInstr = (c: Class, srcNode: Node): NewInstr => ({ instrType: InstrType.NEW, srcNode, - c, -}); + c +}) -export const resTypeInstr = ( - value: Expression | Class, - srcNode: Node, -): ResTypeInstr => ({ +export const resTypeInstr = (value: Expression | Class, srcNode: Node): ResTypeInstr => ({ instrType: InstrType.RES_TYPE, srcNode, - value, -}); + value +}) -export const resTypeContInstr = ( - name: string, - srcNode: Node, -): ResTypeContInstr => ({ +export const resTypeContInstr = (name: string, srcNode: Node): ResTypeContInstr => ({ instrType: InstrType.RES_TYPE_CONT, srcNode, - name, -}); + name +}) -export const resOverloadInstr = ( - name: string, - arity: number, - srcNode: Node, -): ResOverloadInstr => ({ +export const resOverloadInstr = (name: string, arity: number, srcNode: Node): ResOverloadInstr => ({ instrType: InstrType.RES_OVERLOAD, srcNode, name, - arity, -}); + arity +}) -export const resOverrideInstr = ( - srcNode: Node, -): ResOverrideInstr => ({ +export const resOverrideInstr = (srcNode: Node): ResOverrideInstr => ({ instrType: InstrType.RES_OVERRIDE, - srcNode, -}); + srcNode +}) -export const resConOverloadInstr = ( - arity: number, - srcNode: Node, -): ResConOverloadInstr => ({ +export const resConOverloadInstr = (arity: number, srcNode: Node): ResConOverloadInstr => ({ instrType: InstrType.RES_CON_OVERLOAD, srcNode, - arity, -}); + arity +}) diff --git a/src/ec-evaluator/interpreter.ts b/src/ec-evaluator/interpreter.ts index 0eeb08e..fa8c548 100644 --- a/src/ec-evaluator/interpreter.ts +++ b/src/ec-evaluator/interpreter.ts @@ -1,6 +1,6 @@ -import { cloneDeep } from "lodash"; +import { cloneDeep } from 'lodash' -import { +import { Assignment, BinaryExpression, Block, @@ -15,8 +15,8 @@ import { MethodInvocation, ReturnStatement, VariableDeclarator, - Void, -} from "../ast/types/blocks-and-statements"; + Void +} from '../ast/types/blocks-and-statements' import { ConstructorDeclaration, FieldDeclaration, @@ -24,15 +24,22 @@ import { Identifier, MethodDeclaration, NormalClassDeclaration, - UnannType, -} from "../ast/types/classes"; -import { CompilationUnit } from "../ast/types/packages-and-modules"; -import { Control, EnvNode, Environment, Stash } from "./components"; -import { BLOCK_FRAME, GLOBAL_FRAME, OBJECT_CLASS, STEP_LIMIT, SUPER_KEYWORD, THIS_KEYWORD } from "./constants"; -import * as errors from "./errors"; -import * as instr from './instrCreator'; -import * as node from './nodeCreator'; -import * as struct from './structCreator'; + UnannType +} from '../ast/types/classes' +import { CompilationUnit } from '../ast/types/packages-and-modules' +import { Control, EnvNode, Environment, Stash } from './components' +import { + BLOCK_FRAME, + GLOBAL_FRAME, + OBJECT_CLASS, + STEP_LIMIT, + SUPER_KEYWORD, + THIS_KEYWORD +} from './constants' +import * as errors from './errors' +import * as instr from './instrCreator' +import * as node from './nodeCreator' +import * as struct from './structCreator' import { ControlItem, AssmtInstr, @@ -58,9 +65,9 @@ import { ResConOverloadInstr, ResOverrideInstr, ResTypeContInstr, - StructType, -} from "./types"; -import { + StructType +} from './types' +import { defaultValues, evaluateBinaryExpression, getConstructors, @@ -84,46 +91,49 @@ import { resOverride, resConOverload, isNull, - makeNonLocalVarNonParamSimpleNameQualified, -} from "./utils"; + makeNonLocalVarNonParamSimpleNameQualified +} from './utils' type CmdEvaluator = ( command: ControlItem, environment: Environment, control: Control, - stash: Stash, + stash: Stash ) => void /** * Evaluate program in context within limited number of steps. * @throws {errors.RuntimeError} Throw error if program is semantically invalid. */ -export const evaluate = (context: Context, targetStep: number = STEP_LIMIT): StashItem | undefined => { - const environment = context.environment; - const control = context.control; - const stash = context.stash; +export const evaluate = ( + context: Context, + targetStep: number = STEP_LIMIT +): StashItem | undefined => { + const environment = context.environment + const control = context.control + const stash = context.stash + + context.totalSteps = 0 - context.totalSteps = 0; + let command = control.peek() - let command = control.peek(); - while (command) { if (context.totalSteps === targetStep) { - return stash.peek(); + return stash.peek() } - control.pop(); + control.pop() if (isNode(command)) { - cmdEvaluators[command.kind](command, environment, control, stash); + cmdEvaluators[command.kind](command, environment, control, stash) } else { - cmdEvaluators[command.instrType](command, environment, control, stash); + cmdEvaluators[command.instrType](command, environment, control, stash) } - command = control.peek(); - context.totalSteps += 1; + command = control.peek() + context.totalSteps += 1 } - return stash.peek(); + return stash.peek() } const cmdEvaluators: { [type: string]: CmdEvaluator } = { @@ -131,275 +141,263 @@ const cmdEvaluators: { [type: string]: CmdEvaluator } = { command: CompilationUnit, _environment: Environment, control: Control, - _stash: Stash, + _stash: Stash ) => { // Get first class that defines the main method according to program order. - const className = searchMainMtdClass(command.topLevelClassOrInterfaceDeclarations); + const className = searchMainMtdClass(command.topLevelClassOrInterfaceDeclarations) if (!className) { - throw new errors.NoMainMtdError(); + throw new errors.NoMainMtdError() } - control.push(node.mainMtdInvExpStmtNode(className)); - control.push(...handleSequence(command.topLevelClassOrInterfaceDeclarations)); - control.push(node.objClassDeclNode()); + control.push(node.mainMtdInvExpStmtNode(className)) + control.push(...handleSequence(command.topLevelClassOrInterfaceDeclarations)) + control.push(node.objClassDeclNode()) }, - + NormalClassDeclaration: ( command: NormalClassDeclaration, environment: Environment, control: Control, - _stash: Stash, + _stash: Stash ) => { - const className = command.typeIdentifier; + const className = command.typeIdentifier - const instanceFields = getInstanceFields(command); - const instanceMethods = getInstanceMethods(command); + const instanceFields = getInstanceFields(command) + const instanceMethods = getInstanceMethods(command) // Make MethodInvocation simple Identifier qualified to facilitate MethodInvocation evaluation. - instanceMethods.forEach(m => makeMtdInvSimpleIdentifierQualified(m, THIS_KEYWORD)); + instanceMethods.forEach(m => makeMtdInvSimpleIdentifierQualified(m, THIS_KEYWORD)) // Make non local var simple name qualified. - instanceMethods.forEach(m => makeNonLocalVarNonParamSimpleNameQualified(m, THIS_KEYWORD)); - instanceMethods.forEach(m => appendEmtpyReturn(m)); + instanceMethods.forEach(m => makeNonLocalVarNonParamSimpleNameQualified(m, THIS_KEYWORD)) + instanceMethods.forEach(m => appendEmtpyReturn(m)) - const staticFields = getStaticFields(command); - const staticMethods = getStaticMethods(command); + const staticFields = getStaticFields(command) + const staticMethods = getStaticMethods(command) // Make MethodInvocation simple Identifier qualified to facilitate MethodInvocation evaluation. - staticMethods.forEach(m => makeMtdInvSimpleIdentifierQualified(m, className)); + staticMethods.forEach(m => makeMtdInvSimpleIdentifierQualified(m, className)) // Make non local var simple name qualified. - staticMethods.forEach(m => makeNonLocalVarNonParamSimpleNameQualified(m, className)); - staticMethods.forEach(m => appendEmtpyReturn(m)); + staticMethods.forEach(m => makeNonLocalVarNonParamSimpleNameQualified(m, className)) + staticMethods.forEach(m => appendEmtpyReturn(m)) // Class that doesn't explicitly inherit another class implicitly inherits Object, except Object. - className !== OBJECT_CLASS && !command.sclass && (command.sclass = OBJECT_CLASS); + className !== OBJECT_CLASS && !command.sclass && (command.sclass = OBJECT_CLASS) - const constructors = getConstructors(command); + const constructors = getConstructors(command) // Insert default constructor if not overriden. if (!constructors.find(c => c.constructorDeclarator.formalParameterList.length === 0)) { - const defaultConstructor = node.defaultConstructorDeclNode(className, command); - constructors.push(defaultConstructor); + const defaultConstructor = node.defaultConstructorDeclNode(className, command) + constructors.push(defaultConstructor) } // Prepend instance fields initialization if needed at start of constructor body. - constructors.forEach(c => prependInstanceFieldsInitIfNeeded(c, instanceFields)); + constructors.forEach(c => prependInstanceFieldsInitIfNeeded(c, instanceFields)) // Make non local var simple name qualified. - constructors.forEach(c => makeNonLocalVarNonParamSimpleNameQualified(c, THIS_KEYWORD)); + constructors.forEach(c => makeNonLocalVarNonParamSimpleNameQualified(c, THIS_KEYWORD)) // Prepend super() if needed before instance fields initialization. - constructors.forEach(c => prependExpConInvIfNeeded(c, command)); + constructors.forEach(c => prependExpConInvIfNeeded(c, command)) // Append ReturnStatement with this keyword at end of constructor body. - constructors.forEach(c => appendOrReplaceReturn(c)); + constructors.forEach(c => appendOrReplaceReturn(c)) // To restore current (global) env for next NormalClassDeclarations evaluation. - control.push(instr.envInstr(environment.current, command)); - - const superclass: Class | undefined = command.sclass ? environment.getClass(command.sclass) : undefined; + control.push(instr.envInstr(environment.current, command)) + + const superclass: Class | undefined = command.sclass + ? environment.getClass(command.sclass) + : undefined // TODO Object should not extend global? - const fromEnv = superclass ? superclass.frame : environment.global; - environment.extendEnv(fromEnv, className); + const fromEnv = superclass ? superclass.frame : environment.global + environment.extendEnv(fromEnv, className) const c = struct.classStruct( - environment.current, command, constructors, - instanceFields, instanceMethods, staticFields, staticMethods, superclass - ); - environment.defineClass(className, c); - - control.push(...handleSequence(instanceMethods)); - control.push(...handleSequence(staticMethods)); - control.push(...handleSequence(constructors)); - control.push(...handleSequence(staticFields)); - }, - - Block: ( - command: Block, - environment: Environment, - control: Control, - _stash: Stash, - ) => { + environment.current, + command, + constructors, + instanceFields, + instanceMethods, + staticFields, + staticMethods, + superclass + ) + environment.defineClass(className, c) + + control.push(...handleSequence(instanceMethods)) + control.push(...handleSequence(staticMethods)) + control.push(...handleSequence(constructors)) + control.push(...handleSequence(staticFields)) + }, + + Block: (command: Block, environment: Environment, control: Control, _stash: Stash) => { // Save current environment before extending. - control.push(instr.envInstr(environment.current, command)); - control.push(...handleSequence(command.blockStatements)); + control.push(instr.envInstr(environment.current, command)) + control.push(...handleSequence(command.blockStatements)) - environment.extendEnv(environment.current, BLOCK_FRAME); + environment.extendEnv(environment.current, BLOCK_FRAME) }, ConstructorDeclaration: ( command: ConstructorDeclaration, environment: Environment, _control: Control, - _stash: Stash, + _stash: Stash ) => { // Use constructor descriptor as key. - const conDescriptor: string = getDescriptor(command); - const conClosure = struct.closureStruct(command, environment.current); - environment.defineMtdOrCon(conDescriptor, conClosure); + const conDescriptor: string = getDescriptor(command) + const conClosure = struct.closureStruct(command, environment.current) + environment.defineMtdOrCon(conDescriptor, conClosure) }, MethodDeclaration: ( command: MethodDeclaration, environment: Environment, _control: Control, - _stash: Stash, + _stash: Stash ) => { // Use method descriptor as key. - const mtdDescriptor: string = getDescriptor(command); - const mtdClosure = struct.closureStruct(command, environment.current); - environment.defineMtdOrCon(mtdDescriptor, mtdClosure); + const mtdDescriptor: string = getDescriptor(command) + const mtdClosure = struct.closureStruct(command, environment.current) + environment.defineMtdOrCon(mtdDescriptor, mtdClosure) }, FieldDeclaration: ( command: FieldDeclaration, environment: Environment, control: Control, - _stash: Stash, + _stash: Stash ) => { // FieldDeclaration to be evaluated are always static fields. // Instance fields are transformed into Assignment ExpressionStatement inserted into constructors. - const type: UnannType = command.fieldType; - const declaration: VariableDeclarator = command.variableDeclaratorList[0]; - const id: Identifier = declaration.variableDeclaratorId; + const type: UnannType = command.fieldType + const declaration: VariableDeclarator = command.variableDeclaratorList[0] + const id: Identifier = declaration.variableDeclaratorId // Fields are always initialized to default value if initializer is absent. const init = (declaration?.variableInitializer || defaultValues.get(type) || - node.nullLitNode(command)) as Expression; - - environment.declareVariable(id, type); - - control.push(instr.popInstr(command)); - control.push(instr.assmtInstr(command)); - control.push(init); - control.push(instr.evalVarInstr(id, command)); + node.nullLitNode(command)) as Expression + + environment.declareVariable(id, type) + + control.push(instr.popInstr(command)) + control.push(instr.assmtInstr(command)) + control.push(init) + control.push(instr.evalVarInstr(id, command)) }, LocalVariableDeclarationStatement: ( command: LocalVariableDeclarationStatement, environment: Environment, control: Control, - _stash: Stash, + _stash: Stash ) => { - const type: LocalVariableType = command.localVariableType; - const declaration: VariableDeclarator = command.variableDeclaratorList[0]; - const id: Identifier = declaration.variableDeclaratorId; + const type: LocalVariableType = command.localVariableType + const declaration: VariableDeclarator = command.variableDeclaratorList[0] + const id: Identifier = declaration.variableDeclaratorId // Break down LocalVariableDeclarationStatement with VariableInitializer into // LocalVariableDeclarationStatement without VariableInitializer and Assignment. - const init: Expression | undefined = declaration?.variableInitializer as Expression; + const init: Expression | undefined = declaration?.variableInitializer as Expression if (init) { - control.push(node.expStmtAssmtNode(id, init, command)); - control.push(node.localVarDeclNoInitNode(type, id, command)); - return; + control.push(node.expStmtAssmtNode(id, init, command)) + control.push(node.localVarDeclNoInitNode(type, id, command)) + return } // Evaluating LocalVariableDeclarationStatement just declares the variable. - environment.declareVariable(id, type); + environment.declareVariable(id, type) }, ExpressionStatement: ( command: ExpressionStatement, _environment: Environment, control: Control, - _stash: Stash, + _stash: Stash ) => { - control.push(instr.popInstr(command)); - control.push(command.stmtExp); + control.push(instr.popInstr(command)) + control.push(command.stmtExp) }, ReturnStatement: ( command: ReturnStatement, _environment: Environment, control: Control, - _stash: Stash, + _stash: Stash ) => { - control.push(instr.resetInstr(command)); - control.push(command.exp); + control.push(instr.resetInstr(command)) + control.push(command.exp) }, - Assignment: ( - command: Assignment, - _environment: Environment, - control: Control, - _stash: Stash, - ) => { - control.push(instr.assmtInstr(command)); - control.push(command.right); - control.push(instr.evalVarInstr((command.left as ExpressionName).name, command)); + Assignment: (command: Assignment, _environment: Environment, control: Control, _stash: Stash) => { + control.push(instr.assmtInstr(command)) + control.push(command.right) + control.push(instr.evalVarInstr((command.left as ExpressionName).name, command)) }, - + MethodInvocation: ( command: MethodInvocation, _environment: Environment, control: Control, - _stash: Stash, + _stash: Stash ) => { - const nameParts = command.identifier.split("."); - const target = nameParts.splice(0, nameParts.length - 1).join("."); - const identifier = nameParts[nameParts.length - 1]; - + const nameParts = command.identifier.split('.') + const target = nameParts.splice(0, nameParts.length - 1).join('.') + const identifier = nameParts[nameParts.length - 1] + // Arity may be incremented by 1 if the resolved method to be invoked is an instance method. - control.push(instr.invInstr(command.argumentList.length, command)); - control.push(...handleSequence(command.argumentList)); - control.push(instr.resOverrideInstr(command)); - control.push(node.exprNameNode(target, command)); - control.push(instr.resOverloadInstr(identifier, command.argumentList.length, command)); + control.push(instr.invInstr(command.argumentList.length, command)) + control.push(...handleSequence(command.argumentList)) + control.push(instr.resOverrideInstr(command)) + control.push(node.exprNameNode(target, command)) + control.push(instr.resOverloadInstr(identifier, command.argumentList.length, command)) // TODO: only Integer and ExpressionName are allowed as args - control.push(...handleSequence(command.argumentList.map(a => instr.resTypeInstr(a, command)))); - control.push(instr.resTypeInstr(node.exprNameNode(target, command), command)); + control.push(...handleSequence(command.argumentList.map(a => instr.resTypeInstr(a, command)))) + control.push(instr.resTypeInstr(node.exprNameNode(target, command), command)) }, ClassInstanceCreationExpression: ( command: ClassInstanceCreationExpression, environment: Environment, control: Control, - _stash: Stash, + _stash: Stash ) => { - const c: Class = environment.getClass(command.identifier); + const c: Class = environment.getClass(command.identifier) - control.push(instr.invInstr(command.argumentList.length + 1, command)); - control.push(...handleSequence(command.argumentList)); + control.push(instr.invInstr(command.argumentList.length + 1, command)) + control.push(...handleSequence(command.argumentList)) control.push(instr.newInstr(c, command)) - control.push(instr.resConOverloadInstr(command.argumentList.length, command)); - control.push(...handleSequence(command.argumentList.map(a => instr.resTypeInstr(a, command)))); - control.push(instr.resTypeInstr(c, command)); + control.push(instr.resConOverloadInstr(command.argumentList.length, command)) + control.push(...handleSequence(command.argumentList.map(a => instr.resTypeInstr(a, command)))) + control.push(instr.resTypeInstr(c, command)) }, ExplicitConstructorInvocation: ( command: ExplicitConstructorInvocation, _environment: Environment, control: Control, - _stash: Stash, + _stash: Stash ) => { - control.push(instr.popInstr(command)); - control.push(instr.invInstr(command.argumentList.length + 1, command)); - control.push(...handleSequence(command.argumentList)); - control.push(node.exprNameNode(command.thisOrSuper, command)); - control.push(instr.resConOverloadInstr(command.argumentList.length, command)); - control.push(...handleSequence(command.argumentList.map(a => instr.resTypeInstr(a, command)))); - control.push(instr.resTypeInstr(node.exprNameNode(command.thisOrSuper, command), command)); + control.push(instr.popInstr(command)) + control.push(instr.invInstr(command.argumentList.length + 1, command)) + control.push(...handleSequence(command.argumentList)) + control.push(node.exprNameNode(command.thisOrSuper, command)) + control.push(instr.resConOverloadInstr(command.argumentList.length, command)) + control.push(...handleSequence(command.argumentList.map(a => instr.resTypeInstr(a, command)))) + control.push(instr.resTypeInstr(node.exprNameNode(command.thisOrSuper, command), command)) }, - Literal: ( - command: Literal, - _environment: Environment, - _control: Control, - stash: Stash, - ) => { - stash.push(command); + Literal: (command: Literal, _environment: Environment, _control: Control, stash: Stash) => { + stash.push(command) }, - Void: ( - command: Void, - _environment: Environment, - _control: Control, - stash: Stash, - ) => { - stash.push(command); + Void: (command: Void, _environment: Environment, _control: Control, stash: Stash) => { + stash.push(command) }, ExpressionName: ( command: ExpressionName, _environment: Environment, control: Control, - _stash: Stash, + _stash: Stash ) => { - control.push(instr.derefInstr(command)); - control.push(instr.evalVarInstr(command.name, command)); + control.push(instr.derefInstr(command)) + control.push(instr.evalVarInstr(command.name, command)) }, BinaryExpression: ( @@ -408,128 +406,128 @@ const cmdEvaluators: { [type: string]: CmdEvaluator } = { control: Control, _stash: Stash ) => { - control.push(instr.binOpInstr(command.operator, command)); - control.push(command.right); - control.push(command.left); + control.push(instr.binOpInstr(command.operator, command)) + control.push(command.right) + control.push(command.left) }, [InstrType.POP]: ( _command: Instr, _environment: Environment, _control: Control, - stash: Stash, + stash: Stash ) => { - stash.pop(); + stash.pop() }, - + [InstrType.ASSIGNMENT]: ( _command: AssmtInstr, _environment: Environment, _control: Control, - stash: Stash, + stash: Stash ) => { // value is popped before variable becuase value is evaluated later than variable. - const value = stash.pop()! as Literal | Object; - const variable = stash.pop()! as Variable; + const value = stash.pop()! as Literal | Object + const variable = stash.pop()! as Variable // Variable can store variable now. - variable.value.kind === StructType.VARIABLE ? variable.value.value = value : variable.value = value; - stash.push(value); + variable.value.kind === StructType.VARIABLE + ? (variable.value.value = value) + : (variable.value = value) + stash.push(value) }, [InstrType.BINARY_OP]: ( command: BinOpInstr, _environment: Environment, _control: Control, - stash: Stash, + stash: Stash ) => { - const right = stash.pop()! as Literal; - const left = stash.pop()! as Literal; - stash.push(evaluateBinaryExpression(command.symbol, left, right)); + const right = stash.pop()! as Literal + const left = stash.pop()! as Literal + stash.push(evaluateBinaryExpression(command.symbol, left, right)) }, [InstrType.INVOCATION]: ( command: InvInstr, environment: Environment, control: Control, - stash: Stash, - ) => { + stash: Stash + ) => { // Save current environment to be restored after method returns. - control.push(instr.envInstr(environment.current, command.srcNode)); + control.push(instr.envInstr(environment.current, command.srcNode)) // Mark end of method in case method returns halfway. - control.push(instr.markerInstr(command.srcNode)); + control.push(instr.markerInstr(command.srcNode)) // Retrieve method/constructor to be invoked and args in reversed order. - const args: (Literal | Object)[] = []; + const args: (Literal | Object)[] = [] for (let i = 0; i < command.arity; i++) { - args.push(stash.pop()! as Literal | Object); + args.push(stash.pop()! as Literal | Object) } - args.reverse(); - const closure: Closure = stash.pop()! as Closure; + args.reverse() + const closure: Closure = stash.pop()! as Closure - const params: FormalParameter[] = closure.mtdOrCon.kind === "MethodDeclaration" - ? cloneDeep(closure.mtdOrCon.methodHeader.formalParameterList) - : cloneDeep(closure.mtdOrCon.constructorDeclarator.formalParameterList); + const params: FormalParameter[] = + closure.mtdOrCon.kind === 'MethodDeclaration' + ? cloneDeep(closure.mtdOrCon.methodHeader.formalParameterList) + : cloneDeep(closure.mtdOrCon.constructorDeclarator.formalParameterList) // Extend env from global frame. - const mtdOrConDescriptor = getDescriptor(closure.mtdOrCon); - environment.extendEnv(environment.global, mtdOrConDescriptor); + const mtdOrConDescriptor = getDescriptor(closure.mtdOrCon) + environment.extendEnv(environment.global, mtdOrConDescriptor) - const isInstanceMtdOrCon = args.length == params.length + 1; + const isInstanceMtdOrCon = args.length == params.length + 1 if (isInstanceMtdOrCon) { // Append implicit FormalParameter and arg super if needed. if (closure.env.parent.name !== GLOBAL_FRAME) { - params.unshift( - { - kind: "FormalParameter", - unannType: closure.env.parent.name, - identifier: SUPER_KEYWORD, - }, - ); - args.unshift(args[0]); + params.unshift({ + kind: 'FormalParameter', + unannType: closure.env.parent.name, + identifier: SUPER_KEYWORD + }) + args.unshift(args[0]) } // Append implicit FormalParameter this. - params.unshift( - { - kind: "FormalParameter", - unannType: closure.env.name, - identifier: THIS_KEYWORD, - }, - ); + params.unshift({ + kind: 'FormalParameter', + unannType: closure.env.name, + identifier: THIS_KEYWORD + }) } // Bind arguments to corresponding FormalParameters. for (let i = 0; i < args.length; i++) { - environment.defineVariable(params[i].identifier, params[i].unannType, args[i]); + environment.defineVariable(params[i].identifier, params[i].unannType, args[i]) } // Push method/constructor body. - const body = closure.mtdOrCon.kind === "MethodDeclaration" - ? closure.mtdOrCon.methodBody - : closure.mtdOrCon.constructorBody; - control.push(body); + const body = + closure.mtdOrCon.kind === 'MethodDeclaration' + ? closure.mtdOrCon.methodBody + : closure.mtdOrCon.constructorBody + control.push(body) }, [InstrType.ENV]: ( command: EnvInstr, environment: Environment, _control: Control, - _stash: Stash, + _stash: Stash ) => { - environment.restoreEnv(command.env); + environment.restoreEnv(command.env) }, [InstrType.RESET]: ( command: ResetInstr, _environment: Environment, control: Control, - _stash: Stash, + _stash: Stash ) => { // Continue popping ControlItem until Marker is found. - const next: ControlItem | undefined = control.pop(); + const next: ControlItem | undefined = control.pop() if (next && (isNode(next) || next.instrType !== InstrType.MARKER)) { - control.push(instr.resetInstr(command.srcNode)); + control.push(instr.resetInstr(command.srcNode)) } }, @@ -537,57 +535,65 @@ const cmdEvaluators: { [type: string]: CmdEvaluator } = { command: EvalVarInstr, environment: Environment, control: Control, - stash: Stash, + stash: Stash ) => { if (isQualified(command.symbol)) { - const nameParts = command.symbol.split("."); - const name = nameParts.splice(0, nameParts.length - 1).join("."); - const identifier = nameParts[nameParts.length - 1]; - control.push(instr.resInstr(identifier, command.srcNode)); - control.push(instr.evalVarInstr(name, command.srcNode)); - return; + const nameParts = command.symbol.split('.') + const name = nameParts.splice(0, nameParts.length - 1).join('.') + const identifier = nameParts[nameParts.length - 1] + control.push(instr.resInstr(identifier, command.srcNode)) + control.push(instr.evalVarInstr(name, command.srcNode)) + return } - stash.push(environment.getVariable(command.symbol)); + stash.push(environment.getVariable(command.symbol)) }, [InstrType.RES]: ( command: ResInstr, environment: Environment, _control: Control, - stash: Stash, + stash: Stash ) => { - const varOrClass = stash.pop()! as Variable | Class; - + const varOrClass = stash.pop()! as Variable | Class + // E.g., test.x when test is a variable. if (varOrClass.kind === StructType.VARIABLE) { // Name resolution is based on the target type, not target obj. - let classOfField = environment.getClass(varOrClass.type); + let classOfField = environment.getClass(varOrClass.type) // Check recursively if static/instance field. - let staticField = classOfField.staticFields.find(f => f.variableDeclaratorList[0].variableDeclaratorId === command.name); - let instanceField = classOfField.instanceFields.find(f => f.variableDeclaratorList[0].variableDeclaratorId === command.name); + let staticField = classOfField.staticFields.find( + f => f.variableDeclaratorList[0].variableDeclaratorId === command.name + ) + let instanceField = classOfField.instanceFields.find( + f => f.variableDeclaratorList[0].variableDeclaratorId === command.name + ) while (!staticField && !instanceField && classOfField.superclass) { - classOfField = classOfField.superclass; - staticField = classOfField.staticFields.find(f => f.variableDeclaratorList[0].variableDeclaratorId === command.name); - instanceField = classOfField.instanceFields.find(f => f.variableDeclaratorList[0].variableDeclaratorId === command.name); + classOfField = classOfField.superclass + staticField = classOfField.staticFields.find( + f => f.variableDeclaratorList[0].variableDeclaratorId === command.name + ) + instanceField = classOfField.instanceFields.find( + f => f.variableDeclaratorList[0].variableDeclaratorId === command.name + ) } // E.g., test.x where x is a static field. if (staticField) { - const variable = classOfField.frame.getVariable(command.name); - stash.push(variable); - return; + const variable = classOfField.frame.getVariable(command.name) + stash.push(variable) + return } // E.g., test.x where x is an instance field. if (instanceField) { // Throw NullPointerException if instance field but target is null. if (isNull(varOrClass.value as Literal | Object)) { - throw new errors.NullPointerException(); + throw new errors.NullPointerException() } - const obj = varOrClass.value as Object; - let objFrameClass = obj.class; + const obj = varOrClass.value as Object + let objFrameClass = obj.class let objFrame = obj.frame // Name resolution is based on the target type, not target obj. while (objFrameClass.frame.name !== classOfField.frame.name && objFrameClass.superclass) { @@ -595,250 +601,257 @@ const cmdEvaluators: { [type: string]: CmdEvaluator } = { // but each frame does not hv its corresponding class, // so lookup needs to be done from its class wrt class hierarchy. // TODO maybe encode class in frame name? - objFrameClass = objFrameClass.superclass; - objFrame = objFrame.parent; + objFrameClass = objFrameClass.superclass + objFrame = objFrame.parent } const variable = objFrame.getVariable(command.name) - stash.push(variable); - return; + stash.push(variable) + return } throw new errors.UndeclaredVariableError(command.name) } // E.g., Test.x where Test is a class. - const variable = varOrClass.frame.getVariable(command.name); - stash.push(variable); + const variable = varOrClass.frame.getVariable(command.name) + stash.push(variable) }, [InstrType.DEREF]: ( _command: DerefInstr, _environment: Environment, _control: Control, - stash: Stash, + stash: Stash ) => { - const varOrClass = stash.pop()! as Variable | Class; - const value = varOrClass.kind === StructType.CLASS - ? varOrClass - // Variable can store variable now. - : varOrClass.value.kind === StructType.VARIABLE - ? varOrClass.value.value as Literal | Object - : varOrClass.value as Literal | Object; - stash.push(value); + const varOrClass = stash.pop()! as Variable | Class + const value = + varOrClass.kind === StructType.CLASS + ? varOrClass + : // Variable can store variable now. + varOrClass.value.kind === StructType.VARIABLE + ? (varOrClass.value.value as Literal | Object) + : (varOrClass.value as Literal | Object) + stash.push(value) }, [InstrType.NEW]: ( command: NewInstr, environment: Environment, _control: Control, - stash: Stash, + stash: Stash ) => { // To be restore after extending curr env to declare instance fields in obj frame. - const currEnv = environment.current; + const currEnv = environment.current // Get class hierarchy. - const objClass = command.c; - let currClass: Class = objClass; - const classHierachy: Class[] = [currClass]; + const objClass = command.c + let currClass: Class = objClass + const classHierachy: Class[] = [currClass] while (currClass.superclass) { - classHierachy.unshift(currClass.superclass); - currClass = currClass.superclass; - }; - + classHierachy.unshift(currClass.superclass) + currClass = currClass.superclass + } + // Create obj with both declared and inherited fields. - const obj: Object = environment.createObj(objClass); + const obj: Object = environment.createObj(objClass) classHierachy.forEach((c, i) => { - // A frame is alr created in createObj() and does not extend from any env, + // A frame is alr created in createObj() and does not extend from any env, // only subsequent frames need to extend from prev frame. - if (i > 0) environment.extendEnv(environment.current, c.frame.name); + if (i > 0) environment.extendEnv(environment.current, c.frame.name) // Declare instance fields. c.instanceFields.forEach(i => { - const id = i.variableDeclaratorList[0].variableDeclaratorId; - const type = i.fieldType; - environment.declareVariable(id, type); - }); - + const id = i.variableDeclaratorList[0].variableDeclaratorId + const type = i.fieldType + environment.declareVariable(id, type) + }) + // Set alias to static fields. c.staticFields.forEach(i => { - const id = i.variableDeclaratorList[0].variableDeclaratorId; - const type = i.fieldType; - const variable = c.frame.getVariable(id); - environment.defineVariable(id, type, variable); - }); - }); + const id = i.variableDeclaratorList[0].variableDeclaratorId + const type = i.fieldType + const variable = c.frame.getVariable(id) + environment.defineVariable(id, type, variable) + }) + }) // Set obj to correct frame. - obj.frame = environment.current; + obj.frame = environment.current // Push obj on stash. - stash.push(obj); + stash.push(obj) // Restore env. - environment.restoreEnv(currEnv); + environment.restoreEnv(currEnv) }, [InstrType.RES_TYPE]: ( command: ResTypeInstr, environment: Environment, control: Control, - stash: Stash, + stash: Stash ) => { // TODO need to handle all type of arg expressions - const value: Expression | Class = command.value; + const value: Expression | Class = command.value // TODO cleaner way to do this? - let type: UnannType; - if (value.kind === "Literal") { - if (value.literalType.kind === "DecimalIntegerLiteral") { - type = "int"; - } else if (value.literalType.kind === "StringLiteral") { + let type: UnannType + if (value.kind === 'Literal') { + if (value.literalType.kind === 'DecimalIntegerLiteral') { + type = 'int' + } else if (value.literalType.kind === 'StringLiteral') { // TODO remove hardcoding - type = "String[]"; + type = 'String[]' } - } else if (value.kind === "ExpressionName") { + } else if (value.kind === 'ExpressionName') { if (isQualified(value.name)) { - const nameParts = value.name.split("."); + const nameParts = value.name.split('.') for (const namePart of nameParts.slice(1)) { - control.push(instr.resTypeContInstr(namePart, command.srcNode)); + control.push(instr.resTypeContInstr(namePart, command.srcNode)) } - control.push(instr.resTypeInstr(node.exprNameNode(nameParts[0], command.srcNode), command.srcNode)); - return; + control.push( + instr.resTypeInstr(node.exprNameNode(nameParts[0], command.srcNode), command.srcNode) + ) + return } - const v = environment.getName(value.name); - type = v.kind === StructType.VARIABLE ? v.type : v.frame.name; + const v = environment.getName(value.name) + type = v.kind === StructType.VARIABLE ? v.type : v.frame.name } else if (value.kind === StructType.CLASS) { - type = value.frame.name; + type = value.frame.name } - - stash.push(struct.typeStruct(type!)); + + stash.push(struct.typeStruct(type!)) }, [InstrType.RES_TYPE_CONT]: ( command: ResTypeContInstr, environment: Environment, _control: Control, - stash: Stash, + stash: Stash ) => { - const typeToSearchIn = stash.pop()! as Type; + const typeToSearchIn = stash.pop()! as Type - let classToSearchIn: Class | undefined = environment.getClass(typeToSearchIn.type); - let type; + let classToSearchIn: Class | undefined = environment.getClass(typeToSearchIn.type) + let type do { // Check if instance field. for (const f of classToSearchIn.instanceFields) { if (command.name === f.variableDeclaratorList[0].variableDeclaratorId) { - type = f.fieldType; - break; + type = f.fieldType + break } } - if (type) break; + if (type) break // Check if static field. for (const f of classToSearchIn.staticFields) { if (command.name === f.variableDeclaratorList[0].variableDeclaratorId) { - type = f.fieldType; - break; + type = f.fieldType + break } } - if (type) break; + if (type) break // Check if superclass instance/static field. - classToSearchIn = classToSearchIn.superclass; + classToSearchIn = classToSearchIn.superclass } while (classToSearchIn) if (!type) { - throw new errors.UndeclaredVariableError(command.name); + throw new errors.UndeclaredVariableError(command.name) } - - stash.push(struct.typeStruct(type)); + + stash.push(struct.typeStruct(type)) }, [InstrType.RES_OVERLOAD]: ( command: ResOverloadInstr, environment: Environment, control: Control, - stash: Stash, + stash: Stash ) => { // Retrieve arg types in reversed order for method overloading resolution. - const argTypes: Type[] = []; + const argTypes: Type[] = [] for (let i = 0; i < command.arity; i++) { - argTypes.push(stash.pop()! as Type); + argTypes.push(stash.pop()! as Type) } - argTypes.reverse(); + argTypes.reverse() // Retrieve target type for method overloading resolution. - const targetType: Type = stash.pop()! as Type; - const classToSearchIn: Class = environment.getClass(targetType.type); + const targetType: Type = stash.pop()! as Type + const classToSearchIn: Class = environment.getClass(targetType.type) // Method overloading resolution. - const classStore: EnvNode = environment.global; - const closure: Closure = resOverload(classToSearchIn, command.name, argTypes, classStore); - stash.push(closure); + const classStore: EnvNode = environment.global + const closure: Closure = resOverload(classToSearchIn, command.name, argTypes, classStore) + stash.push(closure) // Post-processing required if overload resolved method is instance method. if (isInstance(closure.mtdOrCon as MethodDeclaration)) { // Increment arity of InvInstr on control. - let n = 1; - while (control.peekN(n) && (isNode(control.peekN(n)!) || (control.peekN(n)! as Instr).instrType !== InstrType.INVOCATION)) { - n++; + let n = 1 + while ( + control.peekN(n) && + (isNode(control.peekN(n)!) || + (control.peekN(n)! as Instr).instrType !== InstrType.INVOCATION) + ) { + n++ } - (control.peekN(n)! as ResOverloadInstr).arity++; - }; + ;(control.peekN(n)! as ResOverloadInstr).arity++ + } }, [InstrType.RES_OVERRIDE]: ( _command: ResOverrideInstr, environment: Environment, _control: Control, - stash: Stash, + stash: Stash ) => { - const target = stash.pop()!; - const overloadResolvedClosure = stash.pop()! as Closure; + const target = stash.pop()! + const overloadResolvedClosure = stash.pop()! as Closure if (isStatic(overloadResolvedClosure.mtdOrCon as MethodDeclaration)) { // No method overriding resolution is required if resolved method is a static method. - stash.push(overloadResolvedClosure); - return; + stash.push(overloadResolvedClosure) + return } // Throw NullPointerException if method to be invoked is instance method // but instance is null. if (isNull(target)) { - throw new errors.NullPointerException(); + throw new errors.NullPointerException() } // Retrieve class to search in for method overriding resolution. - const classToSearchIn: Class = environment.getClass((target as Object).class.frame.name); + const classToSearchIn: Class = environment.getClass((target as Object).class.frame.name) // Method overriding resolution. - const overrideResolvedClosure: Closure = resOverride(classToSearchIn, overloadResolvedClosure); - stash.push(overrideResolvedClosure); + const overrideResolvedClosure: Closure = resOverride(classToSearchIn, overloadResolvedClosure) + stash.push(overrideResolvedClosure) // Push target as implicit FormalParameter this. - stash.push(target); + stash.push(target) }, [InstrType.RES_CON_OVERLOAD]: ( command: ResConOverloadInstr, environment: Environment, _control: Control, - stash: Stash, + stash: Stash ) => { // Retrieve arg types in reversed order for constructor resolution. - const argTypes: Type[] = []; + const argTypes: Type[] = [] for (let i = 0; i < command.arity; i++) { - argTypes.push(stash.pop()! as Type); + argTypes.push(stash.pop()! as Type) } - argTypes.reverse(); + argTypes.reverse() // Retrieve class to search in for method resolution. - const className: Identifier = (stash.pop()! as Type).type; - const classToSearchIn: Class = environment.getClass(className); + const className: Identifier = (stash.pop()! as Type).type + const classToSearchIn: Class = environment.getClass(className) // Constructor overloading resolution. - const closure: Closure = resConOverload(classToSearchIn, className, argTypes); - stash.push(closure); + const closure: Closure = resConOverload(classToSearchIn, className, argTypes) + stash.push(closure) // No post-processing required for constructor. - }, -}; + } +} diff --git a/src/ec-evaluator/nodeCreator.ts b/src/ec-evaluator/nodeCreator.ts index fc5be47..05d9c3c 100644 --- a/src/ec-evaluator/nodeCreator.ts +++ b/src/ec-evaluator/nodeCreator.ts @@ -1,4 +1,4 @@ -import { Node } from "../ast/types/ast"; +import { Node } from '../ast/types/ast' import { Assignment, ExplicitConstructorInvocation, @@ -11,132 +11,132 @@ import { MethodInvocation, ReturnStatement, VariableDeclarator, - VariableDeclaratorId, -} from "../ast/types/blocks-and-statements"; + VariableDeclaratorId +} from '../ast/types/blocks-and-statements' import { ClassDeclaration, ConstructorBody, ConstructorDeclaration, ConstructorDeclarator, - UnannType, -} from "../ast/types/classes"; -import { OBJECT_CLASS, SUPER_KEYWORD, THIS_KEYWORD } from "./constants"; + UnannType +} from '../ast/types/classes' +import { OBJECT_CLASS, SUPER_KEYWORD, THIS_KEYWORD } from './constants' export const localVarDeclNoInitNode = ( localVariableType: UnannType, variableDeclaratorId: VariableDeclaratorId, - srcNode: Node, + srcNode: Node ): LocalVariableDeclarationStatement => ({ - kind: "LocalVariableDeclarationStatement", + kind: 'LocalVariableDeclarationStatement', localVariableType, variableDeclaratorList: [ { - kind: "VariableDeclarator", - variableDeclaratorId, - } as VariableDeclarator, + kind: 'VariableDeclarator', + variableDeclaratorId + } as VariableDeclarator ], - location: srcNode.location, -}); + location: srcNode.location +}) export const expStmtAssmtNode = ( left: string, right: Expression, - srcNode: Node, + srcNode: Node ): ExpressionStatement => ({ - kind: "ExpressionStatement", + kind: 'ExpressionStatement', stmtExp: { - kind: "Assignment", + kind: 'Assignment', left: { - kind: "ExpressionName", + kind: 'ExpressionName', name: left, - location: srcNode.location, + location: srcNode.location } as LeftHandSide, - operator: "=", + operator: '=', right, - location: srcNode.location, + location: srcNode.location } as Assignment, - location: srcNode.location, -}); + location: srcNode.location +}) export const mainMtdInvExpStmtNode = (className: string): ExpressionStatement => ({ - kind: "ExpressionStatement", + kind: 'ExpressionStatement', stmtExp: { - kind: "MethodInvocation", + kind: 'MethodInvocation', identifier: `${className}.main`, argumentList: [ { - kind: "Literal", + kind: 'Literal', literalType: { - kind: "StringLiteral", - value: `[""]`, - }, - }, - ], - } as MethodInvocation, -}); + kind: 'StringLiteral', + value: `[""]` + } + } + ] + } as MethodInvocation +}) export const emptyReturnStmtNode = (srcNode: Node): ReturnStatement => ({ - kind: "ReturnStatement", + kind: 'ReturnStatement', exp: { - kind: "Void", - location: srcNode.location, + kind: 'Void', + location: srcNode.location }, - location: srcNode.location, -}); + location: srcNode.location +}) export const returnThisStmtNode = (srcNode: Node): ReturnStatement => ({ - kind: "ReturnStatement", + kind: 'ReturnStatement', exp: { - kind: "ExpressionName", + kind: 'ExpressionName', name: THIS_KEYWORD, - location: srcNode.location, + location: srcNode.location } as ExpressionName, - location: srcNode.location, -}); + location: srcNode.location +}) export const defaultConstructorDeclNode = ( className: string, - srcNode: Node, + srcNode: Node ): ConstructorDeclaration => ({ - kind: "ConstructorDeclaration", + kind: 'ConstructorDeclaration', constructorModifier: [], constructorDeclarator: { identifier: className, - formalParameterList: [], + formalParameterList: [] } as ConstructorDeclarator, constructorBody: { - kind: "Block", + kind: 'Block', blockStatements: [], - location: srcNode.location, + location: srcNode.location } as ConstructorBody, - location: srcNode.location, -}); + location: srcNode.location +}) export const nullLitNode = (srcNode: Node): Literal => ({ - kind: "Literal", + kind: 'Literal', literalType: { - kind: "NullLiteral", - value: "null", + kind: 'NullLiteral', + value: 'null' }, - location: srcNode.location, -}); + location: srcNode.location +}) export const exprNameNode = (name: string, srcNode: Node): ExpressionName => ({ - kind: "ExpressionName", + kind: 'ExpressionName', name, - location: srcNode.location, -}); + location: srcNode.location +}) export const expConInvNode = (srcNode: Node): ExplicitConstructorInvocation => ({ - kind: "ExplicitConstructorInvocation", + kind: 'ExplicitConstructorInvocation', thisOrSuper: SUPER_KEYWORD, argumentList: [], - location: srcNode.location, -}); + location: srcNode.location +}) export const objClassDeclNode = (): ClassDeclaration => ({ - kind: "NormalClassDeclaration", + kind: 'NormalClassDeclaration', classModifier: [], typeIdentifier: OBJECT_CLASS, - classBody: [], -}); + classBody: [] +}) diff --git a/src/ec-evaluator/structCreator.ts b/src/ec-evaluator/structCreator.ts index f5493d4..1943ea4 100644 --- a/src/ec-evaluator/structCreator.ts +++ b/src/ec-evaluator/structCreator.ts @@ -4,55 +4,36 @@ import { MethodDeclaration, NormalClassDeclaration, UnannType -} from "../ast/types/classes"; -import { EnvNode } from "./components"; -import { - Name, - StructType, - Type, - VarValue, - Variable, - Symbol, - Object, - Class, - Closure -} from "./types"; +} from '../ast/types/classes' +import { EnvNode } from './components' +import { Name, StructType, Type, VarValue, Variable, Symbol, Object, Class, Closure } from './types' -export const varStruct = ( - type: UnannType, - name: Name, - value: VarValue, -): Variable => ({ +export const varStruct = (type: UnannType, name: Name, value: VarValue): Variable => ({ kind: StructType.VARIABLE, type, name, - value, -}); + value +}) -export const symStruct = ( - value: string, -): Symbol => ({ +export const symStruct = (value: string): Symbol => ({ kind: StructType.SYMBOL, - value, -}); + value +}) -export const objStruct = ( - frame: EnvNode, - c: Class, -): Object => ({ +export const objStruct = (frame: EnvNode, c: Class): Object => ({ kind: StructType.OBJECT, frame, - class: c, -}); + class: c +}) export const closureStruct = ( mtdOrCon: MethodDeclaration | ConstructorDeclaration, - env: EnvNode, + env: EnvNode ): Closure => ({ kind: StructType.CLOSURE, mtdOrCon, - env, -}); + env +}) export const classStruct = ( frame: EnvNode, @@ -62,7 +43,7 @@ export const classStruct = ( instanceMethods: MethodDeclaration[], staticFields: FieldDeclaration[], staticMethods: MethodDeclaration[], - superclass?: Class, + superclass?: Class ): Class => ({ kind: StructType.CLASS, frame, @@ -72,12 +53,10 @@ export const classStruct = ( instanceMethods, staticFields, staticMethods, - superclass, -}); + superclass +}) -export const typeStruct = ( - type: string, -): Type => ({ +export const typeStruct = (type: string): Type => ({ kind: StructType.TYPE, - type, -}); + type +}) diff --git a/src/ec-evaluator/types.ts b/src/ec-evaluator/types.ts index 3b7eb17..9ce1b6d 100644 --- a/src/ec-evaluator/types.ts +++ b/src/ec-evaluator/types.ts @@ -1,24 +1,24 @@ -import { Node } from "../ast/types/ast"; -import { Expression, Literal, Void } from "../ast/types/blocks-and-statements"; +import { Node } from '../ast/types/ast' +import { Expression, Literal, Void } from '../ast/types/blocks-and-statements' import { ConstructorDeclaration, FieldDeclaration, MethodDeclaration, NormalClassDeclaration, - UnannType, -} from "../ast/types/classes"; -import { Control, EnvNode, Environment, Stash } from "./components"; -import { SourceError } from "./errors"; + UnannType +} from '../ast/types/classes' +import { Control, EnvNode, Environment, Stash } from './components' +import { SourceError } from './errors' export interface Context { - errors: SourceError[], + errors: SourceError[] - control: Control, - stash: Stash, - environment: Environment, + control: Control + stash: Stash + environment: Environment - totalSteps: number, -}; + totalSteps: number +} /** * Instructions @@ -39,28 +39,28 @@ export enum InstrType { RES_TYPE_CONT = 'ResTypeCont', RES_OVERLOAD = 'ResOverload', RES_OVERRIDE = 'ResOverride', - RES_CON_OVERLOAD = 'ResConOverload', + RES_CON_OVERLOAD = 'ResConOverload' } interface BaseInstr { - instrType: InstrType; - srcNode: Node; + instrType: InstrType + srcNode: Node } export interface AssmtInstr extends BaseInstr {} export interface BinOpInstr extends BaseInstr { - symbol: string; + symbol: string } export interface PopInstr extends BaseInstr {} export interface InvInstr extends BaseInstr { - arity: number; + arity: number } export interface EnvInstr extends BaseInstr { - env: EnvNode; + env: EnvNode } export interface MarkerInstr extends BaseInstr {} @@ -68,34 +68,34 @@ export interface MarkerInstr extends BaseInstr {} export interface ResetInstr extends BaseInstr {} export interface EvalVarInstr extends BaseInstr { - symbol: string; + symbol: string } export interface NewInstr extends BaseInstr { - c: Class; + c: Class } export interface ResTypeInstr extends BaseInstr { - value: Expression | Class; + value: Expression | Class } export interface ResTypeContInstr extends BaseInstr { - name: string; + name: string } export interface ResOverloadInstr extends BaseInstr { - name: string; - arity: number; + name: string + arity: number } export interface ResOverrideInstr extends BaseInstr {} export interface ResConOverloadInstr extends BaseInstr { - arity: number; + arity: number } export interface ResInstr extends BaseInstr { - name: string; + name: string } export interface DerefInstr extends BaseInstr {} @@ -115,87 +115,87 @@ export type Instr = | ResTypeInstr | ResTypeContInstr | ResOverloadInstr - | ResConOverloadInstr; + | ResConOverloadInstr /** * Components */ -export type ControlItem = Node | Instr; -export type StashItem = Primitive | Reference | Value | Void | Type; +export type ControlItem = Node | Instr +export type StashItem = Primitive | Reference | Value | Void | Type -export type Name = string; -export type Value = Variable | Closure | Class; +export type Name = string +export type Value = Variable | Closure | Class -export type VarValue = Primitive | Reference | Symbol | Variable; +export type VarValue = Primitive | Reference | Symbol | Variable -export type Primitive = Literal; -export type Reference = Object; +export type Primitive = Literal +export type Reference = Object /** * Structs */ export enum StructType { - VARIABLE = "Variable", - SYMBOL = "Symbol", - OBJECT = "Object", - CLOSURE = "Closure", - CLASS = "Class", - TYPE = "Type", + VARIABLE = 'Variable', + SYMBOL = 'Symbol', + OBJECT = 'Object', + CLOSURE = 'Closure', + CLASS = 'Class', + TYPE = 'Type' } export interface Variable { - kind: StructType.VARIABLE; - type: UnannType; - name: Name; - value: VarValue; + kind: StructType.VARIABLE + type: UnannType + name: Name + value: VarValue } export interface Symbol { - kind: StructType.SYMBOL; - value: string; + kind: StructType.SYMBOL + value: string } export interface Object { - kind: StructType.OBJECT; - frame: EnvNode; - class: Class; + kind: StructType.OBJECT + frame: EnvNode + class: Class } export interface Closure { - kind: StructType.CLOSURE; - mtdOrCon: MethodDeclaration | ConstructorDeclaration; - env: EnvNode; + kind: StructType.CLOSURE + mtdOrCon: MethodDeclaration | ConstructorDeclaration + env: EnvNode } export interface Class { - kind: StructType.CLASS; - frame: EnvNode; - classDecl: NormalClassDeclaration; - constructors: ConstructorDeclaration[]; - instanceFields: FieldDeclaration[]; - instanceMethods: MethodDeclaration[]; - staticFields: FieldDeclaration[]; - staticMethods: MethodDeclaration[]; - superclass?: Class; + kind: StructType.CLASS + frame: EnvNode + classDecl: NormalClassDeclaration + constructors: ConstructorDeclaration[] + instanceFields: FieldDeclaration[] + instanceMethods: MethodDeclaration[] + staticFields: FieldDeclaration[] + staticMethods: MethodDeclaration[] + superclass?: Class } export interface Type { - kind: StructType.TYPE; - type: UnannType; + kind: StructType.TYPE + type: UnannType } /** * Execution results */ export interface Error { - status: 'error'; - context: Context; + status: 'error' + context: Context } export interface Finished { - status: 'finished'; - context: Context; - value: Value; + status: 'finished' + context: Context + value: Value } -export type Result = Finished | Error; +export type Result = Finished | Error diff --git a/src/ec-evaluator/utils.ts b/src/ec-evaluator/utils.ts index ac73751..650a078 100644 --- a/src/ec-evaluator/utils.ts +++ b/src/ec-evaluator/utils.ts @@ -1,4 +1,4 @@ -import { Node } from "../ast/types/ast"; +import { Node } from '../ast/types/ast' import { BlockStatement, DecimalIntegerLiteral, @@ -6,27 +6,27 @@ import { ExpressionStatement, Literal, MethodInvocation, - ReturnStatement, -} from "../ast/types/blocks-and-statements"; + ReturnStatement +} from '../ast/types/blocks-and-statements' import { ConstructorDeclaration, FieldDeclaration, MethodDeclaration, NormalClassDeclaration, - UnannType, -} from "../ast/types/classes"; -import { EnvNode } from "./components"; -import { THIS_KEYWORD } from "./constants"; -import * as errors from "./errors"; + UnannType +} from '../ast/types/classes' +import { EnvNode } from './components' +import { THIS_KEYWORD } from './constants' +import * as errors from './errors' import { emptyReturnStmtNode, expConInvNode, expStmtAssmtNode, exprNameNode, nullLitNode, - returnThisStmtNode, -} from "./nodeCreator"; -import { ControlItem, Context, Instr, Class, Type, Closure, StashItem } from "./types"; + returnThisStmtNode +} from './nodeCreator' +import { ControlItem, Context, Instr, Class, Type, Closure, StashItem } from './types' /** * Components. @@ -45,7 +45,7 @@ export class Stack implements IStack { public push(...items: T[]): void { for (const item of items) { - this.storage.push(item); + this.storage.push(item) } } @@ -102,55 +102,59 @@ export const handleSequence = (seq: ControlItem[]): ControlItem[] => { * Errors */ export const handleRuntimeError = (context: Context, error: errors.RuntimeError) => { - context.errors.push(error); - throw error; + context.errors.push(error) + throw error } /** * Binary Expressions */ -export const evaluateBinaryExpression = (operator: string, left: Literal, right: Literal): Literal => { +export const evaluateBinaryExpression = ( + operator: string, + left: Literal, + right: Literal +): Literal => { switch (operator) { - case "+": + case '+': return { - kind: "Literal", + kind: 'Literal', literalType: { kind: left.literalType.kind, - value: String(Number(left.literalType.value) + Number(right.literalType.value)), - } as DecimalIntegerLiteral, - }; - case "-": + value: String(Number(left.literalType.value) + Number(right.literalType.value)) + } as DecimalIntegerLiteral + } + case '-': return { - kind: "Literal", + kind: 'Literal', literalType: { kind: left.literalType.kind, - value: String(Number(left.literalType.value) - Number(right.literalType.value)), - } as DecimalIntegerLiteral, - }; - case "*": + value: String(Number(left.literalType.value) - Number(right.literalType.value)) + } as DecimalIntegerLiteral + } + case '*': return { - kind: "Literal", + kind: 'Literal', literalType: { kind: left.literalType.kind, - value: String(Number(left.literalType.value) * Number(right.literalType.value)), - } as DecimalIntegerLiteral, - }; - case "/": + value: String(Number(left.literalType.value) * Number(right.literalType.value)) + } as DecimalIntegerLiteral + } + case '/': return { - kind: "Literal", + kind: 'Literal', literalType: { kind: left.literalType.kind, - value: String(Number(left.literalType.value) / Number(right.literalType.value)), - } as DecimalIntegerLiteral, - }; - default /* case "%" */: - return { - kind: "Literal", + value: String(Number(left.literalType.value) / Number(right.literalType.value)) + } as DecimalIntegerLiteral + } + default: + /* case "%" */ return { + kind: 'Literal', literalType: { kind: left.literalType.kind, - value: String(Number(left.literalType.value) % Number(right.literalType.value)), - } as DecimalIntegerLiteral, - }; + value: String(Number(left.literalType.value) % Number(right.literalType.value)) + } as DecimalIntegerLiteral + } } } @@ -158,374 +162,391 @@ export const evaluateBinaryExpression = (operator: string, left: Literal, right: * Default values. */ export const defaultValues = new Map([ - ["int", { - kind: "Literal", - literalType: { - kind: "DecimalIntegerLiteral", - value: "0", - }, - }], -]); + [ + 'int', + { + kind: 'Literal', + literalType: { + kind: 'DecimalIntegerLiteral', + value: '0' + } + } + ] +]) /** * Name */ export const getDescriptor = (mtdOrCon: MethodDeclaration | ConstructorDeclaration): string => { - return mtdOrCon.kind === "MethodDeclaration" - ? `${mtdOrCon.methodHeader.identifier}(${mtdOrCon.methodHeader.formalParameterList.map(p => p.unannType).join(",")})${mtdOrCon.methodHeader.result}` - : `${mtdOrCon.constructorDeclarator.identifier}(${mtdOrCon.constructorDeclarator.formalParameterList.map(p => p.unannType).join(",")})`; + return mtdOrCon.kind === 'MethodDeclaration' + ? `${mtdOrCon.methodHeader.identifier}(${mtdOrCon.methodHeader.formalParameterList.map(p => p.unannType).join(',')})${mtdOrCon.methodHeader.result}` + : `${mtdOrCon.constructorDeclarator.identifier}(${mtdOrCon.constructorDeclarator.formalParameterList.map(p => p.unannType).join(',')})` } export const isQualified = (name: string) => { - return name.includes("."); + return name.includes('.') } export const isSimple = (name: string) => { - return !isQualified(name); + return !isQualified(name) } /** * Class */ export const getInstanceFields = (c: NormalClassDeclaration): FieldDeclaration[] => { - return c.classBody.filter(m => m.kind === "FieldDeclaration" && isInstance(m)) as FieldDeclaration[]; + return c.classBody.filter( + m => m.kind === 'FieldDeclaration' && isInstance(m) + ) as FieldDeclaration[] } export const getInstanceMethods = (c: NormalClassDeclaration): MethodDeclaration[] => { - return c.classBody.filter(m => m.kind === "MethodDeclaration" && isInstance(m)) as MethodDeclaration[]; + return c.classBody.filter( + m => m.kind === 'MethodDeclaration' && isInstance(m) + ) as MethodDeclaration[] } export const getStaticFields = (c: NormalClassDeclaration): FieldDeclaration[] => { - return c.classBody.filter(m => m.kind === "FieldDeclaration" && isStatic(m)) as FieldDeclaration[]; + return c.classBody.filter(m => m.kind === 'FieldDeclaration' && isStatic(m)) as FieldDeclaration[] } export const getStaticMethods = (c: NormalClassDeclaration): MethodDeclaration[] => { - return c.classBody.filter(m => m.kind === "MethodDeclaration" && isStatic(m)) as MethodDeclaration[]; + return c.classBody.filter( + m => m.kind === 'MethodDeclaration' && isStatic(m) + ) as MethodDeclaration[] } export const getConstructors = (c: NormalClassDeclaration): ConstructorDeclaration[] => { - return c.classBody.filter(m => m.kind === "ConstructorDeclaration") as ConstructorDeclaration[]; + return c.classBody.filter(m => m.kind === 'ConstructorDeclaration') as ConstructorDeclaration[] } export const isStatic = (fieldOrMtd: FieldDeclaration | MethodDeclaration): boolean => { - return fieldOrMtd.kind === "FieldDeclaration" - ? fieldOrMtd.fieldModifier.includes("static") - : fieldOrMtd.methodModifier.includes("static"); + return fieldOrMtd.kind === 'FieldDeclaration' + ? fieldOrMtd.fieldModifier.includes('static') + : fieldOrMtd.methodModifier.includes('static') } export const isInstance = (fieldOrMtd: FieldDeclaration | MethodDeclaration): boolean => { - return !isStatic(fieldOrMtd); + return !isStatic(fieldOrMtd) } const convertFieldDeclToExpStmtAssmt = (fd: FieldDeclaration): ExpressionStatement => { - const left = `this.${fd.variableDeclaratorList[0].variableDeclaratorId}`; + const left = `this.${fd.variableDeclaratorList[0].variableDeclaratorId}` // Fields are always initialized to default value if initializer is absent. const right = (fd.variableDeclaratorList[0].variableInitializer || defaultValues.get(fd.fieldType) || - nullLitNode(fd)) as Expression; - return expStmtAssmtNode(left, right, fd); -}; + nullLitNode(fd)) as Expression + return expStmtAssmtNode(left, right, fd) +} export const makeMtdInvSimpleIdentifierQualified = (mtd: MethodDeclaration, qualifier: string) => { mtd.methodBody.blockStatements.forEach(blockStatement => { // MethodInvocation as ExpressionStatement - blockStatement.kind === "ExpressionStatement" && - blockStatement.stmtExp.kind === "MethodInvocation" && - isSimple(blockStatement.stmtExp.identifier) && - (blockStatement.stmtExp.identifier = `${qualifier}.${blockStatement.stmtExp.identifier}`); + blockStatement.kind === 'ExpressionStatement' && + blockStatement.stmtExp.kind === 'MethodInvocation' && + isSimple(blockStatement.stmtExp.identifier) && + (blockStatement.stmtExp.identifier = `${qualifier}.${blockStatement.stmtExp.identifier}`) // MethodInvocation as RHS of Assignment ExpressionStatement - blockStatement.kind === "ExpressionStatement" && - blockStatement.stmtExp.kind === "Assignment" && - blockStatement.stmtExp.right.kind === "MethodInvocation" && - isSimple(blockStatement.stmtExp.right.identifier) && - (blockStatement.stmtExp.right.identifier = `${qualifier}.${blockStatement.stmtExp.right.identifier}`); + blockStatement.kind === 'ExpressionStatement' && + blockStatement.stmtExp.kind === 'Assignment' && + blockStatement.stmtExp.right.kind === 'MethodInvocation' && + isSimple(blockStatement.stmtExp.right.identifier) && + (blockStatement.stmtExp.right.identifier = `${qualifier}.${blockStatement.stmtExp.right.identifier}`) // MethodInvocation as VariableInitializer of LocalVariableDeclarationStatement - blockStatement.kind === "LocalVariableDeclarationStatement" && - blockStatement.variableDeclaratorList[0].variableInitializer && - (blockStatement.variableDeclaratorList[0].variableInitializer as Expression).kind === "MethodInvocation" && - isSimple((blockStatement.variableDeclaratorList[0].variableInitializer as MethodInvocation).identifier) && - ((blockStatement.variableDeclaratorList[0].variableInitializer as MethodInvocation).identifier = `${qualifier}.${(blockStatement.variableDeclaratorList[0].variableInitializer as MethodInvocation).identifier}`); - }); + blockStatement.kind === 'LocalVariableDeclarationStatement' && + blockStatement.variableDeclaratorList[0].variableInitializer && + (blockStatement.variableDeclaratorList[0].variableInitializer as Expression).kind === + 'MethodInvocation' && + isSimple( + (blockStatement.variableDeclaratorList[0].variableInitializer as MethodInvocation) + .identifier + ) && + (( + blockStatement.variableDeclaratorList[0].variableInitializer as MethodInvocation + ).identifier = + `${qualifier}.${(blockStatement.variableDeclaratorList[0].variableInitializer as MethodInvocation).identifier}`) + }) } export const makeNonLocalVarNonParamSimpleNameQualified = ( mtdOrCon: MethodDeclaration | ConstructorDeclaration, qualifier: string ) => { - const headerOrDeclarator = mtdOrCon.kind === "MethodDeclaration" - ? mtdOrCon.methodHeader - : mtdOrCon.constructorDeclarator; - const params = headerOrDeclarator.formalParameterList.map(p => p.identifier); - const localVars = new Set(params); + const headerOrDeclarator = + mtdOrCon.kind === 'MethodDeclaration' ? mtdOrCon.methodHeader : mtdOrCon.constructorDeclarator + const params = headerOrDeclarator.formalParameterList.map(p => p.identifier) + const localVars = new Set(params) const makeSimpleNameQualifiedHelper = (exprOrBlkStmt: Expression | BlockStatement) => { - switch(exprOrBlkStmt.kind) { - case "ExpressionName": - const exprName = exprOrBlkStmt; - isSimple(exprName.name) && !localVars.has(exprName.name) && (exprName.name = `${qualifier}.${exprName.name}`); - break; - case "Assignment": - const asgn = exprOrBlkStmt; - makeSimpleNameQualifiedHelper(asgn.left); - makeSimpleNameQualifiedHelper(asgn.right); - break; - case "BinaryExpression": - const binExpr = exprOrBlkStmt; - makeSimpleNameQualifiedHelper(binExpr.left); - makeSimpleNameQualifiedHelper(binExpr.right); - break; - case "LocalVariableDeclarationStatement": - const localVarDecl = exprOrBlkStmt; - localVarDecl.variableDeclaratorList[0].variableInitializer - && makeSimpleNameQualifiedHelper(localVarDecl.variableDeclaratorList[0].variableInitializer as Expression) - break; - case "ExpressionStatement": - const exprStmt = exprOrBlkStmt; - exprStmt.stmtExp.kind === "Assignment" && makeSimpleNameQualifiedHelper(exprStmt.stmtExp) + switch (exprOrBlkStmt.kind) { + case 'ExpressionName': + const exprName = exprOrBlkStmt + isSimple(exprName.name) && + !localVars.has(exprName.name) && + (exprName.name = `${qualifier}.${exprName.name}`) + break + case 'Assignment': + const asgn = exprOrBlkStmt + makeSimpleNameQualifiedHelper(asgn.left) + makeSimpleNameQualifiedHelper(asgn.right) + break + case 'BinaryExpression': + const binExpr = exprOrBlkStmt + makeSimpleNameQualifiedHelper(binExpr.left) + makeSimpleNameQualifiedHelper(binExpr.right) + break + case 'LocalVariableDeclarationStatement': + const localVarDecl = exprOrBlkStmt + localVarDecl.variableDeclaratorList[0].variableInitializer && + makeSimpleNameQualifiedHelper( + localVarDecl.variableDeclaratorList[0].variableInitializer as Expression + ) + break + case 'ExpressionStatement': + const exprStmt = exprOrBlkStmt + exprStmt.stmtExp.kind === 'Assignment' && makeSimpleNameQualifiedHelper(exprStmt.stmtExp) default: } } - const body = mtdOrCon.kind === "MethodDeclaration" - ? mtdOrCon.methodBody - : mtdOrCon.constructorBody; + const body = + mtdOrCon.kind === 'MethodDeclaration' ? mtdOrCon.methodBody : mtdOrCon.constructorBody body.blockStatements.forEach(blockStatement => { // Local var should be added incrementally to ensure correct scoping. - blockStatement.kind === "LocalVariableDeclarationStatement" && - localVars.add(blockStatement.variableDeclaratorList[0].variableDeclaratorId); + blockStatement.kind === 'LocalVariableDeclarationStatement' && + localVars.add(blockStatement.variableDeclaratorList[0].variableDeclaratorId) - makeSimpleNameQualifiedHelper(blockStatement); - }); + makeSimpleNameQualifiedHelper(blockStatement) + }) } export const prependExpConInvIfNeeded = ( constructor: ConstructorDeclaration, - c: NormalClassDeclaration, + c: NormalClassDeclaration ): void => { - const conBodyBlockStmts = constructor.constructorBody.blockStatements; - if (c.sclass && !conBodyBlockStmts.some(s => s.kind === "ExplicitConstructorInvocation")) { - conBodyBlockStmts.unshift(expConInvNode(constructor)); + const conBodyBlockStmts = constructor.constructorBody.blockStatements + if (c.sclass && !conBodyBlockStmts.some(s => s.kind === 'ExplicitConstructorInvocation')) { + conBodyBlockStmts.unshift(expConInvNode(constructor)) } } export const prependInstanceFieldsInitIfNeeded = ( constructor: ConstructorDeclaration, - instanceFields: FieldDeclaration[], + instanceFields: FieldDeclaration[] ): void => { - const conBodyBlockStmts = constructor.constructorBody.blockStatements; - const isAltConInvAbsent = !conBodyBlockStmts.find(s => s.kind === "ExplicitConstructorInvocation" && s.thisOrSuper === THIS_KEYWORD); + const conBodyBlockStmts = constructor.constructorBody.blockStatements + const isAltConInvAbsent = !conBodyBlockStmts.find( + s => s.kind === 'ExplicitConstructorInvocation' && s.thisOrSuper === THIS_KEYWORD + ) if (isAltConInvAbsent) { - const expStmtAssmts = instanceFields.map(f => convertFieldDeclToExpStmtAssmt(f)); - conBodyBlockStmts.unshift(...expStmtAssmts); + const expStmtAssmts = instanceFields.map(f => convertFieldDeclToExpStmtAssmt(f)) + conBodyBlockStmts.unshift(...expStmtAssmts) } } -export const appendOrReplaceReturn = ( - constructor: ConstructorDeclaration, -): void => { - const conBodyBlockStmts: BlockStatement[] = constructor.constructorBody.blockStatements; +export const appendOrReplaceReturn = (constructor: ConstructorDeclaration): void => { + const conBodyBlockStmts: BlockStatement[] = constructor.constructorBody.blockStatements // TODO deep search - const returnStmt = conBodyBlockStmts.find(stmt => stmt.kind === "ReturnStatement" && stmt.exp.kind === "Void"); + const returnStmt = conBodyBlockStmts.find( + stmt => stmt.kind === 'ReturnStatement' && stmt.exp.kind === 'Void' + ) if (returnStmt) { // Replace empty ReturnStatement with ReturnStatement with this keyword. - (returnStmt as ReturnStatement).exp = exprNameNode(THIS_KEYWORD, constructor); + ;(returnStmt as ReturnStatement).exp = exprNameNode(THIS_KEYWORD, constructor) } else { // Add ReturnStatement with this keyword. - conBodyBlockStmts.push(returnThisStmtNode(constructor)); + conBodyBlockStmts.push(returnThisStmtNode(constructor)) } } -export const appendEmtpyReturn = ( - method: MethodDeclaration, -): void => { - const mtdBodyBlockStmts: BlockStatement[] = method.methodBody.blockStatements; +export const appendEmtpyReturn = (method: MethodDeclaration): void => { + const mtdBodyBlockStmts: BlockStatement[] = method.methodBody.blockStatements // TODO deep search - if (!mtdBodyBlockStmts.find(stmt => stmt.kind === "ReturnStatement")) { + if (!mtdBodyBlockStmts.find(stmt => stmt.kind === 'ReturnStatement')) { // Add empty ReturnStatement if absent. - mtdBodyBlockStmts.push(emptyReturnStmtNode(method)); + mtdBodyBlockStmts.push(emptyReturnStmtNode(method)) } } export const searchMainMtdClass = (classes: NormalClassDeclaration[]) => { return classes.find(c => - c.classBody.some(d => - d.kind === "MethodDeclaration" - && d.methodModifier.includes("public") - && d.methodModifier.includes("static") - && d.methodHeader.result === "void" - && d.methodHeader.identifier === "main" - && d.methodHeader.formalParameterList.length === 1 - && d.methodHeader.formalParameterList[0].unannType === "String[]" - && d.methodHeader.formalParameterList[0].identifier === "args" - ))?.typeIdentifier; + c.classBody.some( + d => + d.kind === 'MethodDeclaration' && + d.methodModifier.includes('public') && + d.methodModifier.includes('static') && + d.methodHeader.result === 'void' && + d.methodHeader.identifier === 'main' && + d.methodHeader.formalParameterList.length === 1 && + d.methodHeader.formalParameterList[0].unannType === 'String[]' && + d.methodHeader.formalParameterList[0].identifier === 'args' + ) + )?.typeIdentifier } /** * Method overloading and overriding resolution. */ -const isSubtype = ( - subtype: UnannType, - supertype: UnannType, - classStore: EnvNode, -): boolean => { +const isSubtype = (subtype: UnannType, supertype: UnannType, classStore: EnvNode): boolean => { // TODO handle primitive subtyping relation - if (subtype === "String[]" || subtype === "int") return true; + if (subtype === 'String[]' || subtype === 'int') return true - let isSubtype = false; - - let subclassSuperclass: Class | undefined = classStore.getClass(subtype).superclass; - const superclass = classStore.getClass(supertype); + let isSubtype = false + + let subclassSuperclass: Class | undefined = classStore.getClass(subtype).superclass + const superclass = classStore.getClass(supertype) while (subclassSuperclass) { if (subclassSuperclass === superclass) { - isSubtype = true; - break; + isSubtype = true + break } - subclassSuperclass = subclassSuperclass.superclass; + subclassSuperclass = subclassSuperclass.superclass } - - return isSubtype; + + return isSubtype } export const resOverload = ( classToSearchIn: Class, mtdName: string, argTypes: Type[], - classStore: EnvNode, + classStore: EnvNode ): Closure => { // Identify potentially applicable methods. - const appClosures: Closure[] = []; - let c: Class | undefined = classToSearchIn; + const appClosures: Closure[] = [] + let c: Class | undefined = classToSearchIn while (c) { for (const [closureName, closure] of c.frame.frame.entries()) { // Methods contains parantheses and must have return type. - if (closureName.includes(mtdName + "(") && closureName[closureName.length - 1] !== ")") { - const params = ((closure as Closure).mtdOrCon as MethodDeclaration).methodHeader.formalParameterList; - - if (argTypes.length != params.length) continue; - - let match = true; + if (closureName.includes(mtdName + '(') && closureName[closureName.length - 1] !== ')') { + const params = ((closure as Closure).mtdOrCon as MethodDeclaration).methodHeader + .formalParameterList + + if (argTypes.length != params.length) continue + + let match = true for (let i = 0; i < argTypes.length; i++) { - match &&= (argTypes[i].type === params[i].unannType - || isSubtype(argTypes[i].type, params[i].unannType, classStore)); - if (!match) break; // short circuit + match &&= + argTypes[i].type === params[i].unannType || + isSubtype(argTypes[i].type, params[i].unannType, classStore) + if (!match) break // short circuit } - - match && appClosures.push(closure as Closure); + + match && appClosures.push(closure as Closure) } } - if (appClosures.length > 0) break; + if (appClosures.length > 0) break // Search recursively. - c = c.superclass; + c = c.superclass } - + if (appClosures.length === 0) { - throw new errors.ResOverloadError(mtdName, argTypes); + throw new errors.ResOverloadError(mtdName, argTypes) } - + if (appClosures.length === 1) { - return appClosures[0]; + return appClosures[0] } - + // Choose most specific method. - const mostSpecClosuresByParam = new Set(); + const mostSpecClosuresByParam = new Set() for (let i = 0; i < argTypes.length; i++) { - let mostSpecClosureByParam = appClosures[0]; + let mostSpecClosureByParam = appClosures[0] for (const appClosure of appClosures) { - const mostSpecParams = (mostSpecClosureByParam.mtdOrCon as MethodDeclaration).methodHeader.formalParameterList; - const params = (appClosure.mtdOrCon as MethodDeclaration).methodHeader.formalParameterList; + const mostSpecParams = (mostSpecClosureByParam.mtdOrCon as MethodDeclaration).methodHeader + .formalParameterList + const params = (appClosure.mtdOrCon as MethodDeclaration).methodHeader.formalParameterList if (isSubtype(params[i].unannType, mostSpecParams[i].unannType, classStore)) { - mostSpecClosureByParam = appClosure; + mostSpecClosureByParam = appClosure } } - mostSpecClosuresByParam.add(mostSpecClosureByParam); + mostSpecClosuresByParam.add(mostSpecClosureByParam) } - const isAmbiguous = mostSpecClosuresByParam.size > 1; + const isAmbiguous = mostSpecClosuresByParam.size > 1 if (isAmbiguous) { - throw new errors.ResOverloadAmbiguousError(mtdName, argTypes); + throw new errors.ResOverloadAmbiguousError(mtdName, argTypes) } - return mostSpecClosuresByParam.values().next().value; -}; + return mostSpecClosuresByParam.values().next().value +} -export const resOverride = ( - classToSearchIn: Class, - overloadResolvedClosure: Closure, -): Closure => { - const overloadResolvedMtd = overloadResolvedClosure.mtdOrCon as MethodDeclaration; - const name = overloadResolvedMtd.methodHeader.identifier; - const overloadResolvedClosureParams = overloadResolvedMtd.methodHeader.formalParameterList; +export const resOverride = (classToSearchIn: Class, overloadResolvedClosure: Closure): Closure => { + const overloadResolvedMtd = overloadResolvedClosure.mtdOrCon as MethodDeclaration + const name = overloadResolvedMtd.methodHeader.identifier + const overloadResolvedClosureParams = overloadResolvedMtd.methodHeader.formalParameterList - const closures: Closure[] = []; + const closures: Closure[] = [] for (const [closureName, closure] of classToSearchIn.frame.frame.entries()) { // Methods contains parantheses and must have return type. - if (closureName.includes(name + "(") && closureName[closureName.length - 1] !== ")") { - closures.push(closure as Closure); + if (closureName.includes(name + '(') && closureName[closureName.length - 1] !== ')') { + closures.push(closure as Closure) } } - let overrideResolvedClosure = overloadResolvedClosure; + let overrideResolvedClosure = overloadResolvedClosure for (const closure of closures) { - const params = (closure.mtdOrCon as MethodDeclaration).methodHeader.formalParameterList; - - if (overloadResolvedClosureParams.length != params.length) continue; - - let match = true; + const params = (closure.mtdOrCon as MethodDeclaration).methodHeader.formalParameterList + + if (overloadResolvedClosureParams.length != params.length) continue + + let match = true for (let i = 0; i < overloadResolvedClosureParams.length; i++) { - match &&= (params[i].unannType === overloadResolvedClosureParams[i].unannType); - if (!match) break; // short circuit + match &&= params[i].unannType === overloadResolvedClosureParams[i].unannType + if (!match) break // short circuit } - + if (match) { - overrideResolvedClosure = closure; - break; + overrideResolvedClosure = closure + break } } - return overrideResolvedClosure; -}; + return overrideResolvedClosure +} export const resConOverload = ( classToSearchIn: Class, conName: string, argTypes: Type[] ): Closure => { - const closures: Closure[] = []; + const closures: Closure[] = [] for (const [closureName, closure] of classToSearchIn.frame.frame.entries()) { // Constructors contains parantheses and do not have return type. - if (closureName.includes(conName + "(") && closureName[closureName.length - 1] === ")") { - closures.push(closure as Closure); + if (closureName.includes(conName + '(') && closureName[closureName.length - 1] === ')') { + closures.push(closure as Closure) } } - let resolved: Closure | undefined; + let resolved: Closure | undefined for (const closure of closures) { - const params = (closure.mtdOrCon as ConstructorDeclaration).constructorDeclarator.formalParameterList; - - if (argTypes.length != params.length) continue; - - let match = true; + const params = (closure.mtdOrCon as ConstructorDeclaration).constructorDeclarator + .formalParameterList + + if (argTypes.length != params.length) continue + + let match = true for (let i = 0; i < argTypes.length; i++) { - match &&= (params[i].unannType === argTypes[i].type); - if (!match) break; // short circuit + match &&= params[i].unannType === argTypes[i].type + if (!match) break // short circuit } - + if (match) { - resolved = closure; - break; + resolved = closure + break } } if (!resolved) { - throw new errors.ResConOverloadError(conName, argTypes); + throw new errors.ResConOverloadError(conName, argTypes) } - return resolved; -}; + return resolved +} export const isNull = (stashItem: StashItem) => { - return stashItem.kind === "Literal" && stashItem.literalType.kind === "NullLiteral"; + return stashItem.kind === 'Literal' && stashItem.literalType.kind === 'NullLiteral' }