tom-weatherhead/thaw-grammar

View on GitHub
src/languages/smalltalk/smalltalk-grammar.ts

Summary

Maintainability
A
0 mins
Test Coverage
// tom-weatherhead/thaw-grammar/src/languages/smalltalk/smalltalk-grammar.ts

// 2021-10-31 : Note: For each language with value type IDOMValue, we can define this:

// interface IDOMValueConverter {
//     toInteger(value: IDOMValue): number | undefined;
//     toFloat(value: IDOMValue): number | undefined;
//     toStringX(value: IDOMValue): string | undefined; // 'toStringX' so we don't conflict with 'toString'
//
//     fromInteger(value: number): IDOMValue;
//     fromFloat(value: number): IDOMValue;
//     fromString(value: string): IDOMValue;
// }

// Then isInteger(value: IDOMValue) can be implemented as: typeof toInteger(value) !== 'undefined'

// ... and fromInteger(value: number) can be implemented as: new SmalltalkInteger(value)

// ****

import { GrammarSymbol, IToken, LexicalState, SemanticStackType } from 'thaw-interpreter-types';

import { ArgumentException, GrammarBase, GrammarException, Name } from 'thaw-interpreter-core';

import { BeginUsage } from '../../common/domain-object-model/begin-usage';

import { IfUsage } from '../../common/domain-object-model/if-usage';

import { CondUsage } from '../../common/domain-object-model/cond-usage';

import { FunctionDefinition } from '../../common/domain-object-model/function-definition';

import { LetStarUsage } from '../../common/domain-object-model/let-star-usage';

import { LetUsage } from '../../common/domain-object-model/let-usage';

import { WhileUsage } from '../../common/domain-object-model/while-usage';

import {
    ISmalltalkExpression,
    ISmalltalkFunctionDefinition,
    ISmalltalkValue,
    ISmalltalkVariable
} from './domain-object-model/interfaces/iexpression';

import { SmalltalkArray } from './domain-object-model/data-types/array';

// import { SmalltalkBeginUsage } from './domain-object-model/begin-usage';
import { SmalltalkCharacter } from './domain-object-model/data-types/character';
import { SmalltalkClass } from './domain-object-model/class';
// import { SmalltalkCondUsage } from './domain-object-model/cond-usage';
import { SmalltalkFloat } from './domain-object-model/data-types/float';
// import { SmalltalkFunctionDefinition } from './domain-object-model/function-definition';
// import { SmalltalkIfUsage } from './domain-object-model/if-usage';
import { SmalltalkInteger } from './domain-object-model/data-types/integer';
// import { SmalltalkLetStarUsage } from './domain-object-model/let-star-usage';
// import { SmalltalkLetUsage } from './domain-object-model/let-usage';
import { SmalltalkOperatorUsage } from './domain-object-model/operator-usage';
import { SmalltalkSetUsage } from './domain-object-model/set-usage';
import { SmalltalkString } from './domain-object-model/data-types/string';
import { SmalltalkSymbol } from './domain-object-model/data-types/symbol';
import { SmalltalkVariable } from './domain-object-model/variable';
// import { SmalltalkWhileUsage } from './domain-object-model/while-usage';

export class SmalltalkGrammar extends GrammarBase {
    // The Smalltalk grammar from Kamin (the book 'Programming Languages: An Interpreter-Based Approach')

