diff --git a/src/value.ts b/src/value.ts index e51368f..8173ff5 100644 --- a/src/value.ts +++ b/src/value.ts @@ -1,22 +1,23 @@ -import { Scope } from "./scope" +import { Scope } from './scope' export type Value = - | { type: 'null', value: null } - | { type: 'boolean', value: boolean } - | { type: 'number', value: number } - | { type: 'string', value: string } - | { type: 'array', value: Value[] } - | { type: 'dict', value: Dict } + | { type: 'null'; value: null } + | { type: 'boolean'; value: boolean } + | { type: 'number'; value: number } + | { type: 'string'; value: string } + | { type: 'array'; value: Value[] } + | { type: 'dict'; value: Dict } + | { type: 'regex'; value: RegExp } | { - type: 'function', - params: string[], - defaults: Record, // indices into constants - body: number, - parentScope: Scope, - variadic: boolean, - named: boolean, - value: '' - } + type: 'function' + params: string[] + defaults: Record // indices into constants + body: number + parentScope: Scope + variadic: boolean + named: boolean + value: '' + } export type Dict = Map @@ -30,14 +31,13 @@ export type FunctionDef = { } export function toValue(v: any): Value /* throws */ { - if (v === null || v === undefined) - return { type: 'null', value: null } + if (v === null || v === undefined) return { type: 'null', value: null } - if (v && typeof v === 'object' && 'type' in v && 'value' in v) - return v as Value + if (v && typeof v === 'object' && 'type' in v && 'value' in v) return v as Value - if (Array.isArray(v)) - return { type: 'array', value: v.map(toValue) } + if (Array.isArray(v)) return { type: 'array', value: v.map(toValue) } + + if (v instanceof RegExp) return { type: 'regex', value: v } switch (typeof v) { case 'boolean': @@ -51,8 +51,7 @@ export function toValue(v: any): Value /* throws */ { case 'object': const dict: Dict = new Map() - for (const key of Object.keys(v)) - dict.set(key, toValue(v[key])) + for (const key of Object.keys(v)) dict.set(key, toValue(v[key])) return { type: 'dict', value: dict } default: @@ -62,13 +61,16 @@ export function toValue(v: any): Value /* throws */ { export function toNumber(v: Value): number { switch (v.type) { - case 'number': return v.value - case 'boolean': return v.value ? 1 : 0 + case 'number': + return v.value + case 'boolean': + return v.value ? 1 : 0 case 'string': { const parsed = parseFloat(v.value) return isNaN(parsed) ? 0 : parsed } - default: return 0 + default: + return 0 } } @@ -85,17 +87,26 @@ export function isTrue(v: Value): boolean { export function toString(v: Value): string { switch (v.type) { - case 'string': return v.value - case 'number': return String(v.value) - case 'boolean': return String(v.value) - case 'null': return 'null' - case 'function': return '' - case 'array': return `[${v.value.map(toString).join(', ')}]` + case 'string': + return v.value + case 'number': + return String(v.value) + case 'boolean': + return String(v.value) + case 'null': + return 'null' + case 'function': + return '' + case 'array': + return `[${v.value.map(toString).join(', ')}]` case 'dict': { - const pairs = Array.from(v.value.entries()) - .map(([k, v]) => `${k}: ${toString(v)}`) + const pairs = Array.from(v.value.entries()).map(([k, v]) => `${k}: ${toString(v)}`) return `{${pairs.join(', ')}}` } + case 'regex': + return String(v.value) + default: + return String(v) } } @@ -103,10 +114,14 @@ export function isEqual(a: Value, b: Value): boolean { if (a.type !== b.type) return false switch (a.type) { - case 'null': return true - case 'boolean': return a.value === (b as typeof a).value - case 'number': return a.value === (b as typeof a).value - case 'string': return a.value === (b as typeof a).value + case 'null': + return true + case 'boolean': + return a.value === b.value + case 'number': + return a.value === b.value + case 'string': + return a.value === b.value case 'array': { const bArr = b as typeof a if (a.value.length !== bArr.value.length) return false @@ -121,19 +136,35 @@ export function isEqual(a: Value, b: Value): boolean { } return true } - case 'function': return false // functions never equal + case 'regex': { + if (!(b instanceof RegExp)) return false + return String(a.value) === String(b.value) + } + case 'function': + return false // functions never equal + default: + return false } } export function fromValue(v: Value): any { switch (v.type) { - case 'null': return null - case 'boolean': return v.value - case 'number': return v.value - case 'string': return v.value - case 'array': return v.value.map(fromValue) - case 'dict': return Object.fromEntries(v.value.entries().map(([k, v]) => [k, fromValue(v)])) - case 'function': return '' + case 'null': + return null + case 'boolean': + return v.value + case 'number': + return v.value + case 'string': + return v.value + case 'array': + return v.value.map(fromValue) + case 'dict': + return Object.fromEntries(v.value.entries().map(([k, v]) => [k, fromValue(v)])) + case 'regex': + return v.value + case 'function': + return '' } } @@ -158,4 +189,4 @@ export function wrapNative(fn: Function): (...args: Value[]) => Promise { export function isWrapped(fn: Function): boolean { return !!(fn as any)[WRAPPED_MARKER] -} \ No newline at end of file +}