diff --git a/lib/src/ast.dart b/lib/src/ast.dart index f80f94e..8660b8b 100644 --- a/lib/src/ast.dart +++ b/lib/src/ast.dart @@ -31,4 +31,13 @@ class Node { final children = []; bool get isNumber => RegExp(r'^-?\d+$').hasMatch(value); + + bool get isQuoted => value.startsWith("'"); + + String get unquoted => value + .substring(1, value.length - 1) + .replaceAll(r'\\', r'\') + .replaceAll(r"\'", r"'"); + + int get intValue => int.parse(value); } diff --git a/lib/src/state.dart b/lib/src/state.dart index d20fcc5..665fd9b 100644 --- a/lib/src/state.dart +++ b/lib/src/state.dart @@ -27,7 +27,7 @@ class Ready implements State { State process(Node node) { switch (node.value) { case '[': - return Ready(selector.then(_bracketSelector(node.children))); + return Ready(selector.then(brackets(node.children))); case '.': return AwaitingField(selector); case '..': @@ -39,42 +39,40 @@ class Ready implements State { } } - Selector _bracketSelector(List nodes) { - if (nodes.length == 1) { - final node = nodes.single; - if (node.value == '*') return AllInArray(); - if (node.isNumber) return Index(int.parse(nodes.first.value)); - if (node.value.startsWith("'")) { - return Field(node.value.substring(1, node.value.length - 1)); + Selector brackets(List nodes) { + if (nodes.isEmpty) throw FormatException('Empty brackets'); + if (nodes.length == 1) return singleValueBrackets(nodes.single); + return multiValueBrackets(nodes); + } + + Slice multiValueBrackets(List nodes) { + int first; + int last; + int step; + var colons = 0; + nodes.forEach((node) { + if (node.value == ':') { + colons++; + return; } - } else if (nodes.length > 1) { - int first; - int last; - int step; - var colons = 0; - nodes.map((_) => _.value).forEach((val) { -// if (nodes.length > 2 && nodes[1].value == ',') { -// // union. take the odd ones -// nodes.map((e) => e.value).where((element) => element != ',') -// } - if (val == ':') { - colons++; - return; - } - if (colons == 0) { - first = int.parse(val); - return; - } - if (colons == 1) { - last = int.parse(val); - return; - } - step = int.parse(val); - }); - return Slice(first: first, last: last, step: step); - } + if (colons == 0) { + first = node.intValue; + return; + } + if (colons == 1) { + last = node.intValue; + return; + } + step = node.intValue; + }); + return Slice(first: first, last: last, step: step); + } - throw StateError('Unexpected bracket expression'); + Selector singleValueBrackets(Node node) { + if (node.value == '*') return AllInArray(); + if (node.isNumber) return Index(node.intValue); + if (node.isQuoted) return Field(node.unquoted); + throw FormatException('Unexpected bracket expression'); } } diff --git a/lib/src/tokenize.dart b/lib/src/tokenize.dart index 215ea9d..f95c452 100644 --- a/lib/src/tokenize.dart +++ b/lib/src/tokenize.dart @@ -1,23 +1,19 @@ /// Parses a JSONPath expression into a list of tokens Iterable tokenize(String expr) sync* { var pos = 0; - var insideQuotedString = false; while (pos < expr.length) { var token = ''; + var insideQuotedString = false; + var escapeMode = false; while (pos < expr.length) { final char = expr[pos]; - if (char == "'") { + if (char == "'" && !escapeMode) { insideQuotedString = !insideQuotedString; } + escapeMode = char == r'\'; if (insideQuotedString) { - if (char == r'\') { - if (expr.length <= pos + 1) throw FormatException('Dangling escape'); - token += expr[pos + 1]; - pos += 2; - } else { - token += char; - pos += 1; - } + token += char; + pos += 1; continue; } if (_singles.contains(char)) { diff --git a/test/json_path_test.dart b/test/json_path_test.dart index 4621faa..4797f30 100644 --- a/test/json_path_test.dart +++ b/test/json_path_test.dart @@ -166,6 +166,7 @@ void main() { test('Escape single quote', () { final j = {r"sq'sq s\s qs\'qs": 'value'}; final path = JsonPath(r"$['sq\'sq s\\s qs\\\'qs']"); + expect(path.toString(), r"$['sq\'sq s\\s qs\\\'qs']"); final select = path.select(j); expect(select.single.value, 'value'); expect(select.single.path, r"$['sq\'sq s\\s qs\\\'qs']");