Исходник Информация My compiled language on TypeScript[JS]

shavuva3311

Участник
Автор темы
47
28
Приветствую!
Хотел бы поделиться своей разработкой, а именно своим компилируемым в байткод языком, написанным на TypeScript.
------------------------------------------------------------------------------------------------------------------------------------------------

Теоретически инструкции в нём всего три: VAR, CONST, IMPORT...
Остальные инструкции создаются вручную в JS-модулях, что даёт возможность легко расширять возможности данного языка.
Для понимания, он, конечно не прост, ведь синтаксисом схож с ассемблером Объясняется это тем, что я поленился написать лексер. Да и практического применения этому языку особого нету.



Как оно работает и примеры кода

-------------------------------------------------------------------------------------------------------------------------------------------------

Выведем в консоль Hello World



print.ml:
IMPORT PRINT ./modules/mlclib.js
PRINT HelloWorld

mlclib.js:
module.exports = {
    PRINT(value) {
        console.log(value);
    },
    TO_INT(str) {
        return Number(str);
    }
}



Для начала наш код необходимо скомпилировать.

1690760702268.png


В итоге получаем вот такой байткод

1690760768521.png


А вот уже полученный скомпилированный файл можно запустить.

1690760815653.png



--------------------------------------------------------------------------------------------------------------------------------

Сейчас покажу пример покрупнее - напишем простенькую игру-кликер с сохранениями.


game.ml:
IMPORT PRINT,CIN,GOTO,TO_INT ./modules/mlclib.js
IMPORT INC,DEC,MUL,DIV ./modules/math.js
IMPORT CONCAT,TO_STRING ./modules/string.js
IMPORT MORE ./modules/logic.js
IMPORT readFileSync,writeFileSync fs
VAR TEMP 0
VAR BALANCE 0
VAR UPGRADE 0
VAR UPGRADE_COST 0
PRINT Game_By_Shavuva3311
readFileSync balance.txt utf8 BALANCE
readFileSync upgrade.txt utf8 UPGRADE
readFileSync upgrade_cost.txt utf8 UPGRADE_COST
TO_INT variables->UPGRADE_COST UPGRADE_COST
TO_INT variables->UPGRADE UPGRADE
TO_INT variables->BALANCE BALANCE
CIN Press_enter
INC variables->BALANCE variables->UPGRADE BALANCE
TO_STRING variables->BALANCE BALANCE
writeFileSync balance.txt variables->BALANCE utf8
CONCAT Your_balance: variables->BALANCE TEMP
PRINT variables->TEMP
MORE variables->BALANCE variables->UPGRADE_COST 24
GOTO 16
MUL variables->UPGRADE 2 UPGRADE
MUL variables->UPGRADE_COST 2 UPGRADE_COST
TO_STRING variables->UPGRADE UPGRADE
TO_STRING variables->UPGRADE_COST UPGRADE_COST
writeFileSync upgrade.txt variables->UPGRADE utf8
writeFileSync upgrade_cost.txt variables->UPGRADE_COST utf8
PRINT Success_upgraded!
GOTO 14


Компилируется в:

1690760951922.png


Запуск:

1690761013574.png




interpreter.ts:
import { instructionCodes } from "./instructions";
import { Methods } from "./methods";

const utf8Encoder: TextEncoder = new TextEncoder();

enum Type {
    INT,
    STRING
}

type Variable = {
    index: number,
    value: number | string
};

type Constant = {
    index: number,
    type: Type,
    value: number | string
};

type Module = {
    methodPointer: any
};

type Command = {
    instructionPointer: any,
    arguments: number[]
};

class VirtualMachineHeap {
    protected variables: Variable[] = [];
    protected constants: Constant[] = [];
    protected connectedMethods: Module[] = [];
}

class VirtualMachineHM extends VirtualMachineHeap {
    protected createConstant(type: Type, value: number | string): Constant {
        const constant: Constant = {
            index: this.constants.filter(x => x.type === type).length,
            value: value,
            type: type
        };

        this.constants.push(constant);

        return constant;
    };
    protected createVariable(index: number, value: number): Variable {
        const variable: Variable = {
            index: index,
            value: value
        };

        this.variables.push(variable);

        return variable;
    };

    protected getConstant(type: Type, index: number): Constant {
        return this.constants.find(x => x.type === type && x.index === index);
    };
    protected getVariable(index: number): Variable {
        return this.variables.find(x => x.index === index);
    };

    protected createImportModulePointer(path: string, methodsIndexes: number[]): void {
        const requiredModuleMethods: any[] = Object.values(require(path));

        for (let i: number = 0; i < methodsIndexes.length; i++) {
            this.connectedMethods.push({
                methodPointer: requiredModuleMethods[methodsIndexes[i]]
            });
        }
    }
}

class VirtualMachineICM extends VirtualMachineHM {
    protected commandsArray: Command[] = [];
    protected currentCommandIndex: number = 0;

    protected [instructionCodes.get("create_variable").toString()](index: number, value: number): boolean {
        this.createVariable(index, value);

        return true;
    };

