Files
languageservices/expressions/src/result.ts
T

230 lines
5.4 KiB
TypeScript
Raw Normal View History

import * as data from "./data";
export function falsy(d: data.ExpressionData): boolean {
switch (d.kind) {
case data.Kind.Null:
return true;
case data.Kind.Boolean:
return !d.value;
2023-03-16 18:08:51 -04:00
case data.Kind.Number: {
const dv = d.value;
return dv === 0 || isNaN(dv);
2023-03-16 18:08:51 -04:00
}
case data.Kind.String:
return d.value.length === 0;
}
return false;
}
export function truthy(d: data.ExpressionData): boolean {
return !falsy(d);
}
// Similar to the Javascript abstract equality comparison algorithm http://www.ecma-international.org/ecma-262/5.1/#sec-11.9.3.
// Except objects are not coerced to primitives.
export function coerceTypes(
li: data.ExpressionData,
ri: data.ExpressionData
): [data.ExpressionData, data.ExpressionData] {
let lv = li;
let rv = ri;
// Do nothing, same kind
if (li.kind === ri.kind) {
return [lv, rv];
}
switch (li.kind) {
// Number, String
case data.Kind.Number:
if (ri.kind === data.Kind.String) {
rv = new data.NumberData(ri.number());
return [lv, rv];
}
break;
// String, Number
case data.Kind.String:
if (ri.kind === data.Kind.Number) {
lv = new data.NumberData(li.number());
return [lv, rv];
}
break;
// Boolean|Null, Any
case data.Kind.Null:
case data.Kind.Boolean:
lv = new data.NumberData(li.number());
return coerceTypes(lv, rv);
}
// Any, Boolean|Null
switch (ri.kind) {
case data.Kind.Null:
case data.Kind.Boolean:
rv = new data.NumberData(ri.number());
return coerceTypes(lv, rv);
}
return [lv, rv];
}
// Similar to the Javascript abstract equality comparison algorithm http://www.ecma-international.org/ecma-262/5.1/#sec-11.9.3.
// Except string comparison is OrdinalIgnoreCase, and objects are not coerced to primitives.
2023-01-09 19:02:19 -05:00
export function equals(lhs: data.ExpressionData, rhs: data.ExpressionData): boolean {
2023-03-16 18:08:51 -04:00
const [lv, rv] = coerceTypes(lhs, rhs);
if (lv.kind != rv.kind) {
return false;
}
switch (lv.kind) {
// Null, Null
case data.Kind.Null:
return true;
// Number, Number
2023-03-16 18:08:51 -04:00
case data.Kind.Number: {
const ld = lv.value;
const rd = (rv as data.NumberData).value;
if (isNaN(ld) || isNaN(rd)) {
return false;
}
return ld == rd;
2023-03-16 18:08:51 -04:00
}
// String, String
2023-03-16 18:08:51 -04:00
case data.Kind.String: {
const ls = lv.value;
const rs = (rv as data.StringData).value;
return toUpperSpecial(ls) === toUpperSpecial(rs);
2023-03-16 18:08:51 -04:00
}
// Boolean, Boolean
2023-03-16 18:08:51 -04:00
case data.Kind.Boolean: {
const lb = lv.value;
const rb = (rv as data.BooleanData).value;
return lb == rb;
2023-03-16 18:08:51 -04:00
}
// Object, Object
case data.Kind.Dictionary:
case data.Kind.Array:
// Check reference equality
return lv === rv;
}
return false;
}
// Similar to the Javascript abstract equality comparison algorithm http://www.ecma-international.org/ecma-262/5.1/#sec-11.9.3.
// Except string comparison is OrdinalIgnoreCase, and objects are not coerced to primitives.
2023-01-09 19:02:19 -05:00
export function greaterThan(lhs: data.ExpressionData, rhs: data.ExpressionData): boolean {
const [lv, rv] = coerceTypes(lhs, rhs);
if (lv.kind != rv.kind) {
return false;
}
switch (lv.kind) {
// Number, Number
2023-03-16 18:08:51 -04:00
case data.Kind.Number: {
const lf = lv.value;
const rf = (rv as data.NumberData).value;
if (isNaN(lf) || isNaN(rf)) {
return false;
}
return lf > rf;
2023-03-16 18:08:51 -04:00
}
// String, String
2023-03-16 18:08:51 -04:00
case data.Kind.String: {
let ls = lv.value;
let rs = (rv as data.StringData).value;
ls = toUpperSpecial(ls);
rs = toUpperSpecial(rs);
return ls > rs;
2023-03-16 18:08:51 -04:00
}
// Boolean, Boolean
2023-03-16 18:08:51 -04:00
case data.Kind.Boolean: {
const lb = lv.value;
const rb = (rv as data.BooleanData).value;
return lb && !rb;
2023-03-16 18:08:51 -04:00
}
}
return false;
}
// Similar to the Javascript abstract equality comparison algorithm http://www.ecma-international.org/ecma-262/5.1/#sec-11.9.3.
// Except string comparison is OrdinalIgnoreCase, and objects are not coerced to primitives.
2023-01-09 19:02:19 -05:00
export function lessThan(lhs: data.ExpressionData, rhs: data.ExpressionData): boolean {
const [lv, rv] = coerceTypes(lhs, rhs);
if (lv.kind != rv.kind) {
return false;
}
switch (lv.kind) {
// Number, Number
2023-03-16 18:08:51 -04:00
case data.Kind.Number: {
const lf = lv.value;
const rf = (rv as data.NumberData).value;
if (isNaN(lf) || isNaN(rf)) {
return false;
}
return lf < rf;
2023-03-16 18:08:51 -04:00
}
// String, String
2023-03-16 18:08:51 -04:00
case data.Kind.String: {
let ls = lv.value;
let rs = (rv as data.StringData).value;
ls = toUpperSpecial(ls);
rs = toUpperSpecial(rs);
return ls < rs;
2023-03-16 18:08:51 -04:00
}
// Boolean, Boolean
2023-03-16 18:08:51 -04:00
case data.Kind.Boolean: {
const lb = lv.value;
const rb = (rv as data.BooleanData).value;
return !lb && rb;
2023-03-16 18:08:51 -04:00
}
}
return false;
}
// Do not toUpper the small-dotless-ı
export function toUpperSpecial(s: string): string {
const sb: string[] = [];
let i = 0;
2023-03-16 18:08:51 -04:00
const len = s.length;
let found = s.indexOf("ı");
while (i < len) {
if (i < found) {
sb.push(s.substring(i, found).toUpperCase()); // Append upper segment
i = found;
} else if (i == found) {
sb.push(s.substring(i, i + 1));
i += 1;
found = s.indexOf("ı", i);
} else {
sb.push(s.substring(i).toUpperCase()); // Append upper remaining
break;
}
}
return sb.join("");
}