expression-evaluation
Version:
Expression Evaluation
201 lines (200 loc) • 9.44 kB
JavaScript
import { FunctionDefinition } from '../FunctionDefinition.js';
import { typeUnknown, typeNumber, typeBuffer, typeString, typeArray, typeOptionalBoolean, typeOptionalNumber, typeOptionalBuffer, typeOptionalString, typeNumberOrString, typeJson } from '../Type.js';
export const funcToUniversalTime = new FunctionDefinition((value) => {
const v = parseDate(value);
return v ? [v.getUTCFullYear(), v.getUTCMonth() + 1, v.getUTCDate(),
v.getUTCHours(), v.getUTCMinutes(), v.getUTCSeconds(), v.getUTCMilliseconds()] : undefined;
}, typeArray, [typeNumberOrString]);
export const funcFromUniversalTime = new FunctionDefinition((value) => Date.UTC(value[0] ?? 1970, (value[1] ?? 1) - 1, value[2] ?? 1, value[3] ?? 0, value[4] ?? 0, value[5] ?? 0, value[6] ?? 0), typeNumber, [typeArray]);
export const funcToLocalTime = new FunctionDefinition((value) => {
const v = parseDate(value);
return v ? [v.getFullYear(), v.getMonth() + 1, v.getDate(),
v.getHours(), v.getMinutes(), v.getSeconds(), v.getMilliseconds()] : undefined;
}, typeArray, [typeNumberOrString]);
export const funcFromLocalTime = new FunctionDefinition((value) => new Date(value[0] ?? 1970, (value[1] ?? 1) - 1, value[2] ?? 1, value[3] ?? 0, value[4] ?? 0, value[5] ?? 0, value[6] ?? 0).getTime(), typeNumber, [typeArray]);
export const funcToUniversalTimeMonthIndex = new FunctionDefinition((value) => parseDate(value)?.getUTCMonth(), typeNumber, [typeNumberOrString]);
export const funcToLocalTimeMonthIndex = new FunctionDefinition((value) => parseDate(value)?.getMonth(), typeNumber, [typeNumberOrString]);
export const funcToUniversalTimeWeekdayIndex = new FunctionDefinition((value) => parseDate(value)?.getUTCDay(), typeNumber, [typeNumberOrString]);
export const funcToLocalTimeWeekdayIndex = new FunctionDefinition((value) => parseDate(value)?.getDay(), typeNumber, [typeNumberOrString]);
export const funcToTimeString = new FunctionDefinition((value) => parseDate(value)?.toISOString(), typeString, [typeNumberOrString]);
export const funcFromTimeString = new FunctionDefinition((value) => parseDate(value)?.getTime(), typeNumber, [typeString]);
export const funcToNumberBuffer = new FunctionDefinition((value, encoding) => {
if (value == null) {
return undefined;
}
let bits = '';
for (let i = 0; i < encoding.length; ++i) {
const c = encoding[i];
if (c >= '0' && c <= '9') {
bits += c;
}
}
const dv = new DataView(new Uint8Array(Number.parseInt(bits) / 8).buffer);
switch (encoding) {
case 'int8':
dv.setInt8(0, value);
break;
case 'int16':
dv.setInt16(0, value);
break;
case 'int16le':
dv.setInt16(0, value, true);
break;
case 'int32':
dv.setInt32(0, value);
break;
case 'int32le':
dv.setInt32(0, value, true);
break;
case 'uint8':
dv.setUint8(0, value);
break;
case 'uint16':
dv.setUint16(0, value);
break;
case 'uint16le':
dv.setUint16(0, value, true);
break;
case 'uint32':
dv.setUint32(0, value);
break;
case 'uint32le':
dv.setUint32(0, value, true);
break;
case 'float32':
dv.setFloat32(0, value);
break;
case 'float32le':
dv.setFloat32(0, value, true);
break;
case 'float64':
dv.setFloat64(0, value);
break;
case 'float64le':
dv.setFloat64(0, value, true);
break;
default: throw new Error(`${encoding} encoding not supported`);
}
return dv.buffer;
}, typeBuffer, [typeNumber, typeString]);
export const funcFromNumberBuffer = new FunctionDefinition((value, encoding, byteOffset) => {
if (value == null) {
return undefined;
}
const dv = new DataView(value, byteOffset);
switch (encoding) {
case 'int8': return dv.getInt8(0);
case 'int16': return dv.getInt16(0);
case 'int16le': return dv.getInt16(0, true);
case 'int32': return dv.getInt32(0);
case 'int32le': return dv.getInt32(0, true);
case 'uint8': return dv.getUint8(0);
case 'uint16': return dv.getUint16(0);
case 'uint16le': return dv.getUint16(0, true);
case 'uint32': return dv.getUint32(0);
case 'uint32le': return dv.getUint32(0, true);
case 'float32': return dv.getFloat32(0);
case 'float32le': return dv.getFloat32(0, true);
case 'float64': return dv.getFloat64(0);
case 'float64le': return dv.getFloat64(0, true);
default: throw new Error(`${encoding} encoding not supported`);
}
}, typeNumber, [typeBuffer, typeString, typeOptionalNumber], 2, 3);
export const funcToStringBuffer = new FunctionDefinition((value, encoding = 'utf8') => {
if (value == null) {
return undefined;
}
if (encoding === 'utf8') {
return new TextEncoder().encode(value).buffer;
}
else {
const dv = new DataView(new Uint16Array(value.length).buffer);
const lessOrEqual = encoding.endsWith('le');
for (let i = 0; i < value.length; ++i) {
dv.setUint16(i << 1, value.charCodeAt(i), lessOrEqual);
}
return dv.buffer;
}
}, typeBuffer, [typeString, typeOptionalString], 1, 2);
export const funcFromStringBuffer = new FunctionDefinition((value, encoding = 'utf8', byteOffset, byteLength) => {
if (value == null) {
return undefined;
}
if (encoding === 'utf8') {
return new TextDecoder().decode(new DataView(value, byteOffset, byteLength));
}
else {
const dv = new DataView(value, byteOffset, byteLength);
const lessOrEqual = encoding.endsWith('le');
let str = '';
for (let i = 0; i < dv.byteLength; i += 2) {
str += String.fromCharCode(dv.getUint16(i, lessOrEqual));
}
return str;
}
}, typeString, [typeBuffer, typeOptionalString, typeOptionalNumber, typeOptionalNumber], 1, 4);
export const funcToBooleanString = new FunctionDefinition((value) => value?.toString(), typeOptionalString, [typeOptionalBoolean]);
export const funcFromBooleanString = new FunctionDefinition((value) => value ? value.toLowerCase() === 'true' : undefined, typeOptionalBoolean, [typeOptionalString]);
export const funcToNumberString = new FunctionDefinition((value, radix) => value?.toString(radix), typeOptionalString, [typeOptionalNumber], 1, 2);
export const funcFromNumberString = new FunctionDefinition((value) => value ? Number.parseFloat(value) : undefined, typeOptionalNumber, [typeOptionalString]);
export const funcToBufferString = new FunctionDefinition((value) => stringifyBuffer(value), typeOptionalString, [typeOptionalBuffer]);
export const funcFromBufferString = new FunctionDefinition((value) => parseBuffer(value), typeOptionalBuffer, [typeOptionalString]);
export const funcToJsonString = new FunctionDefinition((value, whitespace) => value ? JSON.stringify(value, undefined, whitespace) : undefined, typeOptionalString, [typeJson, typeOptionalString], 1, 2);
export const funcFromJsonString = new FunctionDefinition((value) => value ? JSON.parse(value) : undefined, typeJson, [typeOptionalString]);
export const funcToText = new FunctionDefinition((value, whitespace) => textifyValue(value, whitespace), typeString, [typeUnknown], 1, 2);
export function parseDate(value) {
if (value == null) {
return undefined;
}
const date = new Date(value);
return isNaN(date.getTime()) ? undefined : date;
}
export function parseBuffer(value) {
if (value == null) {
return undefined;
}
const bytes = new Uint8Array(Math.ceil(value.length / 2));
for (let i = 0, c = 0; c < value.length; ++i) {
bytes[i] = Number.parseInt(value.slice(c, c += 2), 16);
}
return bytes.buffer;
}
export function stringifyBuffer(value) {
if (value == null) {
return undefined;
}
const bytes = new Uint8Array(value);
let str = '';
for (let i = 0; i < bytes.byteLength; ++i) {
str += bytes[i].toString(16).padStart(2, '0');
}
return str;
}
export function textifyValue(value, whitespace) {
const str = value == null
? 'null'
: typeof value === 'boolean'
? value.toString()
: typeof value === 'number'
? value.toString()
: value instanceof ArrayBuffer
? `#${stringifyBuffer(value)}#`
: typeof value === 'string'
? `"${value}"`
: undefined;
if (str != null) {
return str;
}
const prefix = whitespace ? '\n' + whitespace : '';
const suffix = whitespace ? '\n' : '';
if (Array.isArray(value)) {
const lines = value.map((i) => `${prefix}${textifyValue(i, whitespace).split('\n').join(prefix)}`);
return `[${lines.join(',')}${suffix}]`;
}
if (typeof value === 'object') {
const separator = whitespace ? ' ' : '';
const lines = Object.entries(value).map(([k, v]) => `${prefix}"${k}":${separator}${textifyValue(v, whitespace).split('\n').join(prefix)}`);
return `[${lines.join(',')}${suffix}]`;
}
return 'function';
}