@maplibre/maplibre-gl-style-spec
Version:
a specification for maplibre styles
107 lines (90 loc) • 3.15 kB
text/typescript
import {
ObjectType,
ValueType,
StringType,
NumberType,
BooleanType,
checkSubtype,
typeToString,
array
} from '../types';
import {RuntimeError} from '../runtime_error';
import {typeOf} from '../values';
import type {Expression} from '../expression';
import type {ParsingContext} from '../parsing_context';
import type {EvaluationContext} from '../evaluation_context';
import type {Type} from '../types';
const types = {
string: StringType,
number: NumberType,
boolean: BooleanType,
object: ObjectType
};
export class Assertion implements Expression {
type: Type;
args: Array<Expression>;
constructor(type: Type, args: Array<Expression>) {
this.type = type;
this.args = args;
}
static parse(args: ReadonlyArray<unknown>, context: ParsingContext): Expression {
if (args.length < 2)
return context.error('Expected at least one argument.') as null;
let i = 1;
let type;
const name: string = (args[0] as any);
if (name === 'array') {
let itemType;
if (args.length > 2) {
const type = args[1];
if (typeof type !== 'string' || !(type in types) || type === 'object')
return context.error('The item type argument of "array" must be one of string, number, boolean', 1) as null;
itemType = types[type];
i++;
} else {
itemType = ValueType;
}
let N;
if (args.length > 3) {
if (args[2] !== null &&
(typeof args[2] !== 'number' ||
args[2] < 0 ||
args[2] !== Math.floor(args[2]))
) {
return context.error('The length argument to "array" must be a positive integer literal', 2) as null;
}
N = args[2];
i++;
}
type = array(itemType, N);
} else {
if (!types[name]) throw new Error(`Types doesn't contain name = ${name}`);
type = types[name];
}
const parsed = [];
for (; i < args.length; i++) {
const input = context.parse(args[i], i, ValueType);
if (!input) return null;
parsed.push(input);
}
return new Assertion(type, parsed);
}
evaluate(ctx: EvaluationContext) {
for (let i = 0; i < this.args.length; i++) {
const value = this.args[i].evaluate(ctx);
const error = checkSubtype(this.type, typeOf(value));
if (!error) {
return value;
} else if (i === this.args.length - 1) {
throw new RuntimeError(`Expected value to be of type ${typeToString(this.type)}, but found ${typeToString(typeOf(value))} instead.`);
}
}
throw new Error();
}
eachChild(fn: (_: Expression) => void) {
this.args.forEach(fn);
}
outputDefined(): boolean {
return this.args.every(arg => arg.outputDefined());
}
}