    constructor() {
        super(GrammarSymbol.nonterminalStart);

        // This initial production needed to be added: Start -> Input EOF
        this.addProduction(GrammarSymbol.nonterminalStart, [
            GrammarSymbol.nonterminalInput,
            GrammarSymbol.terminalEOF
        ]);

        // Input -> ( BracketedInput )
        this.addProduction(GrammarSymbol.nonterminalInput, [
            GrammarSymbol.terminalLeftBracket,
            GrammarSymbol.nonterminalBracketedInput,
            GrammarSymbol.terminalRightBracket
        ]);

        // Input -> UnbracketedInput
        this.addProduction(GrammarSymbol.nonterminalInput, [
            GrammarSymbol.nonterminalUnbracketedInput
        ]);

        // BracketedInput -> BracketedExpression
        this.addProduction(GrammarSymbol.nonterminalBracketedInput, [
            GrammarSymbol.nonterminalBracketedExpression
        ]);

        // BracketedInput -> FunDef
        this.addProduction(GrammarSymbol.nonterminalBracketedInput, [
            GrammarSymbol.nonterminalFunDef
        ]);

        // - UnbracketedInput -> Value
        this.addProduction(GrammarSymbol.nonterminalUnbracketedInput, [
            GrammarSymbol.nonterminalValue
        ]);

        // - UnbracketedInput -> Variable
        this.addProduction(GrammarSymbol.nonterminalUnbracketedInput, [
            GrammarSymbol.nonterminalVariable
        ]);

        // FunDef -> define Function ArgList Expression
        this.addProduction(GrammarSymbol.nonterminalFunDef, [
            GrammarSymbol.terminalDefine,
            GrammarSymbol.nonterminalFunction,
            GrammarSymbol.nonterminalArgList,
            GrammarSymbol.nonterminalExpression,
            '#functionDefinition'
        ]);

        // Expression -> Value
        this.addProduction(GrammarSymbol.nonterminalExpression, [GrammarSymbol.nonterminalValue]);

        // Expression -> Variable
        this.addProduction(GrammarSymbol.nonterminalExpression, [
            GrammarSymbol.nonterminalVariable
        ]);

        // Expression -> ( BracketedExpression )
        this.addProduction(GrammarSymbol.nonterminalExpression, [
            GrammarSymbol.terminalLeftBracket,
            GrammarSymbol.nonterminalBracketedExpression,
            GrammarSymbol.terminalRightBracket
        ]);

        this.addProduction(GrammarSymbol.nonterminalArgList, [
            GrammarSymbol.terminalLeftBracket,
            GrammarSymbol.nonterminalVariableList,
            GrammarSymbol.terminalRightBracket
        ]);

        this.addProduction(GrammarSymbol.nonterminalVariableList, [
            GrammarSymbol.nonterminalVariable,
            GrammarSymbol.nonterminalVariableList,
            '#variableList'
        ]);

        this.addProduction(GrammarSymbol.nonterminalVariableList, [
            GrammarSymbol.Lambda,
            '#emptyVariableList'
        ]);

        this.addProduction(GrammarSymbol.nonterminalBracketedExpression, [
            GrammarSymbol.terminalIf,
            GrammarSymbol.nonterminalExpression,
            GrammarSymbol.nonterminalExpression,
            GrammarSymbol.nonterminalExpression,
            '#if'
        ]);

        this.addProduction(GrammarSymbol.nonterminalBracketedExpression, [
            GrammarSymbol.terminalWhile,
            GrammarSymbol.nonterminalExpression,
            GrammarSymbol.nonterminalExpression,
            '#while'
        ]);

        this.addProduction(GrammarSymbol.nonterminalBracketedExpression, [
            GrammarSymbol.terminalSet,
            GrammarSymbol.nonterminalVariable,
            GrammarSymbol.nonterminalExpression,
            '#set'
        ]);

        this.addProduction(GrammarSymbol.nonterminalBracketedExpression, [
            GrammarSymbol.terminalBegin,
            GrammarSymbol.nonterminalExpression,
            GrammarSymbol.nonterminalExpressionList,
            '#begin'
        ]);

        this.addProduction(GrammarSymbol.nonterminalBracketedExpression, [
            GrammarSymbol.nonterminalOptr,
            GrammarSymbol.nonterminalExpressionList,
            '#operatorUsage'
        ]);

        this.addProduction(GrammarSymbol.nonterminalExpressionList, [
            GrammarSymbol.nonterminalExpression,
            GrammarSymbol.nonterminalExpressionList,
            '#expressionList'
        ]);

        this.addProduction(GrammarSymbol.nonterminalExpressionList, [
            GrammarSymbol.Lambda,
            '#emptyExpressionList'
        ]);

        this.addProduction(GrammarSymbol.nonterminalOptr, [GrammarSymbol.nonterminalFunction]);

        this.addProduction(GrammarSymbol.nonterminalOptr, [GrammarSymbol.nonterminalValueOp]);

        this.addProduction(GrammarSymbol.nonterminalValue, [GrammarSymbol.terminalIntegerLiteral]);

        this.addProduction(GrammarSymbol.nonterminalValueOp, [GrammarSymbol.terminalPlus]);

        this.addProduction(GrammarSymbol.nonterminalValueOp, [GrammarSymbol.terminalMinus]);

        this.addProduction(GrammarSymbol.nonterminalValueOp, [GrammarSymbol.terminalMultiply]);

        this.addProduction(GrammarSymbol.nonterminalValueOp, [GrammarSymbol.terminalDivide]);

        this.addProduction(GrammarSymbol.nonterminalValueOp, [GrammarSymbol.terminalEquals]);

        this.addProduction(GrammarSymbol.nonterminalValueOp, [GrammarSymbol.terminalLessThan]);

        this.addProduction(GrammarSymbol.nonterminalValueOp, [GrammarSymbol.terminalGreaterThan]);

        this.addProduction(GrammarSymbol.nonterminalValueOp, [GrammarSymbol.terminalPrint]);

        this.addProduction(GrammarSymbol.nonterminalFunction, [GrammarSymbol.terminalID]);

        this.addProduction(GrammarSymbol.nonterminalVariable, [
            GrammarSymbol.terminalID,
            '#variable'
        ]);

        // cond

        this.addProduction(GrammarSymbol.nonterminalBracketedExpression, [
            GrammarSymbol.terminalCond,
            GrammarSymbol.terminalLeftBracket,
            GrammarSymbol.nonterminalExpression,
            GrammarSymbol.nonterminalExpression,
            GrammarSymbol.terminalRightBracket,
            GrammarSymbol.nonterminalExprPairList,
            '#condUsage'
        ]);
        this.addProduction(GrammarSymbol.nonterminalExprPairList, [
            GrammarSymbol.terminalLeftBracket,
            GrammarSymbol.nonterminalExpression,
            GrammarSymbol.nonterminalExpression,
            GrammarSymbol.terminalRightBracket,
            GrammarSymbol.nonterminalExprPairList,
            '#exprPairList'
        ]);
        this.addProduction(GrammarSymbol.nonterminalExprPairList, [
            GrammarSymbol.Lambda,
            '#emptyExprPairList'
        ]);

        // let and let*

        this.addProduction(GrammarSymbol.nonterminalBracketedExpression, [
            GrammarSymbol.nonterminalLetKeyword,
            GrammarSymbol.terminalLeftBracket,
            GrammarSymbol.nonterminalVarExprList,
            GrammarSymbol.terminalRightBracket,
            GrammarSymbol.nonterminalExpression,
            '#letUsage'
        ]);
        this.addProduction(GrammarSymbol.nonterminalLetKeyword, [GrammarSymbol.terminalLet]);
        this.addProduction(GrammarSymbol.nonterminalLetKeyword, [GrammarSymbol.terminalLetStar]);
        this.addProduction(GrammarSymbol.nonterminalVarExprList, [
            GrammarSymbol.terminalLeftBracket,
            GrammarSymbol.nonterminalVariable,
            GrammarSymbol.nonterminalExpression,
            GrammarSymbol.terminalRightBracket,
            GrammarSymbol.nonterminalVarExprList,
            '#varExprList'
        ]);
        this.addProduction(GrammarSymbol.nonterminalVarExprList, [
            GrammarSymbol.Lambda,
            '#emptyVarExprList'
        ]);

        // Smalltalk Productions

        this.addProduction(GrammarSymbol.nonterminalBracketedInput, [
            GrammarSymbol.terminalClass,
            GrammarSymbol.nonterminalClass,
            GrammarSymbol.nonterminalClass,
            GrammarSymbol.nonterminalInstVars, // Actually the class variables; see Exercise 10 on page 347.
            GrammarSymbol.nonterminalInstVars,
            GrammarSymbol.nonterminalMethodDef,
            GrammarSymbol.nonterminalMethodDefList,
            '#classDefinition'
        ]);

        this.addProduction(GrammarSymbol.nonterminalClass, [GrammarSymbol.terminalID]);

        this.addProduction(GrammarSymbol.nonterminalInstVars, [
            GrammarSymbol.terminalLeftBracket,
            GrammarSymbol.nonterminalVariableList,
            GrammarSymbol.terminalRightBracket
        ]);

        this.addProduction(GrammarSymbol.nonterminalMethodDefList, [
            GrammarSymbol.nonterminalMethodDef,
            GrammarSymbol.nonterminalMethodDefList,
            '#methodDefList'
        ]);

        this.addProduction(GrammarSymbol.nonterminalMethodDefList, [
            GrammarSymbol.Lambda,
            '#emptyMethodDefList'
        ]);

        this.addProduction(GrammarSymbol.nonterminalMethodDef, [
            GrammarSymbol.terminalLeftBracket,
            GrammarSymbol.nonterminalFunDef,
            GrammarSymbol.terminalRightBracket
        ]);

        // Productions.Add(new Production(Symbol.N_Value, new List<object>() { Symbol.N_Symbol }, 45));
        this.addProduction(GrammarSymbol.nonterminalValue, [GrammarSymbol.nonterminalSymbol]);

        // Productions.Add(new Production(Symbol.N_Symbol, new List<object>() { GrammarSymbol.terminalOctothorpe, GrammarSymbol.terminalID, "#symbol" }, 46));
        this.addProduction(GrammarSymbol.nonterminalSymbol, [
            GrammarSymbol.terminalOctothorpe,
            GrammarSymbol.terminalID,
            '#symbol'
        ]);

        // // This next production allows us to redefine built-in value ops (such as +) in classes.
        // Productions.Add(new Production(Symbol.N_FunDef, new List<object>() {
        //     GrammarSymbol.terminalLeftBracket,
        //     GrammarSymbol.terminalDefine,
        //     Symbol.N_ValueOp,
        //     Symbol.N_ArgList,
        //     Symbol.N_Expression,
        //     GrammarSymbol.terminalRightBracket, "#functionDefinition" }, 48));

        // Productions.Add(new Production(Symbol.N_Value, new List<object>() { GrammarSymbol.terminalFloatLiteral }, 49));
        this.addProduction(GrammarSymbol.nonterminalValue, [GrammarSymbol.terminalFloatLiteral]);

        // Productions.Add(new Production(Symbol.N_Value, new List<object>() { GrammarSymbol.terminalStringLiteral }, 50));
        this.addProduction(GrammarSymbol.nonterminalValue, [GrammarSymbol.terminalStringLiteral]);

        // Productions.Add(new Production(Symbol.N_ValueOp, new List<object>() { GrammarSymbol.terminalRandom }, 51));
        this.addProduction(GrammarSymbol.nonterminalValueOp, [GrammarSymbol.terminalRandom]);

        // Productions.Add(new Production(Symbol.N_ValueOp, new List<object>() { GrammarSymbol.terminalToString }, 52));
        // this.addProduction(GrammarSymbol.nonterminalValueOp, [GrammarSymbol.terminal...]);

        // Productions.Add(new Production(Symbol.N_ValueOp, new List<object>() { GrammarSymbol.terminalStringToSymbol }, 53));
        // this.addProduction(GrammarSymbol.nonterminalValueOp, [GrammarSymbol.terminal...]);

        // Productions.Add(new Production(Symbol.N_ValueOp, new List<object>() { GrammarSymbol.terminalPow }, 54));
        this.addProduction(GrammarSymbol.nonterminalValueOp, [GrammarSymbol.terminalPow]);

        // Productions.Add(new Production(Symbol.N_ValueOp, new List<object>() { GrammarSymbol.terminalExp }, 55));
        this.addProduction(GrammarSymbol.nonterminalValueOp, [GrammarSymbol.terminalExp]);

        // Productions.Add(new Production(Symbol.N_ValueOp, new List<object>() { GrammarSymbol.terminalLn }, 56));
        this.addProduction(GrammarSymbol.nonterminalValueOp, [GrammarSymbol.terminalLn]);

        // Productions.Add(new Production(Symbol.N_ValueOp, new List<object>() { GrammarSymbol.terminalSin }, 57));
        this.addProduction(GrammarSymbol.nonterminalValueOp, [GrammarSymbol.terminalSin]);

        // Productions.Add(new Production(Symbol.N_ValueOp, new List<object>() { GrammarSymbol.terminalCos }, 58));
        this.addProduction(GrammarSymbol.nonterminalValueOp, [GrammarSymbol.terminalCos]);

        // Productions.Add(new Production(Symbol.N_ValueOp, new List<object>() { GrammarSymbol.terminalTan }, 59));
        this.addProduction(GrammarSymbol.nonterminalValueOp, [GrammarSymbol.terminalTan]);

        // Productions.Add(new Production(Symbol.N_ValueOp, new List<object>() { GrammarSymbol.terminalFloor }, 60));
        // this.addProduction(GrammarSymbol.nonterminalValueOp, [GrammarSymbol.terminal...]);

        // Productions.Add(new Production(Symbol.N_ValueOp, new List<object>() { GrammarSymbol.terminalAtan2 }, 61));
        // this.addProduction(GrammarSymbol.nonterminalValueOp, [GrammarSymbol.terminal...]);

        // Productions.Add(new Production(Symbol.N_ValueOp, new List<object>() { GrammarSymbol.terminalThrow }, 62));
        // this.addProduction(GrammarSymbol.nonterminalValueOp, [GrammarSymbol.terminal...]);

        // Productions.Add(new Production(Symbol.N_ValueOp, new List<object>() { GrammarSymbol.terminalStringLessThan }, 63));
        // this.addProduction(GrammarSymbol.nonterminalValueOp, [GrammarSymbol.terminal...]);

        // Productions.Add(new Production(Symbol.N_ValueOp, new List<object>() { GrammarSymbol.terminalNumberPred }, 64));
        // this.addProduction(GrammarSymbol.nonterminalValueOp, [GrammarSymbol.terminal...]);

        // Productions.Add(new Production(Symbol.N_ValueOp, new List<object>() { GrammarSymbol.terminalSymbolPred }, 65));
        // this.addProduction(GrammarSymbol.nonterminalValueOp, [GrammarSymbol.terminal...]);

        // Productions.Add(new Production(Symbol.N_ValueOp, new List<object>() { GrammarSymbol.terminalStringPred }, 66));
        // this.addProduction(GrammarSymbol.nonterminalValueOp, [GrammarSymbol.terminal...]);

        // Productions.Add(new Production(Symbol.N_ValueOp, new List<object>() { GrammarSymbol.terminalObjectPred }, 67));
        // this.addProduction(GrammarSymbol.nonterminalValueOp, [GrammarSymbol.terminal...]);

        // Productions.Add(new Production(Symbol.N_ValueOp, new List<object>() { GrammarSymbol.terminalStrlen }, 68));
        // this.addProduction(GrammarSymbol.nonterminalValueOp, [GrammarSymbol.terminal...]);

        // Productions.Add(new Production(Symbol.N_ValueOp, new List<object>() { GrammarSymbol.terminalSubstr }, 69));
        // this.addProduction(GrammarSymbol.nonterminalValueOp, [GrammarSymbol.terminal...]);

        // Productions.Add(new Production(Symbol.N_ValueOp, new List<object>() { GrammarSymbol.terminalTypename }, 70));
        // this.addProduction(GrammarSymbol.nonterminalValueOp, [GrammarSymbol.terminal...]);

        // Productions.Add(new Production(Symbol.N_ValueOp, new List<object>() { GrammarSymbol.terminalHash }, 71));
        // this.addProduction(GrammarSymbol.nonterminalValueOp, [GrammarSymbol.terminal...]);

        // Productions.Add(new Production(Symbol.N_ValueOp, new List<object>() { GrammarSymbol.terminalReferenceEquals }, 72));
        // this.addProduction(GrammarSymbol.nonterminalValueOp, [GrammarSymbol.terminal...]);

        // Productions.Add(new Production(Symbol.N_ValueOp, new List<object>() { GrammarSymbol.terminalStrcat }, 73));
        // this.addProduction(GrammarSymbol.nonterminalValueOp, [GrammarSymbol.terminal...]);

        // Productions.Add(new Production(Symbol.N_ValueOp, new List<object>() { GrammarSymbol.terminalNewArray }, 74));
        // this.addProduction(GrammarSymbol.nonterminalValueOp, [GrammarSymbol.terminal...]);

        // Productions.Add(new Production(Symbol.N_ValueOp, new List<object>() { GrammarSymbol.terminalArrayLength }, 75));
        // this.addProduction(GrammarSymbol.nonterminalValueOp, [GrammarSymbol.terminal...]);

        // Productions.Add(new Production(Symbol.N_ValueOp, new List<object>() { GrammarSymbol.terminalArrayGet }, 76));
        // this.addProduction(GrammarSymbol.nonterminalValueOp, [GrammarSymbol.terminal...]);

        // Productions.Add(new Production(Symbol.N_ValueOp, new List<object>() { GrammarSymbol.terminalArraySet }, 77));
        // this.addProduction(GrammarSymbol.nonterminalValueOp, [GrammarSymbol.terminal...]);

        // Productions.Add(new Production(Symbol.N_Value, new List<object>() {
        //     GrammarSymbol.terminalOctothorpe, GrammarSymbol.terminalLeftBracket, Symbol.N_LiteralList, GrammarSymbol.terminalRightBracket, "#arrayLiteral" }, 78));

        // Productions.Add(new Production(Symbol.N_LiteralList, new List<object>() { Symbol.Lambda, "#emptyLiteralList" }, 79));

        // Productions.Add(new Production(Symbol.N_LiteralList, new List<object>() { Symbol.N_Value, Symbol.N_LiteralList, "#literalList" }, 80));

        // Productions.Add(new Production(Symbol.N_Value, new List<object>() { GrammarSymbol.terminalDollar, GrammarSymbol.terminalID, "#characterLiteral" }, 81));
        this.addProduction(GrammarSymbol.nonterminalValue, [
            GrammarSymbol.terminalDollar,
            GrammarSymbol.terminalID,
            '#characterLiteral'
        ]);

        // Productions.Add(new Production(Symbol.N_ValueOp, new List<object>() { GrammarSymbol.terminalCharPred }, 82));
        // this.addProduction(GrammarSymbol.nonterminalValueOp, [GrammarSymbol.terminalCharPred]);

        // Productions.Add(new Production(Symbol.N_ValueOp, new List<object>() { GrammarSymbol.terminalStringIndex }, 83));
        // this.addProduction(GrammarSymbol.nonterminalValueOp, [GrammarSymbol.terminalStringIndex]);

        // Productions.Add(new Production(Symbol.N_ValueOp, new List<object>() { GrammarSymbol.terminalArrayPred }, 84));
        // this.addProduction(GrammarSymbol.nonterminalValueOp, [GrammarSymbol.terminalArrayPred]);
    }

