diff options
| author | Akshay Nair <phenax5@gmail.com> | 2023-08-20 11:16:52 +0530 |
|---|---|---|
| committer | Akshay Nair <phenax5@gmail.com> | 2023-08-20 11:16:52 +0530 |
| commit | 2b94b07fdc8f1a82f507b99000add9b7dca2c3d8 (patch) | |
| tree | c96e3c0992cc24145c9b42cbaf8ea8f607c6b73c /src/eval.ts | |
| parent | 3ff7d709998fe33f82e6da50d7b3327b076a1039 (diff) | |
| download | css-everything-2b94b07fdc8f1a82f507b99000add9b7dca2c3d8.tar.gz css-everything-2b94b07fdc8f1a82f507b99000add9b7dca2c3d8.zip | |
refactor: adds evalvalue type instead of string
Diffstat (limited to '')
| -rw-r--r-- | src/eval.ts | 171 |
1 files changed, 124 insertions, 47 deletions
diff --git a/src/eval.ts b/src/eval.ts index 01d29c6..26b3df0 100644 --- a/src/eval.ts +++ b/src/eval.ts @@ -1,5 +1,5 @@ import { CSSUnit, Expr } from './parser' -import { match, matchString } from './utils/adt' +import { Enum, constructors, match, matchString } from './utils/adt' export interface EvalActions { addClass(id: string, classes: string): Promise<void> @@ -34,12 +34,27 @@ export interface EvalActions { callMethod( id: string | undefined, method: string, - args: EvalValue[], + args: (string | undefined)[], ): Promise<void> // calculate ?? } -type EvalValue = string | undefined | void +export type EvalValue = Enum<{ + String: string + Number: number + Boolean: boolean + Lazy: Expr[] + Void: never + VarIdentifier: string + // Object: Record<any, any> +}> +export const EvalValue = constructors<EvalValue>() + +export const evalExprAsString = async ( + expr: Expr, + actions: EvalActions, +): Promise<string | undefined> => + evalValueToString(await evalExpr(expr, actions)) export const evalExpr = async ( expr: Expr, @@ -47,64 +62,104 @@ export const evalExpr = async ( ): Promise<EvalValue> => match<Promise<EvalValue>, Expr>(expr, { Call: async ({ name, args }) => getFunctions(name, args, actions), - LiteralString: async s => s, + LiteralString: async s => EvalValue.String(s), LiteralNumber: async ({ value, unit }) => - matchString<number, CSSUnit>(unit, { - s: () => value * 1000, - _: () => value, - }).toString(), - Identifier: async s => s, - VarIdentifier: async s => s, - _: async _ => undefined, + EvalValue.Number( + matchString<number, CSSUnit>(unit, { + s: () => value * 1000, + _: () => value, + }), + ), + Identifier: async s => EvalValue.String(s), + VarIdentifier: async s => EvalValue.VarIdentifier(s), + _: async _ => EvalValue.Void(), + }) + +const evalValueToString = (val: EvalValue): string | undefined => + match<string | undefined, EvalValue>(val, { + String: s => s.replace(/(^'|")|('|"$)/g, ''), + Boolean: b => `${b}`, + Number: n => `${n}`, + _: () => undefined, + }) + +const evalValueToNumber = (val: EvalValue): number | undefined => + match<number | undefined, EvalValue>(val, { + String: s => parseInt(s, 10), + Boolean: b => (b ? 1 : 0), + Number: n => n, + _: () => undefined, }) -const getFunctions = (name: string, args: Expr[], actions: EvalActions) => { +const evalValueToBoolean = (val: EvalValue): boolean => + match<boolean, EvalValue>(val, { + String: s => !['false', '', '0'].includes(s.replace(/(^'|")|('|"$)/g, '')), + Boolean: b => b, + Number: n => !!n, + _: () => false, + }) + +const getFunctions = ( + name: string, + args: Expr[], + actions: EvalActions, +): Promise<EvalValue> => { const getVariable = async () => { const varName = await evalExpr(args[0], actions) const defaultValue = args[1] && (await evalExpr(args[1], actions)) - return varName && (actions.getVariable(varName) ?? defaultValue) + + return match<Promise<EvalValue>, EvalValue>(varName, { + VarIdentifier: async name => { + const value = await actions.getVariable(name) + return value === undefined ? defaultValue : EvalValue.String(value) + }, + _: async () => EvalValue.Void(), + }) } return matchString<Promise<EvalValue>>(name, { 'add-class': async () => { - const id = await evalExpr(args[0], actions) - const classes = await evalExpr(args[1], actions) + const id = evalValueToString(await evalExpr(args[0], actions)) + const classes = evalValueToString(await evalExpr(args[1], actions)) if (id && classes) { await actions.addClass(id, classes) } + return EvalValue.Void() }, 'remove-class': async () => { - const id = await evalExpr(args[0], actions) - const classes = await evalExpr(args[1], actions) + const id = evalValueToString(await evalExpr(args[0], actions)) + const classes = evalValueToString(await evalExpr(args[1], actions)) if (id && classes) { await actions.removeClass(id, classes) } + return EvalValue.Void() }, if: async () => { - const cond = await evalExpr(args[0], actions) - const FALSEY = ['0', 'false'] - if (cond && !FALSEY.includes(cond.replace(/(^'|")|('|"$)/g, ''))) { + const cond = evalValueToBoolean(await evalExpr(args[0], actions)) + if (cond) { return evalExpr(args[1], actions) } else { return evalExpr(args[2], actions) } }, delay: async () => { - const num = await evalExpr(args[0], actions) - num && (await actions.delay(parseInt(num, 10))) + const num = evalValueToNumber(await evalExpr(args[0], actions)) + num !== undefined ? await actions.delay(num) : undefined + return EvalValue.Void() }, 'js-eval': async () => { - const js = await evalExpr(args[0], actions) + const js = evalValueToString(await evalExpr(args[0], actions)) return js && (await actions.jsEval(js)) }, 'load-cssx': async () => { - const id = await evalExpr(args[0], actions) - const url = await evalExpr(args[1], actions) + const id = evalValueToString(await evalExpr(args[0], actions)) + const url = evalValueToString(await evalExpr(args[1], actions)) if (id && url) { await actions.loadCssx(id, url) } + return EvalValue.Void() }, var: getVariable, @@ -113,59 +168,81 @@ const getFunctions = (name: string, args: Expr[], actions: EvalActions) => { update: async () => { const [id, name, value] = args.length >= 3 - ? await evalArgs(args, 3, actions) - : [undefined, ...(await evalArgs(args, 2, actions))] + ? (await evalArgs(args, 3, actions)).map(evalValueToString) + : [ + undefined, + ...(await evalArgs(args, 2, actions)).map(evalValueToString), + ] if (name) { - actions.updateVariable(id ?? undefined, name, value ?? '') + await actions.updateVariable(id ?? undefined, name, value ?? '') } + return EvalValue.Void() }, 'set-attr': async () => { const [id, name, value] = args.length >= 3 - ? await evalArgs(args, 3, actions) - : [undefined, ...(await evalArgs(args, 2, actions))] + ? (await evalArgs(args, 3, actions)).map(evalValueToString) + : [ + undefined, + ...(await evalArgs(args, 2, actions)).map(evalValueToString), + ] if (name) { actions.setAttribute(id ?? undefined, name, value ?? '') } + return EvalValue.Void() }, attr: async () => { const [id, name] = args.length >= 2 - ? await evalArgs(args, 2, actions) - : [undefined, await evalExpr(args[0], actions)] + ? (await evalArgs(args, 2, actions)).map(evalValueToString) + : [undefined, evalValueToString(await evalExpr(args[0], actions))] if (name) { - return actions.getAttribute(id as string | undefined, name) + const val = await actions.getAttribute(id as string | undefined, name) + return val === undefined ? EvalValue.Void() : EvalValue.String(val) } + return EvalValue.Void() }, - 'prevent-default': async () => actions.withEvent(e => e.preventDefault()), + 'prevent-default': async () => { + await actions.withEvent(e => e.preventDefault()) + return EvalValue.Void() + }, request: async () => { - const url = await evalExpr(args[0], actions) - const method = (args[1] && (await evalExpr(args[1], actions))) ?? 'post' + const url = evalValueToString(await evalExpr(args[0], actions)) + const method = + (args[1] && evalValueToString(await evalExpr(args[1], actions))) || + 'post' if (url) { const data = await actions.getFormData() await actions.sendRequest({ method, url, data }) } + return EvalValue.Void() }, 'add-children': async () => { - const id = await evalExpr(args[0], actions) - if (id) actions.addChildren(id, args.slice(1)) + const id = evalValueToString(await evalExpr(args[0], actions)) + if (id) await actions.addChildren(id, args.slice(1)) + return EvalValue.Void() }, - 'remove-element': async () => - actions.removeElement( - (args[0] && (await evalExpr(args[0], actions))) ?? undefined, - ), - call: async () => { - const [id, method, ...methodArgs] = await Promise.all( - args.map(a => evalExpr(a, actions)), - ) + 'remove-element': async () => { + const selector = + (args[0] && evalValueToString(await evalExpr(args[0], actions))) ?? + undefined + if (selector) await actions.removeElement(selector) + return EvalValue.Void() + }, + + 'call-method': async () => { + const [id, method, ...methodArgs] = ( + await Promise.all(args.map(a => evalExpr(a, actions))) + ).map(evalValueToString) if (id && method) { - actions.callMethod(id, method, methodArgs) + await actions.callMethod(id, method, methodArgs) } + return EvalValue.Void() }, _: () => Promise.reject(new Error('not supposed to be here')), |
