mirror of
https://github.com/osmarks/website
synced 2025-09-07 12:57:56 +00:00
RPNCalcv4 and stuff
This commit is contained in:
231
experiments/rpncalc-v4/parse.mjs
Normal file
231
experiments/rpncalc-v4/parse.mjs
Normal file
@@ -0,0 +1,231 @@
|
||||
/* make parser safe */
|
||||
const debug = false;
|
||||
|
||||
const attempt = (parser) => (stream) => {
|
||||
if (debug) {
|
||||
console.log("attempt");
|
||||
console.log(stream);
|
||||
}
|
||||
let streamclone = [...stream];
|
||||
try {
|
||||
let out = parser(stream);
|
||||
return out;
|
||||
} catch(err) {
|
||||
return {parsed:null,stream:streamclone};
|
||||
}
|
||||
}
|
||||
|
||||
/* chain */
|
||||
const or = (a, b) => (stream) => {
|
||||
let streamclone = [...stream];
|
||||
if (debug) {
|
||||
console.log("or");
|
||||
console.log(stream);
|
||||
}
|
||||
let aout = a(stream);
|
||||
if (aout.parsed === null) {
|
||||
return b(streamclone);
|
||||
} else {
|
||||
return aout;
|
||||
}
|
||||
}
|
||||
|
||||
/* (parser) */
|
||||
const parens = (parser) => (stream) => {
|
||||
if (debug) {
|
||||
console.log("parens");
|
||||
console.log(stream);
|
||||
}
|
||||
let a = parseSyntax("(")(stream);
|
||||
if (a.parsed === null) {
|
||||
return {parsed:null, stream:a.stream};
|
||||
}
|
||||
let dat = parser(a.stream);
|
||||
let b = parseSyntax(")")(dat.stream);
|
||||
if (b.parsed === null) {
|
||||
throw 'mismatched parens!';
|
||||
}
|
||||
dat.parsed.pos.start = a.parsed.pos.start;
|
||||
dat.parsed.pos.end = b.parsed.pos.end;
|
||||
return {parsed:dat.parsed, stream:b.stream};
|
||||
}
|
||||
|
||||
/* [parser] */
|
||||
const many = (parser) => (stream) => {
|
||||
if (debug) {
|
||||
console.log("many");
|
||||
console.log(stream);
|
||||
}
|
||||
let parsed = [];
|
||||
while (true) {
|
||||
let i = parser(stream);
|
||||
stream = i.stream;
|
||||
if (i.parsed === null) {
|
||||
break;
|
||||
} else {
|
||||
parsed.push(i.parsed);
|
||||
}
|
||||
}
|
||||
let startPos = 0;
|
||||
let endPos = 0;
|
||||
if (parsed.length > 1) {
|
||||
startPos = parsed[0].pos.start;
|
||||
endPos = parsed[parsed.length-1].pos.start;
|
||||
}
|
||||
return {parsed:{arr:parsed, pos:{start:startPos, end:endPos}}, stream:stream};
|
||||
}
|
||||
|
||||
/* takes in stream, outputs parsed item or null */
|
||||
const parseIdent = (stream) => {
|
||||
if (debug) {
|
||||
console.log("ident");
|
||||
console.log(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.name, pos:{start:e.startPos, end:e.endPos}}, stream:stream};
|
||||
}
|
||||
}
|
||||
|
||||
/* takes in stream, outputs parsed item or null */
|
||||
const parseNum = (stream) => {
|
||||
if (debug) {
|
||||
console.log("num");
|
||||
console.log(stream);
|
||||
}
|
||||
let e = stream[0];
|
||||
if (e === undefined) {
|
||||
return {parsed:null, stream:stream};
|
||||
}
|
||||
if (e.type !== "num") {
|
||||
return {parsed:null, stream:stream};
|
||||
} else {
|
||||
stream.shift();
|
||||
return {parsed:{type:e.type, val:e.val, pos:{start:e.startPos, end:e.endPos}}, stream:stream};
|
||||
}
|
||||
}
|
||||
|
||||
/* takes in stream, outputs parsed item or null */
|
||||
const parseSyntax = (syntax) => (stream) => {
|
||||
if (debug) {
|
||||
console.log("syntax", syntax);
|
||||
console.log(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, pos:{start:e.startPos, end:e.endPos}}, stream:stream};
|
||||
}
|
||||
}
|
||||
|
||||
/* takes in stream, outputs string or null - FAILABLE */
|
||||
const parseName = (stream) => {
|
||||
if (debug) {
|
||||
console.log("name");
|
||||
console.log(stream);
|
||||
}
|
||||
let id = parseIdent(stream);
|
||||
if (id.parsed === null) {
|
||||
return {parsed:null, stream:id.stream};
|
||||
}
|
||||
let syn = parseSyntax(";")(id.stream);
|
||||
if (syn.parsed === null) {
|
||||
throw 'could not parse name!'
|
||||
}
|
||||
return {parsed:{name:id.parsed.val, pos:{start:id.parsed.pos.start, end:syn.parsed.pos.end}}, stream:syn.stream};
|
||||
}
|
||||
|
||||
const parseType = (stream) => {
|
||||
if (debug) {
|
||||
console.log("type");
|
||||
console.log(stream);
|
||||
}
|
||||
let syn = attempt(parseSyntax("\""))(stream);
|
||||
if (syn.parsed === null) {
|
||||
return {parsed:null, stream:syn.stream};
|
||||
}
|
||||
let id = or(parseIdent, parseNum)(syn.stream);
|
||||
if (id.parsed === null) {
|
||||
return {parsed:null, stream:id.stream};
|
||||
}
|
||||
return {parsed:{type:"type", val:id.parsed.val, pos:{start:syn.parsed.pos.start, end:id.parsed.pos.end}}, stream:id.stream};
|
||||
}
|
||||
|
||||
/* takes in stream, outputs parsed item or null - FAILABLE */
|
||||
const parsePush = (stream) => {
|
||||
if (debug) {
|
||||
console.log("push");
|
||||
console.log(stream);
|
||||
}
|
||||
let syn = attempt(parseSyntax("'"))(stream);
|
||||
if (syn.parsed === null) {
|
||||
return {parsed:null, stream:syn.stream};
|
||||
}
|
||||
let id = parseExpr(syn.stream);
|
||||
if (id.parsed === null) {
|
||||
return {parsed:null, stream:id.stream};
|
||||
}
|
||||
return {parsed:{type:"push", elem:id.parsed, pos:{start:syn.parsed.pos.start, end:id.parsed.pos.end}}, stream:id.stream};
|
||||
}
|
||||
|
||||
const parseDefn = (stream) => {
|
||||
if (debug) {
|
||||
console.log("defn");
|
||||
console.log(stream);
|
||||
}
|
||||
let name = parseName(stream);
|
||||
if (name.parsed === null) {
|
||||
throw 'no name found!'
|
||||
}
|
||||
let expr = parseExprs(stream);
|
||||
if (expr.parsed === null) {
|
||||
throw 'no body found!'
|
||||
}
|
||||
return {parsed:{type:"defn", ident:name.parsed.name, defn:expr.parsed.arr, pos:{start:name.parsed.pos.start, end:expr.parsed.pos.end}}, stream:expr.stream}
|
||||
}
|
||||
|
||||
/* takes in stream, outputs parsed item or null - FAILABLE */
|
||||
const parseLambda = (stream) => {
|
||||
if (debug) {
|
||||
console.log("lambda");
|
||||
console.log(stream);
|
||||
}
|
||||
let args = many(parseIdent)(stream);
|
||||
let syn = parseSyntax("->")(args.stream);
|
||||
if (syn.parsed === null) {
|
||||
throw 'no lambda body found!';
|
||||
}
|
||||
let body = parseExprs(syn.stream); // .parsed should never be null, but anyway...
|
||||
if (body.parsed === null) {
|
||||
throw 'no lambda body found!';
|
||||
}
|
||||
return {parsed:{type:"func", args:args.parsed.arr.map(x => x.val), body:body.parsed.arr, pos:{start:args.parsed.pos.start, end:body.parsed.pos.end}}, stream:body.stream};
|
||||
}
|
||||
|
||||
/* takes in stream, outputs parsed item or null */
|
||||
const parseExpr = or(
|
||||
attempt(parens(parseDefn)), or(
|
||||
attempt(parens(parseLambda)), or(
|
||||
attempt(parseLambda), or(
|
||||
parseType, or(
|
||||
parseIdent, or(
|
||||
parseNum,
|
||||
parsePush
|
||||
))))));
|
||||
|
||||
/* takes in stream, outputs parsed items */
|
||||
export const parseExprs = many(parseExpr);
|
Reference in New Issue
Block a user