Skip to content

Commit

Permalink
fix: pass errors as rejected promises
Browse files Browse the repository at this point in the history
  • Loading branch information
arlac77 committed Jan 4, 2017
1 parent 4af07e8 commit 603bdd1
Show file tree
Hide file tree
Showing 3 changed files with 174 additions and 177 deletions.
153 changes: 18 additions & 135 deletions src/expander.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,56 +5,20 @@
* @module config-expander
*/

const path = require('path');

import {
createContext
}
from 'expression-expander';

import {
create
}
from 'pratt-parser';

import {
createValue
}
from './util';

import {
functions
}
from './functions';

class AST {
get value() {
return undefined;
}
}

function Error(error) {
console.error(error);
return Promise.reject(error);
}

class BinOP extends AST {
constructor(a, b, exec) {
super();
Object.defineProperty(this, 'value', {
get: () => exec(a, b)
});
}
}

class FCall extends AST {
constructor(f, context, args) {
super();
Object.defineProperty(this, 'value', {
get: () => f.apply(context, args).value
});
}
grammar
}
from './grammar';

/**
* Expands expressions in a configuration object
Expand All @@ -63,105 +27,24 @@ class FCall extends AST {
* @returns {Promise} expanded configuration
*/
export function expand(config, options = {}) {
const constants = options.constants || {
basedir: '/'
};

const context = {
constants: constants
};

const grammar = create({
identifier(value, properties, context) {
if (context.length >= 2) {
const ctx = context[context.length - 2];

if (ctx.value[value] !== undefined) {
properties.value.value = ctx.value[value];
return;
}
}
if (context[0].value.constants) {
const v = context[0].value.constants[value];
if (v !== undefined) {
properties.value.value = v;
return;
}
}

const c = constants[value];
if (c) {
properties.value.value = c;
} else {
properties.type.value = 'identifier';
properties.value.value = value;
}
},
prefix: {
'(': {
precedence: 80,
led(grammar, left) {
if (left.type === 'identifier') {
const args = [];

if (grammar.token.value !== ')') {
while (true) {
args.push(grammar.expression(0));

if (grammar.token.value !== ',') {
break;
}
grammar.advance(',');
}
}

grammar.advance(')');

const f = functions[left.value];
if (f) {
return new FCall(f, context, args);
} else {
return Error(`unknown function ${left.value}`);
}
} else {
const e = grammar.expression(0);
grammar.advance(')');
return e;
}
}
}
},
infix: {
',': {},
')': {},
'+': {
precedence: 50,
combine: (left, right) => new BinOP(left, right, (l, r) => l.value + r.value)
},
'-': {
precedence: 50,
combine: (left, right) => new BinOP(left, right, (l, r) => l.value - r.value)
},
'*': {
precedence: 60,
combine: (left, right) => new BinOP(left, right, (l, r) => l.value * r.value)
},
'/': {
precedence: 60,
combine: (left, right) => new BinOP(left, right, (l, r) => l.value / r.value)
}
try {
const constants = options.constants || {
basedir: '/'
};

const context = {
constants
};

const ee = createContext({
evaluate: (expression, _context, path) => {
context.path = path;
const ast = grammar.parse(expression, context);
return ast.value;
}
});

const ctx = createContext({
evaluate: (expression, context, path) => {
const ast = grammar.parse(expression, path);
return ast.value;
}
});
});

try {
const r = ctx.expand(config);
const r = ee.expand(config);
return r instanceof Promise ? r : Promise.resolve(r);
} catch (e) {
return Promise.reject(e);
Expand Down
126 changes: 126 additions & 0 deletions src/grammar.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
/* jslint node: true, esnext: true */
'use strict';

import {
create
}
from 'pratt-parser';

import {
functions
}
from './functions';

class AST {
get value() {
return undefined;
}
}

function Error(error) {
return {
value: Promise.reject(error)
};
}

class BinOP extends AST {
constructor(a, b, exec) {
super();
Object.defineProperty(this, 'value', {
get: () => exec(a, b)
});
}
}

class FCall extends AST {
constructor(f, context, args) {
super();
Object.defineProperty(this, 'value', {
get: () => f.apply(context, args).value
});
}
}

export const grammar = create({
identifier(value, properties, context) {
const path = context.path;

if (path.length >= 2) {
const ctx = path[path.length - 2];

if (ctx.value[value] !== undefined) {
properties.value.value = ctx.value[value];
return;
}
}
if (path[0].value.constants) {
const v = path[0].value.constants[value];
if (v !== undefined) {
properties.value.value = v;
return;
}
}

const c = context.constants[value];
if (c) {
properties.value.value = c;
} else {
properties.type.value = 'identifier';
properties.value.value = value;
}
},
prefix: {
'(': {
precedence: 80,
led(grammar, left) {
if (left.type === 'identifier') {
const args = [];

if (grammar.token.value !== ')') {
while (true) {
args.push(grammar.expression(0));

if (grammar.token.value !== ',') {
break;
}
grammar.advance(',');
}
}

grammar.advance(')');

const f = functions[left.value];
if (f) {
return new FCall(f, grammar.context, args);
} else {
return Error(`unknown function ${left.value}`);
}
} else {
const e = grammar.expression(0);
grammar.advance(')');
return e;
}
}
}
},
infix: {
',': {},
')': {},
'+': {
precedence: 50,
combine: (left, right) => new BinOP(left, right, (l, r) => l.value + r.value)
},
'-': {
precedence: 50,
combine: (left, right) => new BinOP(left, right, (l, r) => l.value - r.value)
},
'*': {
precedence: 60,
combine: (left, right) => new BinOP(left, right, (l, r) => l.value * r.value)
},
'/': {
precedence: 60,
combine: (left, right) => new BinOP(left, right, (l, r) => l.value / r.value)
}
}
});
72 changes: 30 additions & 42 deletions tests/simple_test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* global describe, it */
/* global describe, it, xit */
/* jslint node: true, esnext: true */

'use strict';
Expand Down Expand Up @@ -44,6 +44,22 @@ describe('expander', () => {
},
name: 1
})));

it('double def', () => expand({
constants: {
A: 2
},
name: '${A}'
}, {
constants: {
A: 7
}
}).then(r => assert.deepEqual(r, {
constants: {
A: 2
},
name: 2
})));
});

describe('expression', () => {
Expand Down Expand Up @@ -85,47 +101,19 @@ describe('expander', () => {
});

describe('functions', () => {
xit('unknown function', () => expand({
name: "${thisFunctionIsUnknown('lower')}"
}).then(r => assert.deepEqual(r, {
name: 'LOWER'
})));

it('toUpperCase', () => expand({
name: "${toUpperCase('lower')}"
}).then(r => assert.deepEqual(r, {
name: 'LOWER'
})));

it('toLowerCase', () => expand({
name: "${toLowerCase('UPPER')}"
}).then(r => assert.deepEqual(r, {
name: 'upper'
})));

it('substring', () => expand({
name: "${substring('lower',1,3)}"
}).then(r => assert.deepEqual(r, {
name: 'ow'
})));

it('replace', () => expand({
name: "${replace('lower','ow','12')}"
}).then(r => assert.deepEqual(r, {
name: 'l12er'
})));

it('substring with expressions', () => expand({
name: "${substring('lower',1,1+2*1)}"
}).then(r => assert.deepEqual(r, {
name: 'ow'
})));

it('substring with expressions', () => expand({
name: "${substring('lower',1,number('2')+1)}"
}).then(r => assert.deepEqual(r, {
name: 'ow'
})));
it('unknown function', () => expand(
"${thisFunctionIsUnknown()}"
).then(e => assert.deepEqual(e, {}))
.catch(e => assert.deepEqual(e, "unknown function thisFunctionIsUnknown")));
it('toUpperCase', () => expand("${toUpperCase('lower')}").then(r => assert.equal(r, 'LOWER')));
it('toLowerCase', () => expand("${toLowerCase('UPPER')}").then(r => assert.equal(r, 'upper')));
it('substring', () => expand("${substring('lower',1,3)}").then(r => assert.equal(r, 'ow')));
it('replace', () => expand("${replace('lower','ow','12')}").then(r => assert.equal(r, 'l12er')));
it('substring with expressions', () => expand("${substring('lower',1,1+2*1)}").then(r => assert.equal(r,
'ow')));

it('substring with expressions', () => expand("${substring('lower',1,number('2')+1)}").then(r => assert.equal(
r, 'ow')));

it('encrypt/decrypt', () => expand("${decrypt('key',encrypt('key','secret'))}").then(r => assert.equal(
r, 'secret')));
Expand Down

0 comments on commit 603bdd1

Please sign in to comment.