calcium-lang
Version:
Calcium language interpreter
165 lines • 5.45 kB
JavaScript
import * as Cmd from "../command";
import createBuiltinFunction from "../factory/builtinFunction";
import Environment from "./environment";
import Namespace from "./namespace";
import Parser from "../parser";
import Status from "./status";
import * as Builtin from "../builtin";
import Index from "../indexes";
import { Result } from "./block";
import { FunctionCalled, InconsistentBlock } from "../error";
import * as Kw from "../keyword";
import { None } from "../factory";
export default class Runtime {
/**
*
* @param code must be a string or a JSON array of Calcium statements.
*/
constructor(code, opt) {
var _a;
this.breakpoints = new Set();
/**
* a utility flag to execute on a Worker
*/
this.isPaused = false;
this.parser = (_a = opt === null || opt === void 0 ? void 0 : opt.parser) !== null && _a !== void 0 ? _a : new Parser();
// set up built-ins
const builtin = new Namespace();
for (let name in Builtin.Functions) {
const builtinFunc = createBuiltinFunction({
name,
body: Builtin.Functions[name],
});
builtin.register(name, builtinFunc);
}
const env = new Environment(code, builtin);
this.env = env;
}
addBreakpoint(line) {
this.breakpoints.add(line);
}
pause() {
this.isPaused = true;
}
removeBreakpoint(line) {
this.breakpoints.delete(line);
}
resume() {
this.isPaused = false;
}
run() {
if (this.env.address.line >= this.env.code.length) {
return Status.Terminated;
}
while (true) {
const result = this.step();
if (result !== Status.Running) {
return result;
}
else {
continue;
}
}
}
/**
*
* @param funcToOutput built-in function's body to output
*/
setOutputFunction(funcToOutput) {
this.env.funcToOutput = funcToOutput;
}
skipToNextLine() {
let nextIndex;
outer: while (true) {
nextIndex = this.env.address.line + 1;
inner: while (true) {
const nextStmt = this.env.code[nextIndex];
const nextIndent = nextStmt[Index.Statement.Indent];
const delta = this.env.address.indent - nextIndent;
if (delta > 0) {
// some blocks must be popped.
for (let i = 0; i < delta; ++i) {
const result = this.env.lastBlock.exit(this.env);
if (result === Result.Invalid) {
throw new InconsistentBlock();
}
else if (result === Result.Jumpped) {
continue outer;
}
}
break outer;
}
else if (delta === 0) {
break outer;
}
else {
nextIndex += 1;
continue inner;
}
}
}
this.env.address.line = nextIndex;
}
/**
* execute one line.
*
* @returns the result of the execution
*/
step() {
var _a;
if (this.env.address.line >= this.env.code.length) {
return Status.Terminated;
}
let stmt = this.currentStatement;
let cmd = this.parser.read(stmt);
if (cmd instanceof Cmd.End) {
return Status.Terminated;
}
const callerAddr = this.env.address.clone();
const lastCommand = this.env.commandsWithCall[this.env.commandsWithCall.length - 1];
if (lastCommand && callerAddr.isAt(lastCommand.address)) {
cmd = lastCommand.command;
const returnedValue = (_a = this.env.commandsWithCall.pop()) === null || _a === void 0 ? void 0 : _a.returnedValue;
if (returnedValue) {
this.env.returnedValue = returnedValue;
}
}
try {
cmd.execute(this.env);
}
catch (e) {
if (e instanceof FunctionCalled) {
this.env.commandsWithCall.push({
address: callerAddr,
command: cmd,
returnedValue: this.env.returnedValue === None
? undefined
: this.env.returnedValue,
});
}
else {
throw e;
}
}
if (this.isPaused)
return Status.Paused;
this.skipToNextLine();
stmt = this.currentStatement;
let kw = stmt[Index.Statement.Keyword];
// pass through Ifs and Comment commands
while (kw === Kw.Command.Ifs || kw === Kw.Command.Comment) {
const cmd = this.parser.read(stmt);
cmd.execute(this.env);
this.skipToNextLine();
stmt = this.currentStatement;
kw = stmt[Index.Statement.Keyword];
}
if (this.breakpoints.has(this.env.address.line))
return Status.AtBreakpoint;
return Status.Running;
}
get currentStatement() {
return this.env.code[this.env.address.line];
}
}
//# sourceMappingURL=runtime.js.map