implement most of the TS version
This commit is contained in:
parent
3f90dda2e7
commit
e553b51dc0
|
@ -1,5 +1,24 @@
|
||||||
import { BinExpr, emitCont, Result } from './Parse.js';
|
import { BinExpr, emitBack, emitCont, isCont, result, Result } from './Parse.js';
|
||||||
|
|
||||||
export function evalBin(b: Result<BinExpr>): Result<number> {
|
export function evalBin(b: Result<BinExpr>): Result<number> {
|
||||||
return emitCont(0, b.rem); // TODO
|
if (isCont(b)) {
|
||||||
|
const br = result(b);
|
||||||
|
|
||||||
|
if (br.lhs.type === 'digit' && br.oper.type === 'oper' && br.rhs.type === 'digit') {
|
||||||
|
switch (br.oper.val) {
|
||||||
|
case '+':
|
||||||
|
return emitCont(br.lhs.val + br.rhs.val, b.rem);
|
||||||
|
case '-':
|
||||||
|
return emitCont(br.lhs.val - br.rhs.val, b.rem);
|
||||||
|
case '*':
|
||||||
|
return emitCont(br.lhs.val * br.rhs.val, b.rem);
|
||||||
|
case '/':
|
||||||
|
return emitCont(br.lhs.val / br.rhs.val, b.rem);
|
||||||
|
default:
|
||||||
|
return emitBack(b.rem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return emitBack(b.rem);
|
||||||
}
|
}
|
|
@ -32,3 +32,11 @@ export function isJust<T>(m: Maybe<T>): m is Just<T> {
|
||||||
export function isNothing<T>(m: Maybe<T>): m is Nothing {
|
export function isNothing<T>(m: Maybe<T>): m is Nothing {
|
||||||
return Object.getOwnPropertyDescriptor(m, SymbolNothing) !== undefined;
|
return Object.getOwnPropertyDescriptor(m, SymbolNothing) !== undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function mustExist<T>(m: T | undefined): T {
|
||||||
|
if (m === undefined) {
|
||||||
|
throw new Error('this is where things start to go wrong');
|
||||||
|
} else {
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { isJust, isNothing, Just, just, Maybe, Nothing, nothing } from './Maybe.js';
|
import { isJust, isNothing, Just, just, Maybe, mustExist, Nothing, nothing, SymbolJust } from './Maybe.js';
|
||||||
import { map, split } from './Util.js';
|
import { map, primStringToList, split } from './Util.js';
|
||||||
|
|
||||||
export type Token = {
|
export type Token = {
|
||||||
type: 'digit';
|
type: 'digit';
|
||||||
|
@ -17,6 +17,33 @@ export type Token = {
|
||||||
type: 'term';
|
type: 'term';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export function digit(val: number): Token {
|
||||||
|
return {
|
||||||
|
type: 'digit',
|
||||||
|
val,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function oper(val: string): Token {
|
||||||
|
return {
|
||||||
|
type: 'oper',
|
||||||
|
val,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function skip(val: string): Token {
|
||||||
|
return {
|
||||||
|
type: 'skip',
|
||||||
|
val,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function term(): Token {
|
||||||
|
return {
|
||||||
|
type: 'term',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export interface BinExpr {
|
export interface BinExpr {
|
||||||
oper: Token;
|
oper: Token;
|
||||||
lhs: Token;
|
lhs: Token;
|
||||||
|
@ -25,21 +52,36 @@ export interface BinExpr {
|
||||||
|
|
||||||
export interface Result<T, R = Maybe<T>> {
|
export interface Result<T, R = Maybe<T>> {
|
||||||
res: R;
|
res: R;
|
||||||
rem: Array<string>;
|
rem: ReadonlyArray<string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function emitCont<T>(val: T, rem: Array<string>): Result<T> {
|
export const DIGITS = primStringToList("0123456789");
|
||||||
|
export const OPERS = primStringToList("-+*/");
|
||||||
|
export const SKIPS = primStringToList(" ");
|
||||||
|
|
||||||
|
export function emit<T>(val: Maybe<T>, rem: ReadonlyArray<string>): Result<T> {
|
||||||
|
return {
|
||||||
|
res: val,
|
||||||
|
rem,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function emitBack<T>(rem: ReadonlyArray<string>): Result<T> {
|
||||||
|
return {
|
||||||
|
res: nothing(),
|
||||||
|
rem,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function emitCont<T>(val: T, rem: ReadonlyArray<string>): Result<T> {
|
||||||
return {
|
return {
|
||||||
res: just(val),
|
res: just(val),
|
||||||
rem,
|
rem,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function emitBack<T>(rem: Array<string>): Result<T> {
|
export function result<T>(r: Result<T, Just<T>>): T {
|
||||||
return {
|
return r.res[SymbolJust];
|
||||||
res: nothing(),
|
|
||||||
rem,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isCont<T>(r: Result<T>): r is Result<T, Just<T>> {
|
export function isCont<T>(r: Result<T>): r is Result<T, Just<T>> {
|
||||||
|
@ -50,34 +92,133 @@ export function isBack<T>(r: Result<T>): r is Result<T, Nothing> {
|
||||||
return isNothing(r.res);
|
return isNothing(r.res);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function takeCons(cs: Array<string>, xs: Array<string>): Result<Array<string>> {
|
export function takeCons(cs: ReadonlyArray<string>, xs: ReadonlyArray<string>): Result<ReadonlyArray<string>> {
|
||||||
return emitBack([]); // TODO
|
const acc: Array<string> = [];
|
||||||
|
const rem = Array.from(xs);
|
||||||
|
|
||||||
|
while (cs.includes(rem[0])) {
|
||||||
|
acc.push(mustExist(rem.shift()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return emitCont(acc, rem)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ignoreConst(cs: Array<string>, xs: Array<string>): Array<string> {
|
export function ignoreCons(cs: ReadonlyArray<string>, xs: ReadonlyArray<string>): ReadonlyArray<string> {
|
||||||
return xs; // TODO
|
const rem = Array.from(xs);
|
||||||
|
|
||||||
|
while (cs.includes(rem[0])) {
|
||||||
|
rem.shift();
|
||||||
|
}
|
||||||
|
|
||||||
|
return rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function parseChar(): Result<Token> {
|
export function parseChar(c: string): Token {
|
||||||
return emitBack([]); // TODO
|
switch (c) {
|
||||||
|
case '0':
|
||||||
|
return digit(0);
|
||||||
|
case '1':
|
||||||
|
return digit(1);
|
||||||
|
case '2':
|
||||||
|
return digit(2);
|
||||||
|
case '3':
|
||||||
|
return digit(3);
|
||||||
|
case '4':
|
||||||
|
return digit(4);
|
||||||
|
case '5':
|
||||||
|
return digit(5);
|
||||||
|
case '6':
|
||||||
|
return digit(6);
|
||||||
|
case '7':
|
||||||
|
return digit(7);
|
||||||
|
case '8':
|
||||||
|
return digit(8);
|
||||||
|
case '9':
|
||||||
|
return digit(9);
|
||||||
|
case '+':
|
||||||
|
case '-':
|
||||||
|
case '*':
|
||||||
|
case '/':
|
||||||
|
return oper(c);
|
||||||
|
case ' ':
|
||||||
|
return skip(c);
|
||||||
|
default:
|
||||||
|
return {
|
||||||
|
type: 'term',
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function parseNat(): Result<number> {
|
export function parseNat(a: Maybe<number>, cs: ReadonlyArray<string>): Result<number> {
|
||||||
return emitBack([]); // TODO
|
if (cs.length === 0) {
|
||||||
|
return emit(a, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
const [x, ...xs] = cs;
|
||||||
|
const t = parseChar(x);
|
||||||
|
|
||||||
|
if (t.type === 'digit') {
|
||||||
|
return emitCont(t.val, xs)
|
||||||
|
} else {
|
||||||
|
return emitBack(cs);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function takeNat(): Result<number> {
|
export function takeNat(s: ReadonlyArray<string>): Result<number> {
|
||||||
return emitBack([]); // TODO
|
const cs = takeCons(DIGITS, s);
|
||||||
|
|
||||||
|
if (isCont(cs)) {
|
||||||
|
const n = parseNat(nothing(), result(cs));
|
||||||
|
|
||||||
|
if (isCont(n)) {
|
||||||
|
return emitCont(result(n), cs.rem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return emitBack(cs.rem);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function parseOper() {}
|
export function parseOper(s: ReadonlyArray<string>): Result<Token> {
|
||||||
|
if (s.length === 0) {
|
||||||
|
return emitBack([]);
|
||||||
|
}
|
||||||
|
|
||||||
export function takeOper(): Result<Token> {
|
const [x, ...xs] = s;
|
||||||
return emitBack([]); // TODO
|
const o = parseChar(x);
|
||||||
|
|
||||||
|
if (o.type === 'oper') {
|
||||||
|
return emitCont(o, xs);
|
||||||
|
} else {
|
||||||
|
return emitBack(xs);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function takeBin(): Result<BinExpr> {
|
export function takeOper(s: ReadonlyArray<string>): Result<Token> {
|
||||||
return emitBack([]); // TODO
|
return parseOper(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function takeBin(s: ReadonlyArray<string>): Result<BinExpr> {
|
||||||
|
const lhs = takeNat(ignoreCons(SKIPS, s));
|
||||||
|
|
||||||
|
if (isCont(lhs)) {
|
||||||
|
const oper = takeOper(ignoreCons(SKIPS, lhs.rem));
|
||||||
|
|
||||||
|
if (isCont(oper)) {
|
||||||
|
const rhs = takeNat(ignoreCons(SKIPS, oper.rem));
|
||||||
|
|
||||||
|
if (isCont(rhs)) {
|
||||||
|
const bin: BinExpr = {
|
||||||
|
lhs: digit(result(lhs)),
|
||||||
|
rhs: digit(result(rhs)),
|
||||||
|
oper: result(oper),
|
||||||
|
};
|
||||||
|
|
||||||
|
return emitCont(bin, rhs.rem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return emitBack(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function takeLine(c: Array<string>): Array<Result<BinExpr>> {
|
export function takeLine(c: Array<string>): Array<Result<BinExpr>> {
|
||||||
|
|
|
@ -5,14 +5,14 @@ export function show(n: number): string {
|
||||||
return n.toString();
|
return n.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function showList<T>(f: (t: T) => string, arr: Array<T>): string {
|
export function showList<T>(f: (t: T) => string, arr: ReadonlyArray<T>): string {
|
||||||
return arr.map(f).join(', ');
|
return arr.map(f).join(', ');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function showResult<T>(f: (t: T) => string, r: Result<T>): string {
|
export function showResult<T>(f: (t: T) => string, r: Result<T>): string {
|
||||||
if (isCont(r)) {
|
if (isCont(r)) {
|
||||||
return f(r.res[SymbolJust]);
|
return 'result: ' + f(r.res[SymbolJust]);
|
||||||
} else {
|
} else {
|
||||||
return r.rem.join('');
|
return 'remainder: ' + r.rem.join('');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue