рдлрд┐рд▓рд╣рд╛рд▓ рдореИрдВ рд░рдирдЯрд╛рдЗрдо рдкрд░ рдкрд╛рд░реНрд╕рд░ рдЬреЗрдирд░реЗрдЯ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЬреЗрдПрд╕ рдПрдкреАрдЖрдИ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░ рд░рд╣рд╛ рд╣реВрдВред рдпрд╣ рдареАрдХ рдХрд╛рдо рдХрд░рддрд╛ рд╣реИред
рдлрд┐рд░ рдореИрдВрдиреЗ рд░рдирдЯрд╛рдЗрдо рдХреЗ рджреМрд░рд╛рди рдЗрд╕реЗ рдЙрддреНрдкрдиреНрди рдХрд░рдиреЗ рд╕реЗ рдмрдЪрдиреЗ рдХреЗ рд▓рд┐рдП, рд╕реАрдПрд▓рдЖрдИ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдкрд╛рд░реНрд╕рд░ рдЙрддреНрдкрдиреНрди рдХрд░рдиреЗ рдХрд╛ рдкреНрд░рдпрд╛рд╕ рдХрд┐рдпрд╛ред рдЬрдм рдореИрдВ рдЗрд╕рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддрд╛ рд╣реВрдВ, рд╣рд╛рд▓рд╛рдВрдХрд┐ рдореБрдЭреЗ рддреНрд░реБрдЯрд┐рдпрд╛рдВ рдорд┐рд▓рддреА рд╣реИрдВ (~ рд╕реНрдЯреНрд░рд┐рдВрдЧ рдереНрд░реЛ рддреНрд░реБрдЯрд┐рдпреЛрдВ рдХреЛ рдкрд╛рд░реНрд╕ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдореЗрд░реЗ рдЖрдзреЗ рдкрд░реАрдХреНрд╖рдг)ред
grammar.pegjs
pegjs -o parser.js grammar.pegjs
peg.generate('...')
рдирд┐рдХрд╛рд▓реЗрдВ рдФрд░ рдЗрд╕реЗ рдирдП рдкрд╛рд░реНрд╕рд░ рд╕реЗ рдмрджрд▓реЗрдВconst parser = require('./parser');
parser.parse('...');
рдЕрдкреЗрдХреНрд╖рд┐рдд рд╡реНрдпрд╡рд╣рд╛рд░:
рдореИрдВ рдЙрдореНрдореАрдж рдХрд░рддрд╛ рд╣реВрдВ рдХрд┐ рд╕реАрдПрд▓рдЖрдИ рд╕реЗ рдЬреЗрдирд░реЗрдЯ рдХрд┐рдпрд╛ рдЧрдпрд╛ рдкрд╛рд░реНрд╕рд░ рдЬреЗрдПрд╕ рдПрдкреАрдЖрдИ рд╕реЗ рдЬреЗрдирд░реЗрдЯ рдХрд┐рдП рдЧрдП рдкрд╛рд░реНрд╕рд░ рдХреЗ рд╕рдорд╛рди рд╣реА рдХрд╛рдо рдХрд░рддрд╛ рд╣реИред
рд╡рд╛рд╕реНрддрд╡рд┐рдХ рд╡реНрдпрд╡рд╣рд╛рд░:
рдЬреЗрдПрд╕ рдПрдкреАрдЖрдИ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реБрдП, рдЬрдм рдореИрдВ рдЗрд╕ рд╕реНрдЯреНрд░рд┐рдВрдЧ ( 'foo = "bar"'
) рдХреЛ рдкрд╛рд░реНрд╕рд░ рдореЗрдВ рдкрд╛рд╕ рдХрд░рддрд╛ рд╣реВрдВ рддреЛ рдореБрдЭреЗ рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рдПрдПрд╕рдЯреА рдорд┐рд▓рддрд╛ рд╣реИ:
{
kind: 'condition',
target: 'foo',
operator: '=',
value: 'bar',
valueType: 'string',
attributeType: undefined
}
рд╣рд╛рд▓рд╛рдВрдХрд┐, рдЬрдм рдореИрдВ рд╕реАрдПрд▓рдЖрдИ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ "рдЬреЗрдирд░реЗрдЯреЗрдб" рдкрд╛рд░реНрд╕рд░ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддрд╛ рд╣реВрдВ, рдФрд░ рдЙрд╕реА рд╕реНрдЯреНрд░рд┐рдВрдЧ рдХреЛ рдкрд╛рд╕ рдХрд░рддрд╛ рд╣реВрдВ ( 'foo = "bar"'
) рдореБрдЭреЗ рдирд┐рдореНрди рддреНрд░реБрдЯрд┐ рдорд┐рд▓рддреА рд╣реИ:
SyntaxError: Expected "(", boolean, date, datetime, number, string, or time but "\"" found.
at peg$buildStructuredError (/Users/emmenko/xxx/parser.js:446:12)
at Object.peg$parse [as parse] (/Users/emmenko/xxx/parser.js:2865:11)
at repl:1:7
at ContextifyScript.Script.runInThisContext (vm.js:50:33)
at REPLServer.defaultEval (repl.js:240:29)
at bound (domain.js:301:14)
at REPLServer.runBound [as eval] (domain.js:314:12)
at REPLServer.onLine (repl.js:441:10)
at emitOne (events.js:121:20)
at REPLServer.emit (events.js:211:7)
0.10.0
8.9.1
[email protected]
рдмрдврд╝рд┐рдпрд╛, рдЖрдкрдиреЗ рд╕рд╣реА рднрд░рд╛ , рдЕрдм рд╣рдореЗрдВ рдХреЗрд╡рд▓ рд╡реНрдпрд╛рдХрд░рдг рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ рдФрд░ рдореИрдВ рдЖрдкрдХреА рд╕рд╣рд╛рдпрддрд╛ рдХрд░ рд╕рдХрддрд╛ рд╣реВрдБ
рд╣реЗрдпрд░ рдпреВ рдЧреЛ:
// GRAMMAR
const parser = peg.generate(`
{
function getFlattenedValue (value) {
if (!value) return undefined
return Array.isArray(value)
? value.map(function(v){return v.value})
: value.value
}
function getValueType (value) {
if (!value) return undefined
var rawType = value.type
if (Array.isArray(value))
rawType = value[0].type
switch (rawType) {
case 'string':
case 'number':
case 'boolean':
return rawType
default:
return 'string'
}
}
function getAttributeType (target, op, val) {
if (typeof target === 'string' && target.indexOf('attributes.') === 0) {
if (!val)
return undefined
switch (op) {
case 'in':
case 'not in':
return val[0].type;
case 'contains':
return 'set-' + val.type
default:
return Array.isArray(val) ? 'set-' + val[0].type : val.type;
}
}
}
function transformToCondition (target, op, val) {
return {
kind: "condition",
target: target,
operator: op,
value: getFlattenedValue(val),
valueType: getValueType(val),
attributeType: getAttributeType(target, op, val),
}
}
function createIdentifier (body) {
return body
.map(identifiers => identifiers.filter(identifier => (identifier && identifier !== '.'))) // gets raw_identifiers without dots and empty identifiers
.filter(identifiers => identifiers.length > 0) // filter out empty identifiers arrays
.map(identifiers => identifiers.join('.'))
.join('.') // join back to construct the path
}
}
// ----- DSL Grammar -----
predicate
= ws exp:expression ws { return exp; }
expression
= head:term tail:("or" term)*
{
if (tail.length === 0) {
return head;
}
return {
kind: "logical",
logical: "or",
conditions: [head].concat(tail.map(function(el){return el[1];})),
};
}
term
= head:factor tail:("and" factor)*
{
if (tail.length === 0) {
return head;
}
return {
kind: "logical",
logical: "and",
conditions: [head].concat(tail.map(function(el){return el[1];})),
};
}
factor
= ws negation:"not" ws primary:primary ws
{
return {
kind: "negation",
condition: primary,
};
}
/ ws primary:primary ws { return primary; }
primary
= basic_comparison
/ list_comparison
/ empty_comparison
/ parens
// ----- Comparators -----
basic_comparison
= target:val_expression ws op:single_operators ws val:value
{ return transformToCondition(target, op, val); }
list_comparison
= target:val_expression ws op:list_operators ws val:list_of_values
{ return transformToCondition(target, op, val); }
empty_comparison
= target:val_expression ws op:empty_operators
{ return transformToCondition(target, op); }
// ----- Operators -----
single_operators
= "!="
/ "="
/ "<>"
/ ">="
/ ">"
/ "<="
/ "<"
/ "contains"
list_operators
= "!="
/ "="
/ "<>"
/ "not in"
/ "in"
/ "contains all"
/ "contains any"
empty_operators
= "is not empty"
/ "is empty"
/ "is not defined"
/ "is defined"
list_of_values
= ws "(" ws head:value tail:(ws "," ws value)* ws ")" ws
{
if (tail.length === 0) {
return [head];
}
return [head].concat(tail.map(function(el){ return el[el.length -1];}));
}
// ----- Expressions -----
val_expression
= application_expression
/ constant_expression
/ field_expression
application_expression
= identifier ws "(" ws function_argument (ws "," ws function_argument)* ws ")"
constant_expression = ws val:value ws { return val; }
field_expression = ws i:identifier ws { return i; }
function_argument
= expression
/ constant_expression
/ field_expression
value
= v:boolean { return { type: 'boolean', value: v }; }
/ v:datetime { return { type: 'datetime', value: v }; }
/ v:date { return { type: 'date', value: v }; }
/ v:time { return { type: 'time', value: v }; }
/ v:number { return { type: 'number', value: v }; }
/ v:string { return { type: 'string', value: v }; }
// ----- Common rules -----
parens
= ws "(" ws ex:expression ws ")" ws { return ex; }
identifier
= body:((raw_identifier "." escaped_identifier)+ / (raw_identifier "." raw_identifier)+)
{
return createIdentifier(body)
}
/ i:raw_identifier { return i; }
escaped_identifier
= "\`" head:raw_identifier tail:("-" raw_identifier)* "\`"
{ return [head].concat(tail.map(function(el){return el.join('');})).join(''); }
raw_identifier = i:[a-zA-Z0-9_]* { return i.join(''); }
ws "whitespace" = [ \\t\\n\\r]*
// ----- Types: booleans -----
boolean "boolean"
= "false" { return false; }
/ "true" { return true; }
// ----- Types: datetime -----
datetime "datetime"
= quotation_mark datetime:datetime_format quotation_mark
{ return datetime.map(function(el){return Array.isArray(el) ? el.join('') : el;}).join(''); }
datetime_format = date_format time_mark time_format zulu_mark
time_mark = "T"
zulu_mark = "Z"
// ----- Types: date -----
date "date"
= quotation_mark date:date_format quotation_mark { return date.join("");}
date_format = [0-9][0-9][0-9][0-9] minus [0-9][0-9] minus [0-9][0-9]
// ----- Types: time -----
time "time"
= quotation_mark time:time_format quotation_mark { return time.join("");}
time_format = [0-2][0-9] colon [0-5][0-9] colon [0-5][0-9] decimal_point [0-9][0-9][0-9]
colon = ":"
// ----- Types: numbers -----
number "number"
= minus? int frac? exp? { return parseFloat(text()); }
decimal_point = "."
digit1_9 = [1-9]
e = [eE]
exp = e (minus / plus)? DIGIT+
frac = decimal_point DIGIT+
int = zero / (digit1_9 DIGIT*)
minus = "-"
plus = "+"
zero = "0"
// ----- Types: strings -----
string "string"
= quotation_mark chars:char* quotation_mark { return chars.join(""); }
char
= unescaped
/ escape
sequence:(
'"'
/ "\\\\"
/ "/"
/ "b" { return "\\b"; }
/ "f" { return "\\f"; }
/ "n" { return "\\n"; }
/ "r" { return "\\r"; }
/ "t" { return "\\t"; }
/ "u" digits:$(HEXDIG HEXDIG HEXDIG HEXDIG)
{ return String.fromCharCode(parseInt(digits, 16)); }
)
{ return sequence; }
escape = "\\\\"
quotation_mark = '"'
unescaped = [^\\0-\\x1F\\x22\\x5C]
// See RFC 4234, Appendix B (http://tools.ietf.org/html/rfc4234).
DIGIT = [0-9]
HEXDIG = [0-9a-f]i
рдмрдЧ рд╕реЗ рдереЛрдбрд╝рд╛ рд╕рдВрдмрдВрдзрд┐рдд рдЬреЛрдбрд╝ред рдореИрдВ pegjs-loader рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ pegjs
рд╕реЗрдЯрдЕрдк рдХрд░рддрд╛ рд╣реВрдВ ред рдпрд╣ рдЬреЗрдПрд╕ рдПрдкреАрдЖрдИ рдкрд░ parser.generate
рдХреЙрд▓рд┐рдВрдЧ рд╣реБрдб рдХреЗ рддрд╣рдд рд╕рдВрдЪрд╛рд▓рд┐рдд рд╣реЛрддрд╛ рд╣реИ, рдпрд╣ рднреА рдЙрд╕реА рддреНрд░реБрдЯрд┐ рдХреА рдУрд░ рдЬрд╛рддрд╛ рд╣реИред
рд╡реИрд╕реЗ рдкрд░рд┐рдпреЛрдЬрдирд╛ рдХреЗ рд▓рд┐рдП рдмрд╣реБрдд рдзрдиреНрдпрд╡рд╛рдж!
@emmenko рдореБрдЭреЗ рдирд╣реАрдВ рдкрддрд╛ рдХрд┐ рдЖрдкрдХрд╛ рд╡реНрдпрд╛рдХрд░рдг рдПрдкреАрдЖрдИ рдХреЗ рд╕рд╛рде рдХреНрдпреЛрдВ рдХрд╛рдо рдХрд░ рд░рд╣рд╛ рдерд╛ (рдХреНрдпреЛрдВ рдкрддрд╛ рд▓рдЧрд╛рдиреЗ рдХреА рдХреЛрд╢рд┐рд╢ рдХрд░рдирд╛ рдЬрд╛рд░реА рд░рдЦреЗрдЧрд╛), рд▓реЗрдХрд┐рди рдЖрдкрдХрд╛ рд╡реНрдпрд╛рдХрд░рдг рдЧрд▓рдд рдерд╛, unescaped
рдирд┐рдпрдо рд╣реЛрдирд╛ рдЪрд╛рд╣рд┐рдП:
unescaped = !'"' [^\\0-\\x1F\\x22\\x5C]
рдореБрдЭреЗ рдмрддрд╛рдПрдВ рдХрд┐ рдХреНрдпрд╛ рдпрд╣ рдЖрдкрдХреА рддрд░рдл рд╕реЗ рд╕рдорд╕реНрдпрд╛ рдХреЛ рдареАрдХ рдХрд░рддрд╛ рд╣реИ
@tdeekens рдпрджрд┐ рдпрд╣ рд╡рд╣реА рддреНрд░реБрдЯрд┐ рд╣реИ (рдЬреИрд╕реЗ Expected ... but "\"" found.
), рддреЛ рдЬрд╛рдВрдЪреЗрдВ рдХрд┐ рдХреНрдпрд╛ рдЖрдкрдХрд╛ рд╡реНрдпрд╛рдХрд░рдг рд╕рд╣реА рд╣реИ, рдпрд╛ рдЗрд╕реЗ рдпрд╣рд╛рдВ рдкреЛрд╕реНрдЯ рдХрд░реЗрдВ
@futagoza me рдФрд░ @tdeekens рдПрдХ рд╣реА рдЯреАрдо рдореЗрдВ рд╣реИрдВ, рдЗрд╕рд▓рд┐рдП рдпрд╣ рд╡рд╣реА рдореБрджреНрджрд╛ рд╣реИ
рд╣рдо рдЖрдкрдХреЛ рддреИрдирд╛рдд рд░рдЦреЗрдВрдЧреЗ! рдЕрдм рддрдХ рдЖрдкрдХреЗ рд╕рдорд░реНрдерди рдХреЗ рд▓рд┐рдП рдзрдиреНрдпрд╡рд╛рдж ЁЯЩП
рдореБрдЭреЗ рдирд╣реАрдВ рдкрддрд╛ рдХрд┐ рдЖрдкрдХрд╛ рд╡реНрдпрд╛рдХрд░рдг рдПрдкреАрдЖрдИ рдХреЗ рд╕рд╛рде рдХреНрдпреЛрдВ рдХрд╛рдо рдХрд░ рд░рд╣рд╛ рдерд╛
рдИрдорд╛рдирджрд╛рд░ рд╣реЛрдиреЗ рдХреЗ рд▓рд┐рдП рд╣рдореЗрдВ рдЗрд╕рд╕реЗ рдХрднреА рдХреЛрдИ рд╕рдорд╕реНрдпрд╛ рдирд╣реАрдВ рдереАред рд╡реИрд╕реЗ рднреА рдЗрд╕реЗ рдЗрдВрдЧрд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдзрдиреНрдпрд╡рд╛рдж!
рдХреНрдпрд╛ рдЕрдм рдпрд╣ рдХрд╛рдо рдХрд░ рд░рд╣рд╛ рд╣реИ?
рджреБрд░реНрднрд╛рдЧреНрдп рд╕реЗ рдЗрд╕рдиреЗ рдорджрдж рдирд╣реАрдВ рдХреА я╕П
рдЖрдкрдХреЗ рд╡реНрдпрд╛рдХрд░рдг, PEG.js 0.10, Node 8.9.0 рдФрд░ рдЗрдирдкреБрдЯ foo = "bar"
, рдореИрдВрдиреЗ рдЗрд╕реЗ 3 рдорд╛рд░реНрдЧреЛрдВ рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдЖрдЬрдорд╛рдпрд╛:
pegjs
рд╕реАрдПрд▓рдЖрдИрд╕рднреА 3 рдиреЗ рдПрдХ рд╣реА рддреНрд░реБрдЯрд┐ рджрд┐рдЦрд╛рдИ: Line 1, column 7: Expected "(", boolean, date, datetime, number, string, or time but "\"" found.
рдЕрдЧрд░ рдореИрдВ рдЖрдкрдХрд╛ рд╡реНрдпрд╛рдХрд░рдг рдмрджрд▓рддрд╛ рд╣реВрдВ, рддреЛ рдпрд╣ рд╕рднреА 3 рдорд╛рд░реНрдЧреЛрдВ рдХреЗ рд▓рд┐рдП рдЗрд╕ рддреНрд░реБрдЯрд┐ рдХреЛ рдареАрдХ рдХрд░рддрд╛ рд╣реИ:
// orignal
unescaped = [^\\0-\\x1F\\x22\\x5C]
// fixed
unescaped = !'"' [^\\0-\\x1F\\x22\\x5C]
рдирд┐рд╢реНрдЪрд┐рдд рдирд┐рдпрдо рд▓рд╛рдЧреВ рдХрд░рдиреЗ рдХреЗ рдмрд╛рдж, рдХреНрдпрд╛ рдЖрдк рдЬрд╛рдБрдЪ рд╕рдХрддреЗ рд╣реИрдВ рдХрд┐:
рд╕рд╛рде рд╣реА, рдЗрдирдкреБрдЯ рдореЗрдВ рдереЛрдбрд╝рд╛ рдмрджрд▓рд╛рд╡ рдХрд░рдиреЗ рдХреЗ рдмрд╛рдж, рдореИрдВрдиреЗ рдорд╣рд╕реВрд╕ рдХрд┐рдпрд╛ рдХрд┐ рдЖрдкрдХрд╛ рд╡реНрдпрд╛рдХрд░рдг рдиреНрдпреВрд▓рд╛рдЗрдиреНрд╕ рдХреЗ рд▓рд┐рдП рд╡реНрд╣рд╛рдЗрдЯрд╕реНрдкреЗрд╕ рдХреЗ рд░реВрдк рдореЗрдВ рд╕рд╣реА рдврдВрдЧ рд╕реЗ рдЦрд╛рддрд╛ рдирд╣реАрдВ рд╣реИ, рдпрд╣ рдЖрдкрдХреЗ ws
рдирд┐рдпрдо рдХреЗ рдХрд╛рд░рдг рд╕рдмрд╕реЗ рдЕрдзрд┐рдХ рд╕рдВрднрд╛рд╡рдирд╛ рд╣реИред
рд╕рдВрдкрд╛рджрд┐рдд рдХрд░реЗрдВ: рдпрд╣рд╛рдБ рдореЗрд░реА рдкрд░реАрдХреНрд╖рдг рд╕реНрдХреНрд░рд┐рдкреНрдЯ рд╣реИ:
/* eslint node/no-unsupported-features: 0 */
"use strict";
const { exec } = require( "child_process" );
const { readFileSync } = require( "fs" );
const { join } = require( "path" );
const { generate } = require( "pegjs" );
function test( parser ) {
try {
console.log( parser.parse( `foo = "bar"` ) );
} catch ( error ) {
if ( error.name !== "SyntaxError" ) throw error;
const loc = error.location.start;
console.log( `Line ${ loc.line }, column ${ loc.column }: ${ error.message }` );
}
}
const COMMAND = process.argv[ 2 ];
switch ( COMMAND ) {
case "api":
test( generate( readFileSync( join( __dirname, "grammar.pegjs" ), "utf8" ) ) );
break;
case "cli":
exec( "node node_modules/pegjs/bin/pegjs -o parser.js grammar.pegjs", error => {
if ( error ) console.error( error ), process.exit( 1 );
test( require( "./parser" ) );
} );
break;
default:
console.error( `Invalid command "${ COMMAND }" passed to test script.` );
process.exit( 1 );
}
рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рдХреЗ рд▓рд┐рдП рдмрд╣реБрдд рдзрдиреНрдпрд╡рд╛рдж! рд╣рдо рдХрд▓ рдЖрдкрдХреЗ рд╕реБрдЭрд╛рд╡ рдХреЗ рд╕рд╛рде рдкреНрд░рдпрд╛рд╕ рдХрд░реЗрдВрдЧреЗ рдФрд░ рдЕрдЧрд░ рдЗрд╕рд╕реЗ рдорджрдж рдорд┐рд▓реА рддреЛ рд╣рдо рдЖрдкрдХреЛ рдЬрд▓реНрдж рд╕реЗ рдЬрд▓реНрдж рдмрддрд╛рдПрдВрдЧреЗред ЁЯЩП
рдлреАрдбрдмреИрдХ рджреЗрдиреЗ рдХреЗ рд▓рд┐рдП рдзрдиреНрдпрд╡рд╛рджред рдкрд╣рд▓реЗ рднреНрд░рдо рдХреЗ рд▓рд┐рдП рдЦреЗрдж рд╣реИред рдореИрдВ рд╕рд┐рд░реНрдл рдпрд╣ рдмрддрд╛рдирд╛ рдЪрд╛рд╣рддрд╛ рдерд╛ рдХрд┐ рд╕рдорд╕реНрдпрд╛ рд╡реЗрдмрдкреИрдХ-рд▓реЛрдбрд░ рдореЗрдВ рднреА рдереАред рдЦреЗрдж рд╣реИ рдХрд┐ рдЗрд╕ рдореБрджреНрджреЗ рдкрд░ рднреНрд░рдо рдкреИрджрд╛ рд╣реБрдЖред
рд╣рдордиреЗ рд╕реБрдзрд╛рд░ рдХреА рдХреЛрд╢рд┐рд╢ рдХреАред рдпрд╣ рд╕рд╛рдорд╛рдиреНрдп рд░реВрдк рд╕реЗ рдкрд╛рд░реНрд╕рд░ рдХреЛ рдареАрдХ рдХрд░рддрд╛ рд╣реИ рд▓реЗрдХрд┐рди рд╣рдо рдПрдХ рдирдП рдореБрджреНрджреЗ рдореЗрдВ рднрд╛рдЧ рд▓реЗрддреЗ рд╣реИрдВ, рдЕрдм рд╣рдореЗрдВ рдЗрд╕рдХрд╛ рдХрд╛рд░рдг рд╕рдордЭрдиреЗ рдореЗрдВ рдореБрд╢реНрдХрд┐рд▓ рд╣реЛрддреА рд╣реИред
рдПрдХ рдЙрджрд╛рд╣рд░рдг рдПрдХ рдкрд░реАрдХреНрд╖рдг рдХрд╛ рд╣реИ (рдЕрдзрд┐рдХ рдиреАрдЪреЗ)
Object {
+ "attributeType": undefined,
"kind": "condition",
"operator": "=",
"target": "foo",
- "value": "bar",
+ "value": ",b,a,r",
"valueType": "string",
}
рд╣рдореЗрдВ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рддреНрд░реБрдЯрд┐ рд╣рдорд╛рд░реА рддрд░рдл рд╣реЛ рд╕рдХрддреА рд╣реИ, рд╣рдо рдЕрднреА рд╕реБрдирд┐рд╢реНрдЪрд┐рдд рдирд╣реАрдВ рд╣реИрдВ рдХрд┐ рдХрд╣рд╛рдВ рд╣реИред рдРрд╕рд╛ рд╣реЛрддрд╛ рд╣реИ рдЬреИрд╕реЗ рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рдЗрдирдкреБрдЯ рдХреЗ рд╕рд╛рде
categories.id != ("b33f8e3a-f8d1-476f-a595-2615c4b57556")
рдЬреЛ рдмрди рдЬрд╛рддрд╛ рд╣реИ
categories.id != (",b,3,3,f,8,e,3,a,-,f,8,d,1,-,4,7,6,f,-,a,5,9,5,-,2,6,1,5,c,4,b,5,7,5,5,6")
рдЬрдм рдкрд╛рд░реНрд╕ рдХрд┐рдпрд╛ рдЧрдпрд╛ред
рд╣рдо рд╕реНрдкрд╖реНрдЯ рд░реВрдк рд╕реЗ рдПрдХ рд╕реБрд░рд╛рдЧ рдХреЗ рд▓рд┐рдП рдмрд╣реБрдд рдЖрднрд╛рд░реА рд╣реЛрдВрдЧреЗ рд▓реЗрдХрд┐рди рдпрд╣ рднреА рд╕рдордЭреЗрдВрдЧреЗ рдХрд┐ рдХреНрдпрд╛ рд╡рд╣рд╛рдВ рд╣рдорд╛рд░рд╛ рд╕рдорд░реНрдерди рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред
рдУрд╣, рдореЗрд░реА рдЧрд▓рддреА ЁЯШи, рдЗрд╕реЗ рдареАрдХ рдХрд░рдирд╛ рдЪрд╛рд╣рд┐рдП
unescaped = !'"' value:[^\\0-\\x1F\\x22\\x5C] { return value; }
рд╕реБрдкрд░ рддреНрд╡рд░рд┐рдд рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рдХреЗ рд▓рд┐рдП рдзрдиреНрдпрд╡рд╛рджред рдпрд╣ рдорджрдж рдХрд░рддрд╛ рд╣реИ рд▓реЗрдХрд┐рди рд╕реАрдПрд▓рдЖрдИ рдпрд╛ рд╡реЗрдмрдкреИрдХ-рд▓реЛрдбрд░ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╕рдордп рдирд╣реАрдВ рдЬреЛ рдЕрдХреНрд╕рд░ SyntaxError: Expected "(", boolean, date, datetime, number, string, or time but "\"" found.
рдХреА рдкреНрд░рд╛рд░рдВрднрд┐рдХ рддреНрд░реБрдЯрд┐ рд▓реМрдЯрд╛рддрд╛ рд╣реИред рдХреБрдЫ рдРрд╕рд╛ рдЬреЛ рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП not(sku = "123")
рдпрд╛ рдЕрдзрд┐рдХ рдЬрдЯрд┐рд▓ рдЙрджрд╛рд╣рд░рдг lineItemTotal(sku = "SKU1" or list contains all (1,2,3), field.name, "third arg") = "10 EUR"
ред рд╣реЛ рд╕рдХрддрд╛ рд╣реИ рдХрд┐ рдЗрд╕рдХрд╛ рдЕрднреА рднреА рднрд╛рдЧрдиреЗ рд╕реЗ рдХреБрдЫ рд▓реЗрдирд╛-рджреЗрдирд╛ рд╣реЛ?
рд╣рд╛рдВ, рдкрддрд╛ рдЪрд▓рд╛ рдХрд┐ рдпрд╣ рдбрдмрд▓ рдПрд╕реНрдХреЗрдкрд┐рдВрдЧ рдХреЗ рдХрд╛рд░рдг рд╣реИред рдпрд╣рд╛рдБ рдирд┐рд╢реНрдЪрд┐рдд рдирд┐рдпрдо рд╣реИрдВ:
ws "whitespace" = [ \t\n\r]*
char
= unescaped
/ escape
sequence:(
'"'
/ "\\"
/ "/"
/ "b" { return "\b"; }
/ "f" { return "\f"; }
/ "n" { return "\n"; }
/ "r" { return "\r"; }
/ "t" { return "\t"; }
/ "u" digits:$(HEXDIG HEXDIG HEXDIG HEXDIG)
{ return String.fromCharCode(parseInt(digits, 16)); }
)
{ return sequence; }
escape = "\\"
unescaped = !'"' value:[^\0-\x1F\x22\x5C] { return value; }
рд╕рдВрдкрд╛рджрд┐рдд рдХрд░реЗрдВ: рдРрд╕рд╛ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рдЖрдк рдЬрдЯрд┐рд▓ рдЙрджрд╛рд╣рд░рдг рдХреЛ рдкрд╛рд░реНрд╕ рдХрд░рдиреЗ рд╡рд╛рд▓реЗ рдирд┐рдпрдореЛрдВ рдкрд░ рдХрд╛рдо рдХрд░рдирд╛ рдЪрд╛рд╣ рд╕рдХрддреЗ рд╣реИрдВ: lineItemTotal(sku = "SKU1" or list contains all (1,2,3), field.name, "third arg") = "10 EUR"
, рдпрд╣ рд╡рд░реНрддрдорд╛рди рдореЗрдВ рдПрдХ рдЕрдЬреАрдм "kind":"condition"
рдиреЛрдб рдХрд╛ рдЙрддреНрдкрд╛рджрди рдХрд░ рд░рд╣рд╛ рд╣реИ
рдорджрдж рдФрд░ рд╕рд▓рд╛рд╣ рдХреЗ рд▓рд┐рдП рдмрд╣реБрдд-рдмрд╣реБрдд рдзрдиреНрдпрд╡рд╛рджред рдРрд╕рд╛ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рд╣рдорд╛рд░реА рд╕рдорд╕реНрдпрд╛рдУрдВ рдХрд╛ рд╕рдорд╛рдзрд╛рди рд╣реЛ рдЧрдпрд╛ рд╣реИред рд╣рдо "рд╣рд╛рд▓рдд" рдиреЛрдб рдХреЗ рд╕рдВрдмрдВрдз рдореЗрдВ рд╕рд▓рд╛рд╣ рджреЗрдЦреЗрдВрдЧреЗред
рдЖрдкрдХрд╛ рд╕реНрд╡рд╛рдЧрдд рд╣реИ