1
0
Fork 0

typed registers in VM

This commit is contained in:
Sean Sube 2022-06-21 18:00:23 -05:00
parent 8c1a107e94
commit 62ff97083f
6 changed files with 104 additions and 49 deletions

View File

@ -33,3 +33,9 @@ run-debug: ## run the app and wait for an inspector to connect
upload-climate:
cc-test-reporter format-coverage -t lcov -o $(TARGET_PATH)/coverage/codeclimate.json -p $(ROOT_PATH) $(TARGET_PATH)/coverage/lcov.info
cc-test-reporter upload-coverage --debug -i $(TARGET_PATH)/coverage/codeclimate.json -r "$(shell echo "${CODECLIMATE_SECRET}" | base64 -d)"
run-assembly:
node out/src/index.js assembly ./src/examples/loop.assembly.ssrp
run-abstract:
node out/src/index.js abstract ./src/examples/loop.abstract.ssrp

View File

@ -1,6 +1,6 @@
import pegjs from 'pegjs';
import { FunctionNode, ProgramRegisters } from './ast.js';
import { FunctionNode, RawRegisters } from './ast.js';
import { loadFile } from './files.js';
import { Codec, ProgramModule } from './module.js';
@ -13,7 +13,7 @@ export function loadGrammar() {
export interface ParserOutput {
functions: Array<FunctionNode>;
registers: Partial<ProgramRegisters>;
registers: Partial<RawRegisters>;
}
export function abstractToAST(source: string): ProgramModule {
@ -42,4 +42,3 @@ export const codec: Codec = {
load: abstractToAST,
save: abstractFromAST,
};

View File

@ -1,6 +1,6 @@
import pegjs from 'pegjs';
import { CommandNode, ProgramRegisters } from './ast.js';
import { CommandNode, RawRegisters } from './ast.js';
import { loadFile } from './files.js';
import { Codec, ProgramModule } from './module.js';
@ -13,7 +13,7 @@ export function loadGrammar() {
export interface ParserOutput {
instructions: Array<CommandNode>;
registers: Partial<ProgramRegisters>;
registers: Partial<RawRegisters>;
}
/**

View File

@ -1,6 +1,4 @@
export type Address = number;
export type Counter = number;
export type Register = number;
export enum OpCode {
// flow
@ -42,6 +40,13 @@ export enum CompareOperator {
LESS_EQUAL = '<=',
}
export interface Address {
type: 'address';
module: number;
function: number;
offset: number;
}
export interface AddressRegisters {
PC: Address;
RA: Address;
@ -49,6 +54,11 @@ export interface AddressRegisters {
IH: Address;
}
export interface Register {
type: 'data';
value: number;
}
export interface DataRegisters {
R1: Register;
R2: Register;
@ -58,6 +68,8 @@ export interface DataRegisters {
export type ProgramRegisters = AddressRegisters & DataRegisters;
export type RawRegisters = {[K in keyof ProgramRegisters]: number};
export type RegisterName = keyof ProgramRegisters;
// nodes

View File

@ -1,7 +1,7 @@
import { Address, FunctionNode, ProgramRegisters } from './ast';
import { FunctionNode, ProgramRegisters, RawRegisters } from './ast';
export interface Table<TEntry> {
names: Array<[string, Address]>;
names: Array<[string, number]>;
entries: Array<TEntry>;
}
@ -12,13 +12,10 @@ export interface GlobalEntry {
export interface ProgramModule {
functions: Table<FunctionNode>;
globals: Table<GlobalEntry>;
registers: Partial<ProgramRegisters>;
registers: Partial<RawRegisters>;
}
export interface ProgramStack {
counter: number;
values: Array<number>;
}
export type ProgramStack = Array<number>;
export interface Program {
registers: ProgramRegisters;

View File

@ -1,29 +1,56 @@
import { BranchNode, CommandNode, MATH_OP_CODE, OpCode, ProgramRegisters, Register, RegisterName, ValueNode } from './ast.js';
import { Program, ProgramModule, ProgramStack } from './module.js';
import {
Address,
BranchNode,
CommandNode,
MATH_OP_CODE,
OpCode,
ProgramRegisters,
RawRegisters,
Register,
RegisterName,
ValueNode,
} from './ast.js';
import { Program, ProgramModule } from './module.js';
export function address(offset = 0): Address {
return {
type: 'address',
module: 0,
function: 0,
offset,
};
}
export function register(value = 0): Register {
return {
type: 'data',
value,
};
}
export function completeRegisters(partial: Partial<RawRegisters>): ProgramRegisters {
return {
PC: address(partial.PC),
RA: address(partial.RA),
EH: address(partial.EH),
IH: address(partial.IH),
R1: register(partial.R1),
R2: register(partial.R2),
R3: register(partial.R3),
R4: register(partial.R4),
};
}
/**
* Prepare the memory for a program module to run.
*/
export function prep(runtime: ProgramModule, program: ProgramModule): Program {
return {
registers: {
PC: 0,
RA: 0,
EH: 0,
IH: 0,
R1: 0,
R2: 0,
R3: 0,
R4: 0,
...program.registers,
},
registers: completeRegisters(program.registers),
runtime,
program,
modules: [],
stack: {
counter: 0,
values: [],
},
stack: [],
};
}
@ -39,23 +66,37 @@ export function getRegister(value?: ValueNode): RegisterName {
}
}
export function getValue(registers: ProgramRegisters, value?: ValueNode): Register {
export function getValue(registers: ProgramRegisters, value?: ValueNode): number {
if (value === null || value === undefined) {
return 0;
}
if (value.type === 'register') {
return registers[value.register];
const register = registers[value.register];
if (register.type === 'address') {
return register.offset;
} else {
return register.value;
}
} else {
return value.constant;
}
}
export function setValue(registers: ProgramRegisters, name: RegisterName, value: number): void {
const register = registers[name];
if (register.type === 'address') {
register.offset = value;
} else {
register.value = value;
}
}
/**
* Step the program and execute one command.
*/
export function step(state: Program, fn: BranchNode, pc_offset = 0): boolean {
const instruction = fn.body[state.registers.PC - pc_offset];
const instruction = fn.body[state.registers.PC.offset - pc_offset];
console.log('step', instruction, state.registers);
switch (instruction.type) {
@ -67,14 +108,14 @@ export function step(state: Program, fn: BranchNode, pc_offset = 0): boolean {
code: MATH_OP_CODE[instruction.op],
};
const result = stepCommand(command, state.registers, state.stack);
state.registers.PC += result.step;
state.registers.PC.offset += result.step;
console.log('post', result, state.registers);
return result.stop;
}
case 'command':
{
const result = stepCommand(instruction, state.registers, state.stack);
state.registers.PC += result.step;
state.registers.PC.offset += result.step;
console.log('post', result, state.registers);
return result.stop;
}
@ -86,20 +127,20 @@ export function step(state: Program, fn: BranchNode, pc_offset = 0): boolean {
code: OpCode.COMPARE,
};
const result = stepCommand(command, state.registers, state.stack);
state.registers.PC += 1; // abstract conditionals are always a single node
state.registers.PC.offset += 1; // abstract conditionals are always a single node
if (result.step === 1) {
// step through body
console.log('step body', instruction.body);
state.registers.RA = state.registers.PC;
for (let i = 0; i < instruction.body.length; ++i) {
if (step(state, instruction, state.registers.RA) === true) {
if (step(state, instruction, state.registers.RA.offset) === true) {
return true;
}
if (state.registers.PC < state.registers.RA) {
if (state.registers.PC.offset < state.registers.RA.offset) {
// CALL to earlier code
break;
}
if (state.registers.PC > state.registers.RA + instruction.body.length) {
if (state.registers.PC.offset > state.registers.RA.offset + instruction.body.length) {
// CALL to later code
break;
}
@ -130,7 +171,7 @@ export interface StepResult {
step: number;
}
export function stepCommand(instruction: CommandNode, registers: ProgramRegisters, stack: ProgramStack): StepResult {
export function stepCommand(instruction: CommandNode, registers: ProgramRegisters, stack: Array<number>): StepResult {
switch (instruction.code) {
case OpCode.STOP:
return {
@ -154,7 +195,7 @@ export function stepCommand(instruction: CommandNode, registers: ProgramRegister
{
const dest = getValue(registers, instruction.first);
console.log('JUMP', dest);
registers.PC = dest;
setValue(registers, 'PC', dest);
return {
stop: false,
step: 0,
@ -164,7 +205,7 @@ export function stepCommand(instruction: CommandNode, registers: ProgramRegister
{
const dest = getValue(registers, instruction.first);
console.log('CALL', dest);
registers.PC = dest;
registers.PC.offset = dest;
return {
stop: false,
step: 0,
@ -174,21 +215,21 @@ export function stepCommand(instruction: CommandNode, registers: ProgramRegister
{
const reg = getRegister(instruction.first);
console.log('PEEK', reg);
registers[reg] = stack.values[stack.values.length - 1] || 0;
setValue(registers, reg, stack[stack.length - 1] || 0);
break;
}
case OpCode.POP:
{
const reg = getRegister(instruction.first);
console.log('POP', reg);
registers[reg] = stack.values.pop() || 0;
setValue(registers, reg, stack.pop() || 0);
break;
}
case OpCode.PUSH:
{
const val = getValue(registers, instruction.first);
console.log('PUSH', val);
stack.values.push(val);
stack.push(val);
break;
}
case OpCode.ADD:
@ -196,7 +237,7 @@ export function stepCommand(instruction: CommandNode, registers: ProgramRegister
const reg = getRegister(instruction.first);
const val = (getValue(registers, instruction.second) + getValue(registers, instruction.third)) % Number.MAX_SAFE_INTEGER;
console.log('ADD', reg, val);
registers[reg] = val;
setValue(registers, reg, val);
break;
}
default:
@ -212,11 +253,11 @@ export function stepCommand(instruction: CommandNode, registers: ProgramRegister
/**
* Step the program until the function has been exhausted.
*/
export function runProgram(main: ProgramModule): ProgramStack {
export function runProgram(main: ProgramModule): Array<number> {
const state = prep(RUNTIME_MODULE, main);
console.log('start');
const fn = state.program.functions.entries[0];
while (state.registers.PC < fn.body.length) {
while (state.registers.PC.offset < fn.body.length) {
if (step(state, fn) === true) {
break;
}