2020-05-19 17:26:44 +00:00
|
|
|
/* make parser safe */
|
|
|
|
const attempt = (parser) => (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 aout = a(stream);
|
|
|
|
if (aout.parsed === null) {
|
2020-05-19 23:11:31 +00:00
|
|
|
return b(aout.stream);
|
2020-05-19 17:26:44 +00:00
|
|
|
} else {
|
|
|
|
return aout;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* (parser) */
|
|
|
|
const parens = (parser) => (stream) => {
|
2020-05-20 11:04:37 +00:00
|
|
|
let a = parseSyntax("(")(stream);
|
2020-05-19 23:11:31 +00:00
|
|
|
if (a.parsed === null) {
|
|
|
|
return {parsed:null, stream:a.stream};
|
2020-05-19 17:26:44 +00:00
|
|
|
}
|
2020-05-19 23:11:31 +00:00
|
|
|
let dat = parser(a.stream);
|
2020-05-20 11:04:37 +00:00
|
|
|
a = parseSyntax(")")(dat.stream);
|
2020-05-19 23:11:31 +00:00
|
|
|
if (a.parsed === null) {
|
2020-05-19 17:26:44 +00:00
|
|
|
throw 'mismatched parens!';
|
|
|
|
}
|
2020-05-19 23:11:31 +00:00
|
|
|
return {parsed:dat.parsed, stream:a.stream};
|
2020-05-19 17:26:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* [parser] */
|
|
|
|
const many = (parser) => (stream) => {
|
|
|
|
let parsed = [];
|
|
|
|
for (let i = parser(stream); i.parsed !== null; i = parser(stream)) {
|
2020-05-19 22:07:16 +00:00
|
|
|
parsed.push(i.parsed);
|
2020-05-19 23:11:31 +00:00
|
|
|
stream = i.stream;
|
2020-05-19 17:26:44 +00:00
|
|
|
}
|
|
|
|
return {parsed:parsed, 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();
|
2020-05-19 22:07:16 +00:00
|
|
|
return {parsed:{type:e.type, val:e.name}, stream:stream};
|
2020-05-19 17:26:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* takes in stream, outputs parsed item or null */
|
2020-06-02 11:41:24 +00:00
|
|
|
const parseNum = (stream) => {
|
2020-05-19 17:26:44 +00:00
|
|
|
let e = stream[0];
|
|
|
|
if (e === undefined) {
|
|
|
|
return {parsed:null, stream:stream};
|
|
|
|
}
|
2020-06-02 11:41:24 +00:00
|
|
|
if (e.type !== "num") {
|
2020-05-19 17:26:44 +00:00
|
|
|
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 */
|
2020-05-20 11:04:37 +00:00
|
|
|
const parseSyntax = (syntax) => (stream) => {
|
2020-05-19 17:26:44 +00:00
|
|
|
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);
|
2020-05-19 22:07:16 +00:00
|
|
|
if (id.parsed === null) {
|
2020-05-19 23:11:31 +00:00
|
|
|
return {parsed:null, stream:id.stream};
|
2020-05-19 17:26:44 +00:00
|
|
|
}
|
2020-05-20 11:04:37 +00:00
|
|
|
let syn = parseSyntax(";")(id.stream);
|
2020-05-19 22:07:16 +00:00
|
|
|
if (syn.parsed === null) {
|
2020-05-19 17:26:44 +00:00
|
|
|
throw 'could not parse name!'
|
|
|
|
}
|
2020-05-19 23:11:31 +00:00
|
|
|
return {parsed:id.parsed.val, stream:syn.stream};
|
2020-05-19 17:26:44 +00:00
|
|
|
}
|
|
|
|
|
2020-05-30 22:49:46 +00:00
|
|
|
const parseType = (stream) => {
|
2020-05-30 19:05:05 +00:00
|
|
|
let syn = attempt(parseSyntax("\""))(stream);
|
|
|
|
if (syn.parsed === null) {
|
|
|
|
return {parsed:null, stream:syn.stream};
|
|
|
|
}
|
2020-06-02 11:41:24 +00:00
|
|
|
let id = or(parseIdent, parseNum)(syn.stream);
|
2020-05-30 19:05:05 +00:00
|
|
|
if (id.parsed === null) {
|
|
|
|
return {parsed:null, stream:id.stream};
|
|
|
|
}
|
2020-05-30 22:49:46 +00:00
|
|
|
return {parsed:{type:"type", val:id.parsed.val}, stream:id.stream};
|
2020-05-30 19:05:05 +00:00
|
|
|
|
|
|
|
//return {parsed:null, stream:stream};
|
|
|
|
}
|
|
|
|
|
2020-06-03 16:54:59 +00:00
|
|
|
const parseString = (stream) => {
|
|
|
|
let e = stream[0];
|
|
|
|
if (e === undefined) {
|
|
|
|
return {parsed:null, stream:stream};
|
|
|
|
}
|
|
|
|
if (e.type !== "string") {
|
|
|
|
return {parsed:null, stream:stream};
|
|
|
|
} else {
|
|
|
|
stream.shift();
|
|
|
|
return {parsed:{type:e.type, val:e.val}, stream:stream};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-20 10:57:26 +00:00
|
|
|
/* takes in stream, outputs parsed item or null - FAILABLE */
|
|
|
|
const parsePush = (stream) => {
|
2020-05-20 11:04:37 +00:00
|
|
|
let syn = attempt(parseSyntax("'"))(stream);
|
2020-05-20 10:57:26 +00:00
|
|
|
if (syn.parsed === null) {
|
|
|
|
return {parsed:null, stream:syn.stream};
|
|
|
|
}
|
2020-05-20 11:04:37 +00:00
|
|
|
let id = parseExpr(syn.stream);
|
2020-05-20 10:57:26 +00:00
|
|
|
if (id.parsed === null) {
|
|
|
|
return {parsed:null, stream:id.stream};
|
|
|
|
}
|
|
|
|
return {parsed:{type:"push", elem:id.parsed}, stream:id.stream};
|
|
|
|
}
|
|
|
|
|
2020-05-30 23:40:37 +00:00
|
|
|
const parseDefn = (stream) => {
|
|
|
|
let name = parseName(stream);
|
|
|
|
if (name.parsed === null) {
|
2020-05-31 01:52:02 +00:00
|
|
|
throw 'no name found!'
|
2020-05-30 23:40:37 +00:00
|
|
|
}
|
2020-05-31 01:52:02 +00:00
|
|
|
let expr = parseExprs(stream);
|
2020-05-30 23:40:37 +00:00
|
|
|
if (expr.parsed === null) {
|
2020-05-31 01:52:02 +00:00
|
|
|
throw 'no body found!'
|
2020-05-30 23:40:37 +00:00
|
|
|
}
|
|
|
|
return {parsed:{type:"defn", ident:name.parsed, defn:expr.parsed}, stream:expr.stream}
|
|
|
|
}
|
|
|
|
|
2020-05-19 17:26:44 +00:00
|
|
|
/* takes in stream, outputs parsed item or null - FAILABLE */
|
|
|
|
const parseLambda = (stream) => {
|
2020-05-30 23:40:37 +00:00
|
|
|
let args = many(parseIdent)(stream);
|
2020-05-20 11:04:37 +00:00
|
|
|
let syn = parseSyntax("->")(args.stream);
|
2020-05-19 23:11:31 +00:00
|
|
|
if (syn.parsed === null) {
|
2020-05-19 17:26:44 +00:00
|
|
|
throw 'no lambda body found!';
|
|
|
|
}
|
2020-05-19 23:11:31 +00:00
|
|
|
let body = parseExprs(syn.stream); // .parsed should never be null, but anyway...
|
|
|
|
if (body.parsed === null) {
|
2020-05-19 17:26:44 +00:00
|
|
|
throw 'no lambda body found!';
|
|
|
|
}
|
2020-05-30 23:40:37 +00:00
|
|
|
return {parsed:{type:"func", args:args.parsed.map(x => x.val), body:body.parsed}, stream:body.stream};
|
2020-05-19 17:26:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* takes in stream, outputs parsed item or null */
|
2020-05-30 23:40:37 +00:00
|
|
|
const parseExpr = or(
|
|
|
|
attempt(parens(parseDefn)), or(
|
|
|
|
attempt(parens(parseLambda)), or(
|
|
|
|
attempt(parseLambda), or(
|
|
|
|
parseType, or(
|
|
|
|
parseIdent, or(
|
2020-06-03 16:54:59 +00:00
|
|
|
parseNum, or(
|
|
|
|
parseString,
|
2020-05-30 23:40:37 +00:00
|
|
|
parsePush
|
2020-06-03 16:54:59 +00:00
|
|
|
)))))));
|
2020-05-19 17:26:44 +00:00
|
|
|
|
|
|
|
/* takes in stream, outputs parsed items */
|
2020-05-21 14:19:31 +00:00
|
|
|
export const parseExprs = many(parseExpr);
|