mirror of
https://github.com/Baidicoot/rpncalc-v4
synced 2024-12-12 11:10:26 +00:00
wrote parser (untested)
This commit is contained in:
parent
9fd5c7df5c
commit
eacf6cfdde
190
parse.js
190
parse.js
@ -0,0 +1,190 @@
|
||||
/*
|
||||
parse.js
|
||||
--------
|
||||
|
||||
Parse tokenstream into AST
|
||||
|
||||
convert:
|
||||
[
|
||||
{type:"int",val:1},
|
||||
{type:"int",val:2},
|
||||
{type:"ident",name:"+"},
|
||||
{type:"syntax",val:"("},
|
||||
{type:"ident",name:"name"},
|
||||
{type:"syntax",val:";"},
|
||||
{type:"ident",name:"args"},
|
||||
{type:"syntax",val:"->"},
|
||||
{type:"int",val:2},
|
||||
{type:"ident",name:"args"},
|
||||
{type:"ident",name:"+"},
|
||||
{type:"syntax",val:")"}
|
||||
]
|
||||
|
||||
to:
|
||||
[
|
||||
{type:"int", val:1},
|
||||
{type:"int", val:2},
|
||||
{type:"builtin", op:"+"},
|
||||
{type:"func", name:"name", args:["args"], body:[
|
||||
{type:"int", val:1},
|
||||
{type:"ident", val:"args"},
|
||||
{type:"builtin", op:"+"},
|
||||
}]},
|
||||
]
|
||||
|
||||
EXPORTED FUNCTIONS:
|
||||
parse - takes in tokenstream, outputs AST
|
||||
*/
|
||||
|
||||
const builtin = [
|
||||
"+",
|
||||
"-",
|
||||
"*",
|
||||
"/",
|
||||
"typeof",
|
||||
"pair",
|
||||
"unpair"
|
||||
];
|
||||
|
||||
/* make parser safe */
|
||||
const attempt = (parser) => (stream) => {
|
||||
let streamclone = [...stream];
|
||||
try {
|
||||
let out = parser(stream);
|
||||
return out;
|
||||
} catch(err) {
|
||||
console.log(err);
|
||||
return {parsed:null,stream:streamclone};
|
||||
}
|
||||
}
|
||||
|
||||
/* chain */
|
||||
const or = (a, b) => (stream) => {
|
||||
let aout = a(stream);
|
||||
if (aout.parsed === null) {
|
||||
return b(stream);
|
||||
} else {
|
||||
return aout;
|
||||
}
|
||||
}
|
||||
|
||||
/* (parser) */
|
||||
const parens = (parser) => (stream) => {
|
||||
let a = parseSyntax("(", stream);
|
||||
if (a === null) {
|
||||
return {parsed:null, stream:stream};
|
||||
}
|
||||
let dat = parser(stream);
|
||||
a = parseSyntax(")", stream);
|
||||
if (a === null) {
|
||||
throw 'mismatched parens!';
|
||||
}
|
||||
return dat;
|
||||
}
|
||||
|
||||
/* [parser] */
|
||||
const many = (parser) => (stream) => {
|
||||
let parsed = [];
|
||||
for (let i = parser(stream); i.parsed !== null; i = parser(stream)) {
|
||||
idents.push(i.parsed);
|
||||
}
|
||||
return {parsed:parsed, stream:stream};
|
||||
}
|
||||
|
||||
/* takes in stream, outputs {parsed, stream} */
|
||||
const parseBuiltin = (stream) => {
|
||||
let e = stream[0];
|
||||
if (e === undefined) {
|
||||
return {parsed:null, stream:stream};
|
||||
}
|
||||
if (e.type !== "ident") {
|
||||
return {parsed:null, stream:stream};
|
||||
}
|
||||
if (builtin.includes(e.name)) {
|
||||
stream.shift();
|
||||
return {parsed:{type:"builtin", val:e.name}, stream:stream};
|
||||
} else {
|
||||
return {parsed:null, stream:stream};
|
||||
}
|
||||
}
|
||||
|
||||
/* takes in stream, outputs parsed item or null */
|
||||
const parseIdent = (stream) => {
|
||||
let e = stream[0];
|
||||
if (e === undefined) {
|
||||
return {parsed:null, stream:stream};
|
||||
}
|
||||
if (e.type !== "ident") {
|
||||
return {parsed:null, stream:stream};
|
||||
} else {
|
||||
stream.shift();
|
||||
return {parsed:{type:e.type, val:e.val}, stream:stream};
|
||||
}
|
||||
}
|
||||
|
||||
/* takes in stream, outputs parsed item or null */
|
||||
const parseInteger = (stream) => {
|
||||
let e = stream[0];
|
||||
if (e === undefined) {
|
||||
return {parsed:null, stream:stream};
|
||||
}
|
||||
if (e.type !== "int") {
|
||||
return {parsed:null, stream:stream};
|
||||
} else {
|
||||
stream.shift();
|
||||
return {parsed:{type:e.type, val:e.val}, stream:stream};
|
||||
}
|
||||
}
|
||||
|
||||
/* takes in stream, outputs parsed item or null */
|
||||
const parseSyntax = (syntax, stream) => {
|
||||
let e = stream[0];
|
||||
if (e === undefined) {
|
||||
return {parsed:null, stream:stream};
|
||||
}
|
||||
if (e.type !== "syntax") {
|
||||
return {parsed:null, stream:stream};
|
||||
}
|
||||
if (e.val !== syntax) {
|
||||
return {parsed:null, stream:stream};
|
||||
} else {
|
||||
stream.shift();
|
||||
return {parsed:{type:"syntax", val:syntax}, stream:stream};
|
||||
}
|
||||
}
|
||||
|
||||
/* takes in stream, outputs string or null - FAILABLE */
|
||||
const parseName = (stream) => {
|
||||
let id = parseIdent(stream);
|
||||
if (id === null) {
|
||||
return {parsed:null, stream:stream};
|
||||
}
|
||||
let syn = parseSyntax(";", stream);
|
||||
if (syn === null) {
|
||||
throw 'could not parse name!'
|
||||
}
|
||||
return id.val;
|
||||
}
|
||||
|
||||
/* takes in stream, outputs parsed item or null - FAILABLE */
|
||||
const parseLambda = (stream) => {
|
||||
let name = attempt(parseName)(stream).val;
|
||||
if (name === null) {
|
||||
name = "";
|
||||
}
|
||||
let args = many(parseIdent)(stream).parsed;
|
||||
if (parseSyntax("->", stream).parsed === null) {
|
||||
throw 'no lambda body found!';
|
||||
}
|
||||
let body = parseExprs(stream).parsed; // .parsed should never be null, but anyway...
|
||||
if (body === null) {
|
||||
throw 'no lambda body found!';
|
||||
}
|
||||
return {parsed:{type:func, name:name, args:args, body:body}, stream:stream};
|
||||
}
|
||||
|
||||
/* takes in stream, outputs parsed item or null */
|
||||
const parseExpr = or(parseBuiltin, or(parseIdent, or(parseInteger, attempt(parens(parseLambda)))));
|
||||
|
||||
/* takes in stream, outputs parsed items */
|
||||
export const parseExprs = (stream) => many(parseExpr);
|
Loading…
Reference in New Issue
Block a user