    public get languageName(): string {
        return 'Smalltalk';
    }

    private createLetUsage(
        letKeyword: string,
        varExprList: [ISmalltalkVariable, ISmalltalkExpression][],
        expression: ISmalltalkExpression
    ): ISmalltalkExpression {
        // throw new Error('createLetUsage() : Not yet implemented.');

        switch (letKeyword) {
            case 'let':
                return new LetUsage<ISmalltalkValue>(varExprList, expression);

            case 'let*':
                return new LetStarUsage<ISmalltalkValue>(varExprList, expression);

            default:
                throw new ArgumentException(
                    `SmalltalkGrammar.CreateLetUsage() : Unknown 'let' keyword '${letKeyword}'.`,
                    'letKeyword'
                );
        }
    }

    public executeSemanticAction(semanticStack: SemanticStackType, action: string): void {
        let name: Name;
        let functionName: Name;
        let superClassName: Name;
        let variable: ISmalltalkVariable;
        let variableList: ISmalltalkVariable[];
        let argumentList: ISmalltalkVariable[];
        let classVariableList: ISmalltalkVariable[];
        let body: ISmalltalkExpression;
        let expression: ISmalltalkExpression;
        let expression2: ISmalltalkExpression;
        let expression3: ISmalltalkExpression;
        let expressionList: ISmalltalkExpression[];
        let funDef: ISmalltalkFunctionDefinition;
        let funDefList: ISmalltalkFunctionDefinition[];
        let exprPairList: [ISmalltalkExpression, ISmalltalkExpression][];
        let varExprList: [ISmalltalkVariable, ISmalltalkExpression][];
        let literal: ISmalltalkValue;
        let literalList: ISmalltalkValue[];
        let array: SmalltalkArray;

        switch (action) {
            case '#functionDefinition':
                body = semanticStack.pop() as ISmalltalkExpression;
                argumentList = semanticStack.pop() as ISmalltalkVariable[];

                functionName = semanticStack.pop() as Name;
                semanticStack.push(
                    new FunctionDefinition<ISmalltalkValue>(functionName, argumentList, body)
                );
                break;

            case '#variableList':
                variableList = semanticStack.pop() as ISmalltalkVariable[];
                variable = semanticStack.pop() as ISmalltalkVariable;
                variableList.unshift(variable);
                semanticStack.push(variableList);
                break;

            case '#emptyVariableList':
                semanticStack.push([] as ISmalltalkVariable[]);
                break;

            // #if DEAD_CODE
            case '#if':
                expression3 = semanticStack.pop() as ISmalltalkExpression;
                expression2 = semanticStack.pop() as ISmalltalkExpression;
                expression = semanticStack.pop() as ISmalltalkExpression;
                // semanticStack.push(new SmalltalkIfUsage(expression, expression2, expression3));
                semanticStack.push(
                    new IfUsage<ISmalltalkValue>(expression, expression2, expression3)
                );
                break;
            // #endif

            case '#while':
                expression2 = semanticStack.pop() as ISmalltalkExpression;
                expression = semanticStack.pop() as ISmalltalkExpression;
                // semanticStack.push(new SmalltalkWhileUsage(expression, expression2));
                semanticStack.push(new WhileUsage<ISmalltalkValue>(expression, expression2));
                break;

            case '#set':
                expression = semanticStack.pop() as ISmalltalkExpression;
                variable = semanticStack.pop() as ISmalltalkVariable;
                semanticStack.push(new SmalltalkSetUsage(variable, expression));
                break;

            case '#begin':
                expressionList = semanticStack.pop() as ISmalltalkExpression[];
                expression = semanticStack.pop() as ISmalltalkExpression;
                // semanticStack.push(new SmalltalkBeginUsage(expression, expressionList));
                semanticStack.push(new BeginUsage<ISmalltalkValue>(expression, expressionList));
                break;

            case '#operatorUsage':
                expressionList = semanticStack.pop() as ISmalltalkExpression[];
                name = semanticStack.pop() as Name;
                semanticStack.push(new SmalltalkOperatorUsage(name, expressionList));
                break;

            case '#expressionList':
                expressionList = semanticStack.pop() as ISmalltalkExpression[];
                expression = semanticStack.pop() as ISmalltalkExpression;
                expressionList.unshift(expression);
                semanticStack.push(expressionList);
                break;

            case '#emptyExpressionList':
                semanticStack.push([] as ISmalltalkExpression[]);
                break;

            case '#variable':
                name = semanticStack.pop() as Name;
                semanticStack.push(new SmalltalkVariable(name.value, name.line, name.column));
                break;

            case '#methodDefList':
                funDefList = semanticStack.pop() as ISmalltalkFunctionDefinition[];
                funDef = semanticStack.pop() as ISmalltalkFunctionDefinition;
                funDefList.unshift(funDef);
                semanticStack.push(funDefList);
                break;

            case '#emptyMethodDefList':
                semanticStack.push([] as ISmalltalkFunctionDefinition[]);
                break;

            case '#classDefinition':
                funDefList = semanticStack.pop() as ISmalltalkFunctionDefinition[];
                funDef = semanticStack.pop() as ISmalltalkFunctionDefinition;
                funDefList.unshift(funDef);
                variableList = semanticStack.pop() as ISmalltalkVariable[];
                classVariableList = semanticStack.pop() as ISmalltalkVariable[];
                superClassName = semanticStack.pop() as Name;
                name = semanticStack.pop() as Name;
                semanticStack.push(
                    new SmalltalkClass(
                        name.value,
                        superClassName.value,
                        classVariableList,
                        variableList,
                        funDefList,
                        name.line,
                        name.column
                    )
                );
                break;

            case '#symbol':
                name = semanticStack.pop() as Name;
                semanticStack.push(new SmalltalkSymbol(name.value, name.line, name.column));
                break;

            case '#condUsage':
                exprPairList = semanticStack.pop() as [
                    ISmalltalkExpression,
                    ISmalltalkExpression
                ][];
                expression2 = semanticStack.pop() as ISmalltalkExpression;
                expression = semanticStack.pop() as ISmalltalkExpression;
                exprPairList.unshift([expression, expression2]);
                // semanticStack.push(new SmalltalkCondUsage(exprPairList));
                semanticStack.push(new CondUsage<ISmalltalkValue>(exprPairList));
                break;

            case '#exprPairList':
                exprPairList = semanticStack.pop() as [
                    ISmalltalkExpression,
                    ISmalltalkExpression
                ][];
                expression2 = semanticStack.pop() as ISmalltalkExpression;
                expression = semanticStack.pop() as ISmalltalkExpression;
                exprPairList.unshift([expression, expression2]);
                semanticStack.push(exprPairList);
                break;

            case '#emptyExprPairList':
                semanticStack.push([] as [ISmalltalkExpression, ISmalltalkExpression][]);
                break;

            case '#letUsage':
                expression = semanticStack.pop() as ISmalltalkExpression;
                varExprList = semanticStack.pop() as [ISmalltalkVariable, ISmalltalkExpression][];

                name = semanticStack.pop() as Name; // Either 'let' or 'let*'

                semanticStack.push(this.createLetUsage(name.value, varExprList, expression));
                break;

            case '#varExprList':
                varExprList = semanticStack.pop() as [ISmalltalkVariable, ISmalltalkExpression][];
                expression = semanticStack.pop() as ISmalltalkExpression;
                variable = semanticStack.pop() as ISmalltalkVariable;
                varExprList.unshift([variable, expression]);
                semanticStack.push(varExprList);
                break;

            case '#emptyVarExprList':
                semanticStack.push([] as [ISmalltalkVariable, ISmalltalkExpression][]);
                break;

            case '#emptyLiteralList':
                semanticStack.push([] as ISmalltalkValue[]);
                break;

            case '#literalList':
                literalList = semanticStack.pop() as ISmalltalkValue[];
                literal = semanticStack.pop() as ISmalltalkValue;
                literalList.unshift(literal);
                semanticStack.push(literalList);
                break;

            case '#arrayLiteral':
                literalList = semanticStack.pop() as ISmalltalkValue[];

                array = new SmalltalkArray(literalList.length);

                for (let i = 0; i < literalList.length; ++i) {
                    array.value[i] = literalList[i];
                }

                semanticStack.push(array);
                break;

            case '#characterLiteral':
                name = semanticStack.pop() as Name;
                semanticStack.push(new SmalltalkCharacter(name.value[0]));
                break;

            default:
                // base.ExecuteSemanticAction(semanticStack, action);
                // break;
                throw new GrammarException(`Smalltalk: Unrecognized semantic action: ${action}`);
        }
    }

