functionalscript
Version:
FunctionalScript is a purely functional subset of JavaScript
159 lines (158 loc) • 5.96 kB
JavaScript
import * as result from "../../types/result/module.f.js";
import { fold, first, drop, toArray, concat } from "../../types/list/module.f.js";
import { setReplace } from "../../types/ordered_map/module.f.js";
import { fromMap } from "../../types/object/module.f.js";
const addKeyToObject = obj => key => ({ kind: 'object', values: obj.values, key: key });
const addValueToObject = obj => value => ({ kind: 'object', values: setReplace(obj.key)(value)(obj.values), key: '' });
const addToArray = array => value => ({ kind: 'array', values: concat(array.values)([value]) });
const pushKey = state => value => {
if (state.top?.kind === 'object') {
return { status: '{k', top: addKeyToObject(state.top)(value), stack: state.stack };
}
return { status: 'error', message: 'error' };
};
const pushValue = state => value => {
if (state.top === null) {
return { status: 'result', value: value };
}
if (state.top.kind === 'array') {
return { status: '[v', top: addToArray(state.top)(value), stack: state.stack };
}
return { status: '{v', top: addValueToObject(state.top)(value), stack: state.stack };
};
const startArray = state => {
const newStack = state.top === null ? null : { first: state.top, tail: state.stack };
return { status: '[', top: { kind: 'array', values: null }, stack: newStack };
};
const endArray = state => {
const array = state.top !== null ? toArray(state.top.values) : null;
const newState = { status: '', top: first(null)(state.stack), stack: drop(1)(state.stack) };
return pushValue(newState)(array);
};
const startObject = state => {
const newStack = state.top === null ? null : { first: state.top, tail: state.stack };
return { status: '{', top: { kind: 'object', values: null, key: '' }, stack: newStack };
};
const endObject = state => {
const obj = state.top?.kind === 'object' ? fromMap(state.top.values) : null;
const newState = { status: '', top: first(null)(state.stack), stack: drop(1)(state.stack) };
return pushValue(newState)(obj);
};
const tokenToValue = token => {
switch (token.kind) {
case 'null': return null;
case 'false': return false;
case 'true': return true;
case 'number': return parseFloat(token.value);
case 'string': return token.value;
default: return null;
}
};
const isValueToken = token => {
switch (token.kind) {
case 'null':
case 'false':
case 'true':
case 'number':
case 'string': return true;
default: return false;
}
};
const parseValueOp = token => state => {
switch (token.kind) {
case ']':
if (state.status === '[,') {
return endArray(state);
}
return { status: 'error', message: 'unexpected token' };
case '[': return startArray(state);
case '{': return startObject(state);
default:
if (isValueToken(token)) {
return pushValue(state)(tokenToValue(token));
}
return { status: 'error', message: 'unexpected token' };
}
};
const parseArrayStartOp = token => state => {
if (isValueToken(token)) {
return pushValue(state)(tokenToValue(token));
}
if (token.kind === '[') {
return startArray(state);
}
if (token.kind === ']') {
return endArray(state);
}
if (token.kind === '{') {
return startObject(state);
}
return { status: 'error', message: 'unexpected token' };
};
const parseArrayValueOp = token => state => {
if (token.kind === ']') {
return endArray(state);
}
if (token.kind === ',') {
return { status: '[,', top: state.top, stack: state.stack };
}
return { status: 'error', message: 'unexpected token' };
};
const parseObjectStartOp = token => state => {
if (token.kind === 'string') {
return pushKey(state)(token.value);
}
if (token.kind === '}') {
return endObject(state);
}
return { status: 'error', message: 'unexpected token' };
};
const parseObjectKeyOp = token => state => {
if (token.kind === ':') {
return { status: '{:', top: state.top, stack: state.stack };
}
return { status: 'error', message: 'unexpected token' };
};
const parseObjectNextOp = token => state => {
if (token.kind === '}') {
return endObject(state);
}
if (token.kind === ',') {
return { status: '{,', top: state.top, stack: state.stack };
}
return { status: 'error', message: 'unexpected token' };
};
const parseObjectCommaOp = token => state => {
if (token.kind === '}') {
return endObject(state);
}
if (token.kind === 'string') {
return pushKey(state)(token.value);
}
return { status: 'error', message: 'unexpected token' };
};
const foldOp = token => state => {
if (token.kind === 'eof')
return state;
switch (state.status) {
case 'result': return { status: 'error', message: 'unexpected token' };
case 'error': return { status: 'error', message: state.message };
case '': return parseValueOp(token)(state);
case '[': return parseArrayStartOp(token)(state);
case '[v': return parseArrayValueOp(token)(state);
case '[,': return parseValueOp(token)(state);
case '{': return parseObjectStartOp(token)(state);
case '{k': return parseObjectKeyOp(token)(state);
case '{:': return parseValueOp(token)(state);
case '{v': return parseObjectNextOp(token)(state);
case '{,': return parseObjectCommaOp(token)(state);
}
};
export const parse = tokenList => {
const state = fold(foldOp)({ status: '', top: null, stack: null })(tokenList);
switch (state.status) {
case 'result': return result.ok(state.value);
case 'error': return result.error(state.message);
default: return result.error('unexpected end');
}
};