diff --git a/lib/chain.js b/lib/chain.js index 16813aa..9657d34 100644 --- a/lib/chain.js +++ b/lib/chain.js @@ -5,7 +5,8 @@ const {object} = require('@logdna/stdlib') const typeOf = require('./lang/type-of.js') const builtins = require('./actions/index.js') const {Parser, Lexer, Visitor} = require('./parser/index.js') -const {BANG, HASH} = require('./parser/tokens.js') +const {BANG, HASH, DASH} = require('./parser/tokens.js') +const ESCAPE_TOKENS = new Set([DASH.name]) const PARSE_TOKENS = new Set([BANG.name, HASH.name]) const lookup = Symbol('lookup') @@ -113,6 +114,10 @@ module.exports = class SetupChain { if (!lexed.tokens.length) return this.visitor.wrapLiteral(str) const firstToken = lexed.tokens[0] + if (ESCAPE_TOKENS.has(firstToken.tokenType.name)) { + return this.visitor.wrapLiteral(str.slice(1)) + } + if (!PARSE_TOKENS.has(firstToken.tokenType.name)) { return this.visitor.wrapLiteral(str) } @@ -155,7 +160,6 @@ module.exports = class SetupChain { const fn = this.actions[key] this.state[label || key] = await fn.apply(this, rest) } catch (err) { - console.error(err) const error = new Error(`Setup Chain error in ${key}`) error.stack = err.stack error.chain_params = task diff --git a/lib/parser/tokens.js b/lib/parser/tokens.js index cf6978e..be123c4 100644 --- a/lib/parser/tokens.js +++ b/lib/parser/tokens.js @@ -7,6 +7,11 @@ const BANG = createToken({ , pattern: /!/ }) +const DASH = createToken({ + name: 'dash' +, pattern: /-/ +}) + const COMMA = createToken({ name: 'comma' , pattern: ',' @@ -54,6 +59,7 @@ const WHITE_SPACE = createToken({ }) BANG.LABEL = '!' +DASH.LABEL = '-' HASH.LABEL = '#' COMMA.LABEL = ',' COLON.LABEL = ':' @@ -66,6 +72,7 @@ module.exports = { , DOT , HASH , BANG +, DASH , LPAREN , RPAREN , COMMA diff --git a/test/integration/chain.js b/test/integration/chain.js index 307b8cf..8cb1b66 100644 --- a/test/integration/chain.js +++ b/test/integration/chain.js @@ -207,6 +207,27 @@ test('Setup chain', async (t) => { } }) + t.test('escape control', async (t) => { + class EscapeChain extends Chain { + constructor(state) { + super(state) + } + + $test(arg) { + return arg + } + } + + const chain = new EscapeChain({f00: 'wrong'}) + chain.set('color', '-#f00') + chain.set('rand', '-!random') + chain.set('complex', '!test("-#foo")') + const state = await chain.execute() + t.equal(state.color, '#f00') + t.equal(state.rand, '!random') + t.equal(state.complex, '#foo') + }) + t.test('Default SetupChain (no actions, state); Only built-ins', async (t) => { const chain = new Chain(null, null) t.same(chain.state, {}, 'Empty state')