bds.js
Version:
A simple interpreter written to simulate and run BDScript Language in JavaScript
178 lines (177 loc) • 5.36 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Lexer = void 0;
const SYNTAX = "[]\\;$";
const OPS = /[!=<>]+$/;
const FN_DEF = /[a-z_]/i;
class Lexer {
constructor(input) {
this.input = input;
this.pos = 0;
this.line = 0;
this.col = 0;
this.escape_c = false;
if (typeof input !== "string" || !input)
throw new Error("input arg must be non-empty and typeof string!");
}
main() {
const Tokens = [];
while (true) {
let res = this.advance();
if (res)
Tokens.push(res);
if (this.eof())
break;
}
return this.clean(Tokens);
}
peek(offset = 0) {
return this.input[this.pos + offset];
}
next() {
let current = this.input[this.pos++];
if (this.peek() === "\n") {
this.line += 1;
this.col = 0;
}
else {
this.col += 1;
}
return current;
}
eof() {
return this.peek() === "" || this.peek() === undefined;
}
isOperator(x) {
return OPS.test(x);
}
isSyntax(c) {
return SYNTAX.indexOf(c) > -1;
}
isNumber(x) {
return parseInt(x) === Number(x);
}
parseOperator(x) {
switch (x) {
case "==":
case "!=":
case ">=":
case "<=":
case ">":
case "<": return { type: "operator", value: x, pos: this.col, line: this.line };
}
return { type: "string", value: x, pos: this.col, line: this.line };
}
validateCall(c) {
return FN_DEF.test(c);
}
parseCall() {
const fn = this.readInput(this.validateCall);
if (!fn)
return { type: "string", value: "$", pos: this.col, line: this.line };
return { type: "call", value: "$" + fn, child: [], pos: this.col, line: this.line };
}
validateString(c) {
return !(this.isSyntax(c) || this.isOperator(c));
}
parseString() {
const str = this.readInput(this.validateString);
if (this.isNumber(str))
return { type: "number", value: Number(str), pos: this.col, line: this.line };
return { type: "string", value: str, pos: this.col, line: this.line };
}
readInput(validator) {
let str = "";
while (!this.eof() && validator.apply(this, [this.peek()])) {
str += this.next();
}
return str;
}
advance() {
let c = this.peek();
if (this.escape_c) {
this.escape_c = false;
this.next();
if (this.isSyntax(c) || this.isOperator(c))
return { type: "string", value: c, pos: this.col, line: this.line };
return { type: "string", value: "\\" + c, pos: this.col, line: this.line };
}
switch (c) {
case "[":
{
this.next();
return { type: "open", pos: this.col, line: this.line };
}
;
case "]":
{
this.next();
return { type: "close", pos: this.col, line: this.line };
}
;
case ";":
{
this.next();
return { type: "newArg", pos: this.col, line: this.line };
}
;
case "\\":
{
this.next();
this.escape_c = true;
return void 0;
}
;
case "$":
{
this.next();
return this.parseCall();
}
;
}
if (this.isOperator(c)) {
this.next();
if (this.isOperator(c + this.peek()))
return this.parseOperator(c + this.next());
return this.parseOperator(c);
}
return this.parseString();
}
clean(tokens) {
let newArr = [];
let token;
let current;
while (tokens.length > 0) {
token = tokens.shift();
if (!current) {
current = token;
continue;
}
;
if (current.type === "string" && current.type === token.type) {
current.value += token.value;
continue;
}
else {
if (current.type !== "string") {
newArr.push(current);
current = token;
}
else {
if (token.type !== "string") {
newArr.push(current);
current = token;
}
else
throw new Error("dunno wat to do");
}
}
}
if (current)
newArr.push(current);
token = void 0;
current = void 0;
return newArr;
}
}
exports.Lexer = Lexer;