From c7a137861494edd65d0c8de76ab09f422ab15481 Mon Sep 17 00:00:00 2001 From: Akshay Nair Date: Sun, 21 Jan 2024 16:12:43 +0530 Subject: feat(eval): adds evaluation for simple binop expressions --- src/eval.ts | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 57 insertions(+), 4 deletions(-) (limited to 'src/eval.ts') diff --git a/src/eval.ts b/src/eval.ts index 636023b..87bcd6d 100644 --- a/src/eval.ts +++ b/src/eval.ts @@ -1,4 +1,4 @@ -import { CSSUnit, Expr, parse } from './parser' +import { CSSUnit, Expr, parse, parseExpr } from './parser' import { Enum, constructors, match, matchString } from './utils/adt' export interface EvalActions { @@ -58,8 +58,10 @@ export const EvalValue = constructors() export const evalExprAsString = async ( expr: Expr, actions: EvalActions, -): Promise => - evalValueToString(await evalExpr(expr, actions)) +): Promise => { + const evalVal = await evalExpr(expr, actions) + return evalValueToString(evalVal) +} export const evalExpr = async ( expr: Expr, @@ -72,11 +74,15 @@ export const evalExpr = async ( EvalValue.Number( matchString(unit, { s: () => value * 1000, + rem: () => value * 16, // TODO: get root font size + em: () => value * 16, // TODO: get parent font size + '%': () => value * 100, // TODO: Get parent width _: () => value, }), ), Identifier: async s => EvalValue.String(s), VarIdentifier: async s => EvalValue.VarIdentifier(s), + Parens: ({ expr }) => evalExpr(expr, actions), _: async _ => EvalValue.Void(), }) } @@ -119,7 +125,9 @@ const getFunctions = ( ): Promise => { const getVariable = async () => { const varName = await evalExpr(args[0], actions) - const defaultValue = args[1] && (await evalExpr(args[1], actions)) + const defaultValue = args[1] + ? await evalExpr(args[1], actions) + : EvalValue.Void() return match, EvalValue>(varName, { VarIdentifier: async name => { @@ -358,10 +366,55 @@ const getFunctions = ( }) }, + calc: async () => { + const result = await evalCalcExpr(args[0], actions) + return EvalValue.Number(result) + }, + _: () => Promise.reject(new Error(`Not implemented: ${name}`)), }) } +const evalBinOp = async ( + left: Expr, + right: Expr, + actions: EvalActions, + op: (a: number, b: number) => number, +): Promise => + op(await evalCalcExpr(left, actions), await evalCalcExpr(right, actions)) + +export const evalCalcExpr = ( + expr: Expr, + actions: EvalActions, +): Promise => + match(expr, { + BinOp: async ({ op, left, right }) => + matchString(op, { + '+': () => evalBinOp(left, right, actions, (a, b) => a + b), + '*': () => evalBinOp(left, right, actions, (a, b) => a * b), + '-': () => evalBinOp(left, right, actions, (a, b) => a - b), + '/': () => evalBinOp(left, right, actions, (a, b) => a / b), + _: () => + Promise.reject( + new Error(`Invalid operator in calc expression: ${op}`), + ), + }), + Parens: ({ expr }) => evalCalcExpr(expr, actions), + _: async () => { + if (expr.tag === 'Call' && expr.value.name === 'var') { + const value = await evalExprAsString(expr, actions) + try { + const pvalue = await evalExpr(parseExpr(value ?? ''), actions) + return evalValueToNumber(pvalue) ?? 0 + } catch (e) { + return 0 + } + } + const value = await evalExpr(expr, actions) + return evalValueToNumber(value) ?? 0 + }, + }) + export const evalArgs = ( args: Array, count: number, -- cgit v1.3.1