UNPKG

functionalscript

Version:

FunctionalScript is a purely functional subset of JavaScript

159 lines (158 loc) 5.96 kB
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'); } };