Skip to content

Commit

Permalink
Refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
f3ath committed Aug 3, 2020
1 parent 2c6a7c7 commit e8b8f83
Show file tree
Hide file tree
Showing 6 changed files with 53 additions and 48 deletions.
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@
- [x] Square-bracket field notation (`['foo']`, `$['"some" \'special\' [chars]']`)
- [x] Slice (`articles[1:10:2]`)
- [x] Union (`book[0, 1]`, `book[author, title, price]`)
- [ ] Basic filtering (`book[?(@.price)]`)
- [ ] Expressions (`book[?(@.price * @.discount < 10)]`)
- [ ] Filtering


[JSONPath]: https://goessner.net/articles/JsonPath/
18 changes: 8 additions & 10 deletions lib/src/ast.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,31 @@ import 'package:json_path/src/node.dart';
/// The Abstract Syntax Tree
class AST {
AST(Iterable<String> tokens) {
tokens.skipWhile((token) => token == _root).forEach(_processToken);
tokens.skipWhile((_) => _ == r'$').forEach(_processToken);
}

/// The children of the root node
Iterable<Node> get children => _stack.last.children;

static const _root = r'$';

final _stack = <Node>[Node(_root)];
final _stack = <Node>[Node('')];

void _processToken(String token) {
if (token == '[') {
_startBrackets();
_start('[]');
} else if (token == ']') {
_finishBrackets();
_finish('[]');
} else {
_stack.last.children.add(Node(token));
}
}

void _startBrackets() {
_stack.add(Node('[]'));
void _start(String root) {
_stack.add(Node(root));
}

void _finishBrackets() {
void _finish(String root) {
final children = <Node>[];
while (_stack.last.value != '[]') {
while (_stack.last.value != root) {
children.add(_stack.removeLast());
}
final bracketExp = _stack.removeLast();
Expand Down
6 changes: 3 additions & 3 deletions lib/src/parser.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class Parser {
/// Builds a selector from the JsonPath [expression]
Selector parse(String expression) {
if (expression.isEmpty) throw FormatException('Empty expression');
ParserState state = Ready(Root());
ParserState state = Ready(RootSelector());
AST(_tokenize(expression)).children.forEach((node) {
state = state.process(node);
});
Expand All @@ -21,8 +21,8 @@ class Parser {

static final _tokens = RegExp([
r'\$', // root
r'\[', // open bracket
r'\]', // closing bracket
r'\[', // left bracket
r'\]', // right bracket
r'\.\.', // recursion
r'\.', // child
r'\*', // wildcard
Expand Down
66 changes: 37 additions & 29 deletions lib/src/parser_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class Ready implements ParserState {
ParserState process(Node node) {
switch (node.value) {
case '[]':
return Ready(selector.then(brackets(node.children)));
return Ready(selector.then(bracketExpression(node.children)));
case '.':
return AwaitingField(selector);
case '..':
Expand All @@ -41,35 +41,27 @@ class Ready implements ParserState {
}
}

Selector brackets(List<Node> nodes) {
Selector bracketExpression(List<Node> nodes) {
if (nodes.isEmpty) throw FormatException('Empty brackets');
if (nodes.length == 1) return singleValueBrackets(nodes.single);
return multiValueBrackets(nodes);
}

Selector singleValueBrackets(Node node) {
if (node.isWildcard) return ListWildcard();
if (node.isNumber) return Index(node.intValue);
if (node.isQuoted) return Field(node.unquoted);
throw FormatException('Unexpected bracket expression');
}

Selector multiValueBrackets(List<Node> nodes) {
if (nodes.any((node) => node.value == ':')) {
int first;
int last;
int step;
var colons = 0;
nodes.forEach((node) {
if (node.value == ':') {
colons++;
return;
}
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);
}
if (_isSlice(nodes)) return _slice(nodes);
return _union(nodes);
}

bool _isSlice(List<Node> nodes) => nodes.any((node) => node.value == ':');

Selector _union(List<Node> nodes) {
final filtered = nodes.where((_) => _.value != ',');
if (nodes.first.isNumber) {
return ListUnion(filtered.map((_) => _.intValue).toList());
Expand All @@ -78,11 +70,27 @@ class Ready implements ParserState {
filtered.map((_) => _.isQuoted ? _.unquoted : _.value).toList());
}

Selector singleValueBrackets(Node node) {
if (node.isWildcard) return ListWildcard();
if (node.isNumber) return Index(node.intValue);
if (node.isQuoted) return Field(node.unquoted);
throw FormatException('Unexpected bracket expression');
Slice _slice(List<Node> nodes) {
int first;
int last;
int step;
var colons = 0;
nodes.forEach((node) {
if (node.value == ':') {
colons++;
return;
}
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);
}
}

Expand Down
4 changes: 2 additions & 2 deletions lib/src/selector/root.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import 'package:json_path/src/result.dart';
import 'package:json_path/src/selector/selector.dart';
import 'package:json_path/src/selector/selector_mixin.dart';

class Root with SelectorMixin {
const Root();
class RootSelector with SelectorMixin {
const RootSelector();

@override
Iterable<Result> filter(Iterable<Result> results) =>
Expand Down
4 changes: 2 additions & 2 deletions test/json_path_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -247,15 +247,15 @@ void main() {
});
});

group('Arrays', () {
group('Lists', () {
test('Path with an index', () {
final path = JsonPath(r'$.store.book[0].title');
expect(path.toString(), r"$['store']['book'][0]['title']");
expect(path.filter(json).single.value, 'Sayings of the Century');
expect(path.filter(json).single.path, r"$['store']['book'][0]['title']");
});

test('All in array', () {
test('All in list', () {
final path = JsonPath(r'$.store.book[*]');
expect(path.toString(), r"$['store']['book'][*]");
expect(path.filter(json).length, 4);
Expand Down

0 comments on commit e8b8f83

Please sign in to comment.