    public override tokenToSymbol(token: IToken): GrammarSymbol {
        const tokenValueAsString: string = token.tokenValue as string;

        if (token.tokenType === LexicalState.tokenIdent) {
            // switch (token.tokenType) {
            //     case LexicalState.tokenIdent:
            switch (tokenValueAsString) {
                // TODO (when we replace the keyword 'if' with a method) :
                // (if x y z) -> (x y z)
                // (true x y) -> x
                // (false x y) -> y
                // case 'if': return GrammarSymbol.terminalID;

                // case 'list?':
                //     return GrammarSymbol.terminalListPred;
                // case '=>':
                //     return GrammarSymbol.terminalThickArrow;
                //case '>': return GrammarSymbol.terminalGreaterThan;
                case 'class':
                    return GrammarSymbol.terminalClass;
                case 'number?':
                    return GrammarSymbol.terminalNumberPred;
                case 'symbol?':
                    return GrammarSymbol.terminalSymbolPred;
                case 'string?':
                    return GrammarSymbol.terminalStringPred;
                case 'object?':
                    return GrammarSymbol.terminalObjectPred;
                // case 'random':
                //     return GrammarSymbol.terminalRandom;
                case 'tostring':
                    return GrammarSymbol.terminalToString;
                case 'stringtosymbol':
                    return GrammarSymbol.terminalStringToSymbol;
                case 'atan2':
                    return GrammarSymbol.terminalAtan2;
                case 'floor':
                    return GrammarSymbol.terminalFloor;
                case 'throw':
                    return GrammarSymbol.terminalThrow;
                case 'string<':
                    return GrammarSymbol.terminalStringLessThan;
                case 'strlen':
                    return GrammarSymbol.terminalStrlen;
                case 'substr':
                    return GrammarSymbol.terminalSubstr;
                case 'typename':
                    return GrammarSymbol.terminalTypename;
                case 'strcat':
                    return GrammarSymbol.terminalStrcat;
                case 'newarray':
                    return GrammarSymbol.terminalNewArray;
                case 'arraylength':
                    return GrammarSymbol.terminalArrayLength;
                case 'arrayget':
                    return GrammarSymbol.terminalArrayGet;
                case 'arrayset':
                    return GrammarSymbol.terminalArraySet;
                case 'array?':
                    return GrammarSymbol.terminalArrayPred;
                case 'char?':
                    return GrammarSymbol.terminalCharPred;
                case 'stridx':
                    return GrammarSymbol.terminalStringIndex;
                default:
                    break;
            }
        }

        return super.tokenToSymbol(token);
    }

