- 47
- 29
Приветствую!
Хотел бы поделиться своей разработкой, а именно своим компилируемым в байткод языком, написанным на TypeScript.
------------------------------------------------------------------------------------------------------------------------------------------------
Теоретически инструкции в нём всего три: VAR, CONST, IMPORT...
Остальные инструкции создаются вручную в JS-модулях, что даёт возможность легко расширять возможности данного языка.
Для понимания, он, конечно не прост, ведь синтаксисом схож с ассемблером Объясняется это тем, что я поленился написать лексер. Да и практического применения этому языку особого нету.
Как оно работает и примеры кода
-------------------------------------------------------------------------------------------------------------------------------------------------
Выведем в консоль Hello World
Хотел бы поделиться своей разработкой, а именно своим компилируемым в байткод языком, написанным на 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);
}
}
Для начала наш код необходимо скомпилировать.
В итоге получаем вот такой байткод
А вот уже полученный скомпилированный файл можно запустить.
--------------------------------------------------------------------------------------------------------------------------------
Сейчас покажу пример покрупнее - напишем простенькую игру-кликер с сохранениями.
В итоге получаем вот такой байткод
А вот уже полученный скомпилированный файл можно запустить.
--------------------------------------------------------------------------------------------------------------------------------
Сейчас покажу пример покрупнее - напишем простенькую игру-кликер с сохранениями.
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
Компилируется в:
Запуск:
Запуск:
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:
inclib.mlc:
print.mlc:
print.mlc:
Последнее редактирование: