Skip to content

Commit

Permalink
Support implicit argument coercions for compound expressions with mul…
Browse files Browse the repository at this point in the history
…tiple overloads.

Support string->formatted coercion (currently unused).
  • Loading branch information
ChrisLoer committed Jul 27, 2018
1 parent 07583cc commit be75fe9
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 20 deletions.
53 changes: 33 additions & 20 deletions src/style-spec/expression/compound_expression.js
Expand Up @@ -67,29 +67,36 @@ class CompoundExpression implements Expression {
signature.length === args.length - 1 // correct param count
));

// First parse all the args
const parsedArgs: Array<Expression> = [];
for (let i = 1; i < args.length; i++) {
const arg = args[i];
let expected;
if (overloads.length === 1) {
const params = overloads[0][0];
expected = Array.isArray(params) ?
params[i - 1] :
params.type;
}
const parsed = context.parse(arg, 1 + parsedArgs.length, expected);
if (!parsed) return null;
parsedArgs.push(parsed);
}

let signatureContext: ParsingContext = (null: any);

for (const [params, evaluate] of overloads) {
// Use a fresh context for each attempted signature so that, if
// we eventually succeed, we haven't polluted `context.errors`.
signatureContext = new ParsingContext(context.registry, context.path, null, context.scope);

// First parse all the args, potentially coercing to the
// types expected by this overload.
const parsedArgs: Array<Expression> = [];
let argParseFailed = false;
for (let i = 1; i < args.length; i++) {
const arg = args[i];
const expectedType = Array.isArray(params) ?
params[i - 1] :
params.type;

const parsed = signatureContext.parse(arg, 1 + parsedArgs.length, expectedType);
if (!parsed) {
argParseFailed = true;
break;
}
parsedArgs.push(parsed);
}
if (argParseFailed) {
// Couldn't coerce args of this overload to expected type, move
// on to next one.
continue;
}

if (Array.isArray(params)) {
if (params.length !== parsedArgs.length) {
signatureContext.error(`Expected ${params.length} arguments, but found ${parsedArgs.length} instead.`);
Expand Down Expand Up @@ -117,10 +124,16 @@ class CompoundExpression implements Expression {
const signatures = expected
.map(([params]) => stringifySignature(params))
.join(' | ');
const actualTypes = parsedArgs
.map(arg => toString(arg.type))
.join(', ');
context.error(`Expected arguments of type ${signatures}, but found (${actualTypes}) instead.`);

const actualTypes = [];
// For error message, re-parse arguments without trying to
// apply any coercions
for (let i = 1; i < args.length; i++) {
const parsed = context.parse(args[i], 1 + actualTypes.length);
if (!parsed) return null;
actualTypes.push(toString(parsed.type));
}
context.error(`Expected arguments of type ${signatures}, but found (${actualTypes.join(', ')}) instead.`);
}

return null;
Expand Down
10 changes: 10 additions & 0 deletions src/style-spec/expression/definitions/coercion.js
Expand Up @@ -10,6 +10,7 @@ import type { Expression } from '../expression';
import type ParsingContext from '../parsing_context';
import type EvaluationContext from '../evaluation_context';
import type { Type } from '../types';
import { Formatted, FormattedSection } from './formatted';

const types = {
'to-number': NumberType,
Expand Down Expand Up @@ -73,6 +74,15 @@ class Coercion implements Expression {
}
}
throw new RuntimeError(error || `Could not parse color from value '${typeof input === 'string' ? input : JSON.stringify(input)}'`);
} else if (this.type.kind === 'formatted') {
let input;
for (const arg of this.args) {
input = arg.evaluate(ctx);
if (typeof input === 'string') {
return new Formatted([new FormattedSection(input, null, null)]);
}
}
throw new RuntimeError(`Could not parse formatted text from value '${typeof input === 'string' ? input : JSON.stringify(input)}'`);
} else {
let value = null;
for (const arg of this.args) {
Expand Down
4 changes: 4 additions & 0 deletions src/style-spec/expression/parsing_context.js
Expand Up @@ -113,6 +113,10 @@ class ParsingContext {
if (!options.omitTypeAnnotations) {
parsed = new Coercion(expected, [parsed]);
}
} else if (expected.kind === 'formatted' && (actual.kind === 'value' || actual.kind === 'string')) {
if (!options.omitTypeAnnotations) {
parsed = new Coercion(expected, [parsed]);
}
} else if (this.checkSubtype(this.expectedType, parsed.type)) {
return null;
}
Expand Down

0 comments on commit be75fe9

Please sign in to comment.