tom-weatherhead/thaw-grammar

View on GitHub
src/languages/smalltalk/domain-object-model/class.ts

Summary

Maintainability
A
35 mins
Test Coverage
// tom-weatherhead/thaw-grammar/src/languages/smalltalk/domain-object-model/class.ts

import { IParser, ITokenizer } from 'thaw-interpreter-types';

import { EnvironmentFrame } from '../../../common/domain-object-model/environment-frame';

import {
    ISmalltalkClass,
    ISmalltalkEnvironmentFrame,
    // ISmalltalkExpression,
    ISmalltalkFunctionDefinition,
    ISmalltalkGlobalInfo,
    ISmalltalkValue,
    ISmalltalkVariable
} from './interfaces/iexpression';
// import { ISmalltalkValue } from './interfaces/ivalue';

// import { SmalltalkEnvironmentFrame } from './environment-frame';

export class SmalltalkClass implements ISmalltalkClass {
    public superClass: ISmalltalkClass | undefined = undefined;
    public readonly exportedDict = new Map<string, ISmalltalkFunctionDefinition>();
    public readonly classVariableEnvFrame = new EnvironmentFrame<ISmalltalkValue>();
    // private static readonly reservedTypeNames = new HashSet<string>() { "int", "float", "symbol", "char", "string", "array" };

    constructor(
        public readonly className: string,
        public readonly superClassName: string | undefined,
        public readonly classVariableList: ISmalltalkVariable[],
        public readonly clRep: ISmalltalkVariable[],
        private exportedList: ISmalltalkFunctionDefinition[],
        public readonly line = 0,
        public readonly column = 0
    ) {}

    public findMethod(methodName: string): {
        method: ISmalltalkFunctionDefinition | undefined;
        classInWhichMethodWasFound: ISmalltalkClass | undefined;
    } {
        const method = this.exportedDict.get(methodName);

        if (typeof method !== 'undefined') {
            return { method, classInWhichMethodWasFound: this };
        } else if (typeof this.superClass !== 'undefined') {
            return this.superClass.findMethod(methodName);
        } else {
            return { method: undefined, classInWhichMethodWasFound: undefined };
        }
    }

    public findClassVariableValue(variable: ISmalltalkVariable): ISmalltalkValue | undefined {
        const value = this.classVariableEnvFrame.lookup(variable);

        if (typeof value !== 'undefined') {
            return value;
        } else if (typeof this.superClass !== 'undefined') {
            return this.superClass.findClassVariableValue(variable);
        } else {
            return undefined;
        }
    }

    public trySetClassVariableValue(variable: ISmalltalkVariable, value: ISmalltalkValue): boolean {
        if (this.classVariableEnvFrame.isDefined(variable)) {
            this.classVariableEnvFrame.dict.set(variable.name, value);
            // Or: this.classVariableEnvFrame.add(variable, value);

            return true;
        } else if (typeof this.superClass !== 'undefined') {
            return this.superClass.trySetClassVariableValue(variable, value);
        } else {
            return false;
        }
    }

    public addFunction(tokenizer: ITokenizer, parser: IParser, functionAsString: string): void {
        const funcDef = parser.parse(
            tokenizer.tokenize(functionAsString)
        ) as ISmalltalkFunctionDefinition;

        this.exportedDict.set(funcDef.functionName.value, funcDef);
    }

    /* eslint-disable no-unused-vars */
    // public evaluate(
    //     localEnvironment: ISmalltalkEnvironmentFrame | undefined,
    //     receiver: ISmalltalkValue, // | undefined,
    //     c: ISmalltalkClass | undefined,
    //     globalInfo: ISmalltalkGlobalInfo
    // ): ISmalltalkValue {
    /* eslint-disable @typescript-eslint/no-unused-vars */
    public evaluate(
        globalInfo: ISmalltalkGlobalInfo, // I.e. IGlobalInfo<ISmalltalkValue>
        localEnvironment: ISmalltalkEnvironmentFrame | undefined, // I.e. IEnvironmentFrame<ISmalltalkValue> | undefined
        options?: unknown
        /* eslint-enable @typescript-eslint/no-unused-vars */
    ): ISmalltalkValue {
        // if (reservedTypeNames.indexOf(this.className) >= 0) {
        //     throw new EvaluationException(
        //         string.Format("SmalltalkClass.Evaluate() : Cannot create a class named '{0}'; reserved typename", ClassName),
        //         LineNumber, ColumnNumber);
        // }

        globalInfo.classDict.set(this.className, this);

        // if (string.IsNullOrEmpty(SuperClassName)) {
        //     throw new EvaluationException(
        //         string.Format("SmalltalkClass.Evaluate() : Class {0} : SuperClassName is null or empty", ClassName),
        //         LineNumber, ColumnNumber);
        // }
        // TODO 2014/12/09 : Throw an exception if ClassName == SuperClassName
        // else

        if (typeof this.superClassName !== 'undefined') {
            const superClass = globalInfo.classDict.get(this.superClassName);

            if (typeof superClass === 'undefined') {
                // throw new EvaluationException(
                //     string.Format("SmalltalkClass.Evaluate() : Class {0} : Unknown SuperClass {1}", ClassName, SuperClassName),
                //     LineNumber, ColumnNumber);
                throw new Error(
                    `SmalltalkClass.Evaluate() : Class ${this.className} : Unknown SuperClass ${this.superClassName}`
                );
            }

            this.superClass = superClass;
            // this.clRep.AddRange(this.superClass.clRep);

            for (const v of this.superClass.clRep) {
                this.clRep.push(v);
            }
        }

        for (const exportedFuncDef of this.exportedList) {
            this.exportedDict.set(exportedFuncDef.functionName.value, exportedFuncDef);
        }

        for (const classVariable of this.classVariableList) {
            this.classVariableEnvFrame.add(classVariable, globalInfo.falseValue);
        }

        return globalInfo.falseValue;
    }
    /* eslint-enable no-unused-vars */
}