From af65d13038dcdeaf93c5c718cbc74c20120c6a22 Mon Sep 17 00:00:00 2001 From: Akshay Nair Date: Sun, 20 Aug 2023 12:42:06 +0530 Subject: feat: implements function call and adds support for recursion --- src/declarations.ts | 1 + src/eval.ts | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++-- src/index.ts | 51 +++++++++++++++++++++++++++++++++++++++++++------- 3 files changed, 97 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/declarations.ts b/src/declarations.ts index 5c2f9a3..c0966fa 100644 --- a/src/declarations.ts +++ b/src/declarations.ts @@ -62,6 +62,7 @@ export const toDeclaration = (expr: Expr): Declaration | undefined => { _: _ => {}, }) + // TODO: Refactor with eval match(map, { Call: ({ name, args }) => { if (name !== 'map') return diff --git a/src/eval.ts b/src/eval.ts index e421d7a..8112353 100644 --- a/src/eval.ts +++ b/src/eval.ts @@ -1,4 +1,4 @@ -import { CSSUnit, Expr } from './parser' +import { CSSUnit, Expr, parse } from './parser' import { Enum, constructors, match, matchString } from './utils/adt' export interface EvalActions { @@ -36,6 +36,10 @@ export interface EvalActions { method: string, args: (string | undefined)[], ): Promise + evaluateInScope( + exprs: Expr[], + properties: Record, + ): Promise // calculate ?? } @@ -46,6 +50,7 @@ export type EvalValue = Enum<{ Lazy: Expr[] Void: never VarIdentifier: string + Map: { [key in string]: EvalValue } // Object: Record }> export const EvalValue = constructors() @@ -75,7 +80,7 @@ export const evalExpr = async ( _: async _ => EvalValue.Void(), }) -const evalValueToString = (val: EvalValue): string | undefined => +export const evalValueToString = (val: EvalValue): string | undefined => match(val, { String: s => s.replace(/(^'|")|('|"$)/g, ''), Boolean: b => `${b}`, @@ -245,8 +250,53 @@ const getFunctions = ( return EvalValue.Void() }, + map: async () => { + const values = await Promise.all( + args.map(async mapExpr => + match, Expr>(mapExpr, { + Pair: async ({ key, value }) => [ + key, + await evalExpr(value, actions), + ], + _: async () => undefined, + }), + ), + ) + + return EvalValue.Map(Object.fromEntries(values.filter(Boolean) as any)) + }, + func: async () => EvalValue.Lazy(args), + call: async () => { + const varId = match( + await evalExpr(args[0], actions), + { + VarIdentifier: id => id, + _: () => undefined, + }, + ) + + const propMapExpr = await evalExpr(args[1], actions) + const properties = match, EvalValue>( + propMapExpr, + { + Map: m => m, + _: () => ({}), + }, + ) + + if (varId) { + const prop = await actions.getVariable(varId) + if (prop) { + const exprs = parse(prop) + return actions.evaluateInScope(exprs, properties) + } + } + + return EvalValue.Void() + }, + _: () => Promise.reject(new Error(`Not implemented: ${name}`)), }) } diff --git a/src/index.ts b/src/index.ts index 52f3a1b..e7e42cb 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,10 +1,16 @@ -import { EvalActions, evalExpr, evalExprAsString } from './eval' +import { + EvalActions, + EvalValue, + evalExpr, + evalExprAsString, + evalValueToString, +} from './eval' import { extractDeclaration, DeclarationEval, expressionsToDeclrs, } from './declarations' -import { parse } from './parser' +import { Expr, parse } from './parser' import { match, matchString } from './utils/adt' const CSSX_ON_UPDATE_EVENT = 'cssx--update' @@ -88,8 +94,10 @@ const getElement = ( const getEvalActions = ( $element: HTMLElement, - { event = null, pure = false }: { event?: any; pure?: boolean }, + ctx: { event?: any; pure?: boolean }, ): EvalActions => { + const { event = null, pure = false } = ctx + const actions: EvalActions = { addClass: async (id, cls) => getElement(id, $element)?.classList.add(cls), removeClass: async (id, cls) => @@ -172,10 +180,39 @@ const getEvalActions = ( const $el = id ? getElement(id, $element) : $element ;($el as any)[method].call($el, args) }, + + evaluateInScope: async (exprs, properties) => { + const node = document.createElement('div') + node.style.display = 'none' + + for (const [key, evalVal] of Object.entries(properties)) { + const value = evalValueToString(evalVal) + console.log(key, evalVal, value) + value && node.style.setProperty(key, value) + } + $element.appendChild(node) + + const result = await evalExprInScope(exprs, getEvalActions(node, ctx)) + + // node.parentNode?.removeChild(node) + + return result + }, } return actions } +const evalExprInScope = async ( + exprs: Expr[], + actions: EvalActions, +): Promise => { + let lastVal = EvalValue.Void() + for (const expr of exprs) { + lastVal = await evalExpr(expr, actions) + } + return lastVal +} + export const handleEvents = async ( $element: HTMLElement, isNewElement: boolean = false, @@ -185,10 +222,10 @@ export const handleEvents = async ( if (handlerExpr) { const eventHandler = async (event: any) => { - const exprs = parse(handlerExpr) - for (const expr of exprs) { - await evalExpr(expr, getEvalActions($element, { event })) - } + await evalExprInScope( + parse(handlerExpr), + getEvalActions($element, { event }), + ) } matchString(eventType, { -- cgit v1.3.1