summaryrefslogtreecommitdiff
path: root/src/eval.ts
diff options
context:
space:
mode:
authorAkshay Nair <phenax5@gmail.com>2023-08-20 11:16:52 +0530
committerAkshay Nair <phenax5@gmail.com>2023-08-20 11:16:52 +0530
commit2b94b07fdc8f1a82f507b99000add9b7dca2c3d8 (patch)
treec96e3c0992cc24145c9b42cbaf8ea8f607c6b73c /src/eval.ts
parent3ff7d709998fe33f82e6da50d7b3327b076a1039 (diff)
downloadcss-everything-2b94b07fdc8f1a82f507b99000add9b7dca2c3d8.tar.gz
css-everything-2b94b07fdc8f1a82f507b99000add9b7dca2c3d8.zip
refactor: adds evalvalue type instead of string
Diffstat (limited to '')
-rw-r--r--src/eval.ts171
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')),