    protected [instructionCodes.get("import_module").toString()](type: 0 | 1, moduleIndex: number, ...methodIndexes: number[]): boolean {
        // @ts-ignore
        //const path: string = this.getConstant(Type.STRING, pathOfModuleConstantIndex).value;

        this.createImportModulePointer(type === 1 ? `./modules/${moduleIndex.toString()}.js` : this.getConstant(Type.STRING, moduleIndex).value, methodIndexes);

        return true;
    };

    protected [instructionCodes.get("call_method").toString()](methodIndex: number, variableIndex: number, ..._arguments: number[]): boolean {
        const variable: Variable = methodIndex > 0 ? this.getVariable(variableIndex) : void 0;
        const method: Module = this.connectedMethods[methodIndex - 50];
        const readyArgumentsForCall: (number | string)[] = [];

        for (let i: number = 0; i < _arguments.length;) {
            const argumentChunk: number[] = _arguments.slice(i, i + _arguments[i]);

            const element: Variable | Constant = argumentChunk[0] === 2 ?
                this.getVariable(argumentChunk[1]) :
                this.getConstant(Type[Type[argumentChunk[2]]], argumentChunk[1]);

            readyArgumentsForCall.push(element.value);

            i += argumentChunk[0];
        }

        const methodOutput: string | number = method.methodPointer.bind(this)(...readyArgumentsForCall);

        if (variable) variable.value = methodOutput;

        return true;
    }
}

export class VirtualMachine extends VirtualMachineICM {
    private sourceCode: number[];

    constructor(
        private bytes: number[]
    ) {
        super();
    };

    private loadConstants(): void {
        const countOfIntConstants: number = this.bytes[0];
        const sizeOfSourceCode: number = this.bytes[1];

        this.sourceCode = this.bytes.slice(2, 2 + sizeOfSourceCode);
        const chunkOfIntegers: number[] = this.bytes.slice(3 + sizeOfSourceCode, 3 + sizeOfSourceCode + countOfIntConstants);
        const chunkOfStrings: number[][] = Methods.splitArray<number>(this.bytes.slice(3 + sizeOfSourceCode + countOfIntConstants, this.bytes.length), 5);

        chunkOfIntegers.forEach(function(integer: number): void {
            this.createConstant(Type.INT, Number(integer));
        }.bind(this));

        chunkOfStrings.forEach(function(byteString: number[]): void {
            this.createConstant(Type.STRING, String.fromCharCode(...byteString));
        }.bind(this));
    };

    private startProcessing(): void {
        for (let i: number = 0; i < this.sourceCode.length;) {
            const byte: string = this.sourceCode[i].toString();
            const instruction: any = this[byte];

            const argumentsChunkSize: number = this.sourceCode[i + 1];
            const argumentsChunk: number[] = this.sourceCode.slice(i + 2, i + 2 + argumentsChunkSize);

            this.commandsArray.push({
                instructionPointer: instruction ?? this["21"],
                arguments: instruction ? argumentsChunk : [this.sourceCode[i], ...argumentsChunk]
            });

            i += (argumentsChunkSize + 2);
        }
    };

    public startInterpretation(): void {
        this.loadConstants();
        this.startProcessing();

        while (this.currentCommandIndex < this.commandsArray.length) {
            const commandObject: Command = this.commandsArray[this.currentCommandIndex];
            const resultOfRunInstruction: boolean = commandObject.instructionPointer.bind(this)(...commandObject.arguments);

            if (resultOfRunInstruction) this.currentCommandIndex++;
        }
    };
}
methods.ts:
export class Methods {
    public static splitArray<Type>(array: Type[], splitValue: Type): Type[][] {
        const newArray: Type[][] = [];
        let lastArray: Type[] = [];

        for (let i: number = 0; i < array.length; i++) {
            const element: Type = array[i];

            if (element === splitValue) {
                newArray.push(lastArray);
                lastArray = [];
                continue;
            }

            lastArray.push(element);
        }

        return newArray;
    };
}


1. - была добавлена возможность статического импортирования скомпилированных .mlc модулей.

-

Update 0.1​

Была добавлена возможность статического импортирования скомпилированных .mlc модулей.​


Пример использования (тут я создал модуль, функция которого складывать два числа):​


inclib.ml[Module]:
IMPORT INC ./modules/math.js
IMPORT EXPORT ./modules/mlclib.js
CONST INT ARG_OPERAND1 0
CONST INT ARG_OPERAND2 0
VAR RESULT 0
INC constants->INT->ARG_OPERAND1 constants->INT->ARG_OPERAND2 RESULT
EXPORT variables->RESULT
print.ml:
IMPORT PRINT ./modules/mlclib.js
IMPORT INC ./modules/inclib.mlc
VAR RESULT 0
INC 3 9 RESULT
PRINT variables->RESULT

Результат выполнения print.mlc:​

1690831355047.png


inclib.mlc:
1690831152114.png


print.mlc:
1690831183529.png
 
Последнее редактирование: