functionalscript
Version:
FunctionalScript is a purely functional subset of JavaScript
278 lines (277 loc) • 7.37 kB
JavaScript
import { cp, range, remove, set, str } from "../func/module.f.js";
import { parser, toRuleMap } from "./module.f.js";
import { classic } from "../func/testlib.f.js";
import * as j from "../../json/module.f.js";
import { sort } from "../../types/object/module.f.js";
import { stringToCodePointList } from "../../text/utf16/module.f.js";
import { toArray } from "../../types/list/module.f.js";
const stringify = j.stringify(sort);
const classicTest = () => {
const map = {
json: [
['element']
],
value: [
['object'],
['array'],
['string'],
['number'],
str('true'),
str('false'),
str('null'),
],
object: [
[cp('{'), 'ws', cp('}')],
[cp('{'), 'members', cp('}')],
],
members: [
['member'],
['member', cp(','), 'members'],
],
member: [
['ws', 'string', 'ws', cp(':'), 'element'],
],
array: [
[cp('['), 'ws', cp(']')],
[cp('['), 'elements', cp(']')],
],
elements: [
['element'],
['element', cp(','), 'elements'],
],
element: [
['ws', 'value', 'ws'],
],
string: [
[cp('"'), 'characters', cp('"')],
],
characters: [
[],
['character', 'characters'],
],
character: [
...remove([0x20, 0x10FFFF], [cp('"'), cp('\\')]),
[cp('\\'), 'escape'], // 92
],
escape: [
str('"'),
str('\\'),
str('/'),
str('b'),
str('f'),
str('n'),
str('r'),
str('t'),
[cp('u'), 'hex', 'hex', 'hex', 'hex'],
],
hex: [
['digit'],
[range('AF')], // A-F
[range('af')], // a-f
],
number: [
['integer', 'fraction', 'exponent'],
],
integer: [
['digit'],
['onenine', 'digits'],
[cp('-'), 'digit'],
[cp('-'), 'onenine', 'digits'],
],
digits: [
['digit'],
['digit', 'digits'],
],
digit: [
[cp('0')],
['onenine'],
],
onenine: [
[range('19')],
],
fraction: [
[],
[cp('.'), 'digits'],
],
exponent: [
[],
[cp('E'), 'sign', 'digits'],
[cp('e'), 'sign', 'digits'],
],
sign: [
[],
[cp('+')],
[cp('-')],
],
ws: [
[],
[cp(' '), 'ws'],
[cp('\n'), 'ws'],
[cp('\r'), 'ws'],
[cp('\t'), 'ws'],
],
};
const result = map;
return result;
};
const repeat0Name = (v) => `${v}Repeat0`;
const repeat0Body = (v) => [
[],
[v, repeat0Name(v)]
];
const repeat0 = (v) => {
const name = repeat0Name(v);
const body = repeat0Body(v);
return { [name]: body };
};
const deterministic = () => {
const map = {
json: [
['wsRepeat0', 'element']
],
value: [
[cp('{'), 'wsRepeat0', 'object', cp('}')],
[cp('['), 'wsRepeat0', 'array', cp(']')],
['string'],
['number'],
str('true'),
str('false'),
str('null'),
],
object: [
[],
['member', 'memberTailRepeat0'],
],
memberTail: [
[cp(','), 'wsRepeat0', 'member']
],
...repeat0('memberTail'),
member: [
['string', 'wsRepeat0', cp(':'), 'wsRepeat0', 'element'],
],
array: [
[],
['element', 'elements'],
],
elements: [
[],
[cp(','), 'wsRepeat0', 'element', 'elements'],
],
element: [
['value', 'wsRepeat0'],
],
string: [
[cp('"'), 'characterRepeat0', cp('"')],
],
...repeat0('character'),
character: [
...remove([0x20, 0x10FFFF], [cp('"'), cp('\\')]),
[cp('\\'), 'escape'], // 92
],
escape: [
...set('"\\/bfnrt'),
[cp('u'), 'hex', 'hex', 'hex', 'hex'],
],
hex: [
['digit'],
[range('AF')],
[range('af')],
],
numberSign: [
[],
[cp('-')]
],
number: [
['numberSign', 'integer', 'fraction', 'exponent'],
],
integer: [
[cp('0')],
['onenine', 'digitRepeat0'],
],
...repeat0('digit'),
digit: [
[cp('0')],
['onenine'],
],
onenine: [
[range('19')],
],
fraction: [
[],
[cp('.'), 'digit', 'digitRepeat0'],
],
e: set('Ee'),
exponent: [
[],
['e', 'sign', 'digit', 'digitRepeat0'],
],
sign: [
[],
[cp('+')],
[cp('-')],
],
ws: set(' \n\r\t'),
...repeat0('ws'),
};
const _map = map;
return _map;
};
export default {
example: {
module: () => {
// Define a simple grammar
const grammar = () => [
[range('AZ')], // 'A-Z'
[range('az'), grammar], // 'a-z' followed by more grammar
];
const ruleMap = toRuleMap(grammar);
const parse = parser(ruleMap);
// Parse an input
const input = toArray(stringToCodePointList('abcdefgA'));
const result = parse(grammar.name, input);
if (result === null) {
throw result;
}
const [, b] = result;
if (b?.length !== 0) {
throw b;
}
},
},
classic: () => {
const c = classic();
const json = stringify(toRuleMap(c.json));
const jsonE = stringify(classicTest());
if (json !== jsonE) {
//console.error(json)
//console.error(jsonE)
throw [json, jsonE];
}
},
map: () => {
const f = parser(deterministic());
// console.error(stringify(x))
//
const isSuccess = (s) => s?.length === 0;
const expect = (s, success) => {
const [a, r] = f('json', toArray(stringToCodePointList(s)));
if (isSuccess(r) !== success) {
throw r;
}
};
//
expect(' true ', true);
expect(' tr2ue ', false);
expect(' true" ', false);
expect(' "Hello" ', true);
expect(' "Hello ', false);
expect(' "Hello\\n\\r\\"" ', true);
expect(' -56.7e+5 ', true);
expect(' h-56.7e+5 ', false);
expect(' -56.7e+5 3', false);
expect(' [ 12, false, "a"] ', true);
expect(' [ 12, false2, "a"] ', false);
expect(' { "q": [ 12, false, [{}], "a"] } ', true);
expect(' { "q": [ 12, false, [}], "a"] } ', false);
}
};