Skip to content

Commit

Permalink
Merge pull request #23 from tfso/bugfix/escaping-of-slash
Browse files Browse the repository at this point in the history
bugfix/Escaping of slash
  • Loading branch information
lostfields authored Mar 12, 2024
2 parents 38fad26 + 3ba6b93 commit 95a4e8b
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 25 deletions.
5 changes: 3 additions & 2 deletions src/linq/peg/grammar/odata.pegjs
Original file line number Diff line number Diff line change
Expand Up @@ -343,15 +343,16 @@ HexDigit
= [a-f] / [A-F] / [0-9]

StringLiteral
= "\'" chars:(Escape / !['\\\n\r] . )* "\'"
{ return { type: 'Literal', value: chars.map(l => l[0] == undefined ? l[1] : l[0] + l[1]).join('').replace(/\\(["'\\])|'(')/g, '$1$2') } }
= "'" chars:(Escape / !['\n\r] . )* "'"
{ return { type: 'Literal', value: chars.map(l => l[0] || l[1]).join('') } }

ArrayLiteral
= LPAR __ elements:(first:Expression rest:(COMMA __ Expression)* { return buildList(first, rest, 2)})? (COMMA __)? __ RPAR __
{ return { type: 'ArrayLiteral', elements: elements } }

Escape
= "''"
/ "\\"
/ "\\" ([btnfr"'\\] / OctalEscape / UnicodeEscape)

OctalEscape
Expand Down
51 changes: 30 additions & 21 deletions src/linq/peg/parser/odata-parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ function peg$parse(input, options) {
var peg$r11 = /^[+\-]/;
var peg$r12 = /^[pP]/;
var peg$r13 = /^[0-9A-Fa-f]/;
var peg$r14 = /^['\\\n\r]/;
var peg$r14 = /^['\n\r]/;
var peg$r15 = /^[btnfr"'\\]/;
var peg$r16 = /^[0-3]/;

Expand Down Expand Up @@ -266,7 +266,7 @@ function peg$parse(input, options) {
var peg$e28 = peg$classExpectation(["+", "-"], false, false);
var peg$e29 = peg$classExpectation(["p", "P"], false, false);
var peg$e30 = peg$classExpectation([["0", "9"], ["A", "F"], ["a", "f"]], false, false);
var peg$e31 = peg$classExpectation(["'", "\\", "\n", "\r"], false, false);
var peg$e31 = peg$classExpectation(["'", "\n", "\r"], false, false);
var peg$e32 = peg$anyExpectation();
var peg$e33 = peg$literalExpectation("''", false);
var peg$e34 = peg$literalExpectation("\\", false);
Expand Down Expand Up @@ -438,7 +438,7 @@ function peg$parse(input, options) {
var peg$f26 = function(value) { return { type: 'DateLiteral', value: flattenArray(value).join('') }; };
var peg$f27 = function(first, value, last) { return { type: 'DateLiteral', value: flattenArray(value).join('') }; };
var peg$f28 = function() { return { type: 'NumberLiteral', value: text() }; };
var peg$f29 = function(chars) { return { type: 'Literal', value: chars.map(l => l[0] == undefined ? l[1] : l[0] + l[1]).join('').replace(/\\(["'\\])|'(')/g, '$1$2') } };
var peg$f29 = function(chars) { return { type: 'Literal', value: chars.map(l => l[0] || l[1]).join('') } };
var peg$f30 = function(first, rest) { return buildList(first, rest, 2)};
var peg$f31 = function(elements) { return { type: 'ArrayLiteral', elements: elements } };
var peg$currPos = options.peg$currPos | 0;
Expand Down Expand Up @@ -4013,38 +4013,47 @@ function peg$parse(input, options) {
if (peg$silentFails === 0) { peg$fail(peg$e33); }
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
if (input.charCodeAt(peg$currPos) === 92) {
s1 = peg$c18;
s0 = peg$c18;
peg$currPos++;
} else {
s1 = peg$FAILED;
s0 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e34); }
}
if (s1 !== peg$FAILED) {
s2 = input.charAt(peg$currPos);
if (peg$r15.test(s2)) {
if (s0 === peg$FAILED) {
s0 = peg$currPos;
if (input.charCodeAt(peg$currPos) === 92) {
s1 = peg$c18;
peg$currPos++;
} else {
s2 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e35); }
s1 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e34); }
}
if (s2 === peg$FAILED) {
s2 = peg$parseOctalEscape();
if (s1 !== peg$FAILED) {
s2 = input.charAt(peg$currPos);
if (peg$r15.test(s2)) {
peg$currPos++;
} else {
s2 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e35); }
}
if (s2 === peg$FAILED) {
s2 = peg$parseUnicodeEscape();
s2 = peg$parseOctalEscape();
if (s2 === peg$FAILED) {
s2 = peg$parseUnicodeEscape();
}
}
if (s2 !== peg$FAILED) {
s1 = [s1, s2];
s0 = s1;
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
}
if (s2 !== peg$FAILED) {
s1 = [s1, s2];
s0 = s1;
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/linq/peg/translator/odatatranslator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export class ODataTranslator implements IExpressionVisitor<string> {
return `${expression.value}`

case 'string':
return `'${expression.value.replace(/'/g, '\'')}'`
return `'${expression.value.replace(/'/g, '\'\'')}'`

case 'boolean':
return expression.value ? 'true' : 'false'
Expand Down
9 changes: 8 additions & 1 deletion src/test/odatavisitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,6 @@ describe('When using OData for ExpressionVisitor', () => {
assert.equal((<Expr.LiteralExpression>expr).value, true)
})


it('should evaluate a expression with date as string', () => {
let reduced = reducer.parseOData('date ge datetime\'2017-05-01Z\''),
expr = reducer.evaluate(reduced, vars)
Expand Down Expand Up @@ -254,6 +253,14 @@ describe('When using OData for ExpressionVisitor', () => {
assert.ok((<Expr.ILiteralExpression>expr).value == false, 'Expected a literal of value true')
})

it('should handle escaping of special character \\', () => {
let reduced = reducer.parseOData(`contains(string, '\\\\')`),
expr = reducer.evaluate(reduced, vars)

assert.ok(expr.type == Expr.ExpressionType.Literal, 'Expected a literal')
assert.ok((<Expr.ILiteralExpression>expr).value == false, 'Expected a literal of value false')
})

describe('for IN operator', () => {
it('should evaluate in operator using constant with primary array where it is true', () => {
let reduced = reducer.parseOData('13 in array'),
Expand Down
28 changes: 28 additions & 0 deletions src/test/translator/odata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,33 @@ describe('When using Translator', () => {

chai.expect(odata).to.equal(`(contains(customer/name, 'kalle') eq true) or (contains(customer/no, '54'))`)
})

it('should handle escape of character \'', () => {
let expr = visitor.parseOData(`contains(customer/name, 'ka''e')`)
let odata = translator.visit(expr)

chai.expect(odata).to.equal(`contains(customer/name, 'ka''e')`)
})

it('should handle escape of character \\', () => {
let expr = visitor.parseOData(`contains(customer/name, 'ka\\e')`)
let odata = translator.visit(expr)

chai.expect(odata).to.equal(`contains(customer/name, 'ka\\e')`)
})

it('should handle escape of character \\ twice', () => {
let expr = visitor.parseOData(`contains(customer/name, 'ka\\\\e')`)
let odata = translator.visit(expr)

chai.expect(odata).to.equal(`contains(customer/name, 'ka\\\\e')`)
})

it('should handle unicode if that is a thing', () => {
let expr = visitor.parseOData(`contains(customer/name, 'ka\\u1128')`)
let odata = translator.visit(expr)

chai.expect(odata).to.equal(`contains(customer/name, 'ka\\u1128')`)
})
})
})

0 comments on commit 95a4e8b

Please sign in to comment.