    public override pushTokenOntoSemanticStack(
        semanticStack: SemanticStackType,
        tokenAsSymbol: GrammarSymbol,
        token: IToken
    ): void {
        const value = token.tokenValue;

        switch (tokenAsSymbol) {
            case GrammarSymbol.terminalNumberPred:
            case GrammarSymbol.terminalSymbolPred:
            case GrammarSymbol.terminalStringPred:
            case GrammarSymbol.terminalObjectPred:
            case GrammarSymbol.terminalToString:
            case GrammarSymbol.terminalStringToSymbol:
            case GrammarSymbol.terminalAtan2:
            case GrammarSymbol.terminalFloor:
            case GrammarSymbol.terminalThrow:
            case GrammarSymbol.terminalStringLessThan:
            case GrammarSymbol.terminalStrlen:
            case GrammarSymbol.terminalSubstr:
            case GrammarSymbol.terminalTypename:
            case GrammarSymbol.terminalStrcat:
            case GrammarSymbol.terminalNewArray:
            case GrammarSymbol.terminalArrayLength:
            case GrammarSymbol.terminalArrayGet:
            case GrammarSymbol.terminalArraySet:
            case GrammarSymbol.terminalArrayPred:
            case GrammarSymbol.terminalCharPred:
            case GrammarSymbol.terminalStringIndex:
                semanticStack.push(new Name(value as string, token.line, token.column));
                // Or: semanticStack.push(new Name(value.toString(), token.line, token.column));
                // Or: semanticStack.push(new Name(`${value}`, token.line, token.column));
                break;

            case GrammarSymbol.terminalIntegerLiteral:
                semanticStack.push(new SmalltalkInteger(value, token.line, token.column));
                break;

            case GrammarSymbol.terminalFloatLiteral:
                semanticStack.push(new SmalltalkFloat(value, token.line, token.column));
                break;

            case GrammarSymbol.terminalStringLiteral:
                semanticStack.push(new SmalltalkString(value, token.line, token.column));
                break;

            case GrammarSymbol.terminalClass:
            case GrammarSymbol.terminalDollar:
            case GrammarSymbol.terminalOctothorpe:
                // For these terminals, push nothing onto the semantic stack.
                break;

            default:
                super.pushTokenOntoSemanticStack(semanticStack, tokenAsSymbol, token);
        }
    }
}