corascript
Version:
cora is a lisp dialect compile to javascript
109 lines (105 loc) • 3.13 kB
JavaScript
var {Symbol, Cons} = require('../lib/kl');
function arrayToCons(x) {
let result = null;
for (let i = x.length - 1; i >= 0; i--) result = new Cons(x[i], result);
return result;
}
class Parser {
static parseString(text) {
return new Parser(text).parse();
}
static parseAllString(text) {
return new Parser(text).parseAll();
}
constructor(text) {
this.text = text;
this.pos = 0;
}
get current() {
return this.text[this.pos];
}
get next() {
return this.text[this.pos + 1];
}
get done() {
return this.pos >= this.text.length;
}
get more() {
return !this.done;
}
skipWhitespace() {
while (this.more && /\s/.test(this.current)) this.skipOne();
}
skipOne() {
this.pos++;
}
static isDigit(ch) {
return ch !== undefined && /\d/.test(ch);
}
static isSign(ch) {
return ch !== undefined && /[\-\+]/.test(ch);
}
static isSymbolChar(ch) {
return ch !== undefined && /[^\s\(\)]/.test(ch);
}
readString() {
this.skipOne();
const start = this.pos;
while (this.current !== '"') {
if (this.done) throw new Error('unexpected end of input');
this.skipOne();
}
const end = this.pos;
this.skipOne();
return this.text.substring(start, end);
}
readNumber() {
const start = this.pos;
if (this.more && Parser.isSign(this.current)) this.skipOne();
while (this.more && Parser.isDigit(this.current)) this.skipOne();
if (this.more && this.current === '.') {
this.skipOne();
while (this.more && Parser.isDigit(this.current)) this.skipOne();
}
const end = this.pos;
return parseFloat(this.text.substring(start, end));
}
readSymbol() {
const start = this.pos;
while (this.more && Parser.isSymbolChar(this.current)) this.skipOne();
const end = this.pos;
return new Symbol(this.text.substring(start, end));
}
parse() {
this.skipWhitespace();
if (this.done) throw new Error('unexpected end of input');
if (this.current === '(') {
this.skipOne();
const children = [];
let child = this.parse();
while (child !== undefined) {
children.push(child);
child = this.parse();
}
return arrayToCons(children);
}
if (this.current === ')') {
this.skipOne();
return undefined;
}
if (this.current === '"') return this.readString();
if (Parser.isDigit(this.current) ||
(Parser.isSign(this.current) && Parser.isDigit(this.next))) return this.readNumber();
return this.readSymbol();
}
parseAll() {
this.skipWhitespace();
const results = [];
while (this.more) {
results.push(this.parse());
this.skipWhitespace();
}
return results;
}
}
module.exports = Parser;