From 00eb9e97b6313fa3a94428fa37a091689d5421b0 Mon Sep 17 00:00:00 2001 From: Tony Garnock-Jones Date: Mon, 6 Jun 2022 15:28:11 +0200 Subject: [PATCH] Implement count function --- implementations/python/preserves/path.prb | 4 +- implementations/python/preserves/path.py | 18 +++++-- implementations/python/tests/test_path.py | 30 +++++++++++ implementations/rust/preserves-path/path.bin | 4 +- .../rust/preserves-path/src/parse.rs | 16 ++++++ .../rust/preserves-path/src/step.rs | 53 +++++++++++++++++-- path/path.bin | 4 +- path/path.prs | 6 ++- 8 files changed, 121 insertions(+), 14 deletions(-) create mode 100644 implementations/python/tests/test_path.py diff --git a/implementations/python/preserves/path.prb b/implementations/python/preserves/path.prb index ca12e84..e5241a0 100644 --- a/implementations/python/preserves/path.prb +++ b/implementations/python/preserves/path.prb @@ -1,5 +1,5 @@ -´³schema·³version‘³ definitions·³Axis´³orµµ±values´³rec´³lit³values„´³tupleµ„„„„µ± descendants´³rec´³lit³ descendants„´³tupleµ„„„„µ±at´³rec´³lit³at„´³tupleµ´³named³key³any„„„„„µ±label´³rec´³lit³label„´³tupleµ„„„„µ±keys´³rec´³lit³keys„´³tupleµ„„„„µ±length´³rec´³lit³length„´³tupleµ„„„„µ± annotations´³rec´³lit³ annotations„´³tupleµ„„„„µ±embedded´³rec´³lit³embedded„´³tupleµ„„„„µ±parse´³rec´³lit³parse„´³tupleµ´³named³module´³seqof´³atom³Symbol„„„´³named³name´³atom³Symbol„„„„„„µ±unparse´³rec´³lit³unparse„´³tupleµ´³named³module´³seqof´³atom³Symbol„„„´³named³name´³atom³Symbol„„„„„„„„³Step´³orµµ±Axis´³refµ„³Axis„„µ±Filter´³refµ„³Filter„„„„³Filter´³orµµ±nop´³rec´³lit³nop„´³tupleµ„„„„µ±compare´³rec´³lit³compare„´³tupleµ´³named³op´³refµ„³ -Comparison„„´³named³literal³any„„„„„µ±regex´³rec´³lit³regex„´³tupleµ´³named³regex´³atom³String„„„„„„µ±test´³rec´³lit³test„´³tupleµ´³named³pred´³refµ„³ Predicate„„„„„„µ±real´³rec´³lit³real„´³tupleµ„„„„µ±int´³rec´³lit³int„´³tupleµ„„„„µ±kind´³rec´³lit³kind„´³tupleµ´³named³kind´³refµ„³ ValueKind„„„„„„„„³Selector´³seqof´³refµ„³Step„„³ Predicate´³orµµ±Selector´³refµ„³Selector„„µ±not´³rec´³lit³not„´³tupleµ´³named³pred´³refµ„³ Predicate„„„„„„µ±or´³rec´³lit³or„´³tupleµ´³named³preds´³seqof´³refµ„³ Predicate„„„„„„„µ±and´³rec´³lit³and„´³tupleµ´³named³preds´³seqof´³refµ„³ Predicate„„„„„„„„„³ ValueKind´³orµµ±Boolean´³lit³Boolean„„µ±Float´³lit³Float„„µ±Double´³lit³Double„„µ± SignedInteger´³lit³ SignedInteger„„µ±String´³lit³String„„µ± +´³schema·³version‘³ definitions·³Axis´³orµµ±values´³rec´³lit³values„´³tupleµ„„„„µ± descendants´³rec´³lit³ descendants„´³tupleµ„„„„µ±at´³rec´³lit³at„´³tupleµ´³named³key³any„„„„„µ±label´³rec´³lit³label„´³tupleµ„„„„µ±keys´³rec´³lit³keys„´³tupleµ„„„„µ±length´³rec´³lit³length„´³tupleµ„„„„µ± annotations´³rec´³lit³ annotations„´³tupleµ„„„„µ±embedded´³rec´³lit³embedded„´³tupleµ„„„„µ±parse´³rec´³lit³parse„´³tupleµ´³named³module´³seqof´³atom³Symbol„„„´³named³name´³atom³Symbol„„„„„„µ±unparse´³rec´³lit³unparse„´³tupleµ´³named³module´³seqof´³atom³Symbol„„„´³named³name´³atom³Symbol„„„„„„„„³Step´³orµµ±Axis´³refµ„³Axis„„µ±Filter´³refµ„³Filter„„µ±Function´³refµ„³Function„„„„³Filter´³orµµ±nop´³rec´³lit³nop„´³tupleµ„„„„µ±compare´³rec´³lit³compare„´³tupleµ´³named³op´³refµ„³ +Comparison„„´³named³literal³any„„„„„µ±regex´³rec´³lit³regex„´³tupleµ´³named³regex´³atom³String„„„„„„µ±test´³rec´³lit³test„´³tupleµ´³named³pred´³refµ„³ Predicate„„„„„„µ±real´³rec´³lit³real„´³tupleµ„„„„µ±int´³rec´³lit³int„´³tupleµ„„„„µ±kind´³rec´³lit³kind„´³tupleµ´³named³kind´³refµ„³ ValueKind„„„„„„„„³Function´³rec´³lit³count„´³tupleµ´³named³selector´³refµ„³Selector„„„„„³Selector´³seqof´³refµ„³Step„„³ Predicate´³orµµ±Selector´³refµ„³Selector„„µ±not´³rec´³lit³not„´³tupleµ´³named³pred´³refµ„³ Predicate„„„„„„µ±or´³rec´³lit³or„´³tupleµ´³named³preds´³seqof´³refµ„³ Predicate„„„„„„„µ±and´³rec´³lit³and„´³tupleµ´³named³preds´³seqof´³refµ„³ Predicate„„„„„„„„„³ ValueKind´³orµµ±Boolean´³lit³Boolean„„µ±Float´³lit³Float„„µ±Double´³lit³Double„„µ± SignedInteger´³lit³ SignedInteger„„µ±String´³lit³String„„µ± ByteString´³lit³ ByteString„„µ±Symbol´³lit³Symbol„„µ±Record´³lit³Record„„µ±Sequence´³lit³Sequence„„µ±Set´³lit³Set„„µ± Dictionary´³lit³ diff --git a/implementations/python/preserves/path.py b/implementations/python/preserves/path.py index 75a884e..bf05a6c 100644 --- a/implementations/python/preserves/path.py +++ b/implementations/python/preserves/path.py @@ -57,12 +57,17 @@ FILTER_SET = Symbol('set') FILTER_DICT = Symbol('dict') FILTER_EMBEDDED = Symbol('embedded') +FUNCTION_COUNT = Symbol('count') + TRANSFORM_REAL = Symbol('~real') TRANSFORM_INT = Symbol('~int') def parse_step(tokens): t = next(tokens) - if isinstance(t, tuple): return syntax.Step.Filter(syntax.Filter.test(parse_predicate(iter(t)))) + if isinstance(t, tuple): return syntax.Step.Filter(syntax.Filter.test(parse_predicate(t))) + if isinstance(t, Record): + if t.key == FUNCTION_COUNT: return syntax.Step.Function(syntax.Function(parse_selector(t.fields))) + raise ValueError('Invalid Preserves path function: ' + repr(t)) if t == AXIS_VALUES: return syntax.Step.Axis(syntax.Axis.values()) if t == AXIS_DESCENDANTS: return syntax.Step.Axis(syntax.Axis.descendants()) if t == AXIS_MEMBER: return syntax.Step.Axis(syntax.Axis.at(next(tokens))) @@ -88,11 +93,11 @@ def parse_step(tokens): return syntax.Step.Filter(syntax.Filter.regex(re_val)) if t == FILTER_LABEL: label_lit = next(tokens) - return syntax.Step.Test(syntax.Predicate.Selector(syntax.Selector([ + return syntax.Step.Filter(syntax.Filter.test(syntax.Predicate.Selector(syntax.Selector([ syntax.Step.Axis(syntax.Axis.label()), syntax.Step.Filter(syntax.Filter.compare( syntax.Comparison.eq(), - label_lit))]))) + label_lit))])))) if t == TRANSFORM_REAL: return syntax.Step.Filter(syntax.Filter.real) if t == TRANSFORM_INT: return syntax.Step.Filter(syntax.Filter.int) if t == FILTER_BOOL: return kind_filter(syntax.ValueKind.Boolean()) @@ -181,6 +186,7 @@ def exec(self, v): @extend(syntax.Step.Axis) @extend(syntax.Step.Filter) +@extend(syntax.Step.Function) def exec(self, v): return self.value.exec(v) @@ -225,7 +231,7 @@ def exec(self, v): @extend(syntax.Axis.label) def exec(self, v): v = preserve(_unwrap(v)) - return (v.label,) if isinstance(v, Record) else () + return (v.key,) if isinstance(v, Record) else () @extend(syntax.Axis.keys) def exec(self, v): @@ -383,6 +389,10 @@ def exec(self, v): def exec(self, v): return (v,) if isinstance(v, Embedded) else () +@extend(syntax.Function) +def exec(self, v): + return (len(self.selector.exec(v)),) + if __name__ == '__main__': import sys sel = parse(sys.argv[1]) diff --git a/implementations/python/tests/test_path.py b/implementations/python/tests/test_path.py new file mode 100644 index 0000000..2f8afa5 --- /dev/null +++ b/implementations/python/tests/test_path.py @@ -0,0 +1,30 @@ +import unittest + +from preserves import * +from preserves.path import parse + +class BasicPathTests(unittest.TestCase): + def test_identity(self): + self.assertEqual(parse('').exec(1), (1,)) + self.assertEqual(parse('').exec([]), ([],)) + self.assertEqual(parse('').exec(Record(Symbol('hi'), [])), (Record(Symbol('hi'), []),)) + + def test_children(self): + self.assertEqual(parse('/').exec([1, 2, 3]), (1, 2, 3)) + self.assertEqual(parse('/').exec([1, [2], 3]), (1, [2], 3)) + self.assertEqual(parse('/').exec(Record(Symbol('hi'), [1, [2], 3])), (1, [2], 3)) + + def test_label(self): + self.assertEqual(parse('.^').exec([1, 2, 3]), ()) + self.assertEqual(parse('.^').exec([1, [2], 3]), ()) + self.assertEqual(parse('.^').exec(Record(Symbol('hi'), [1, [2], 3])), (Symbol('hi'),)) + + def test_count(self): + self.assertEqual(parse('').exec([ Record(Symbol('hi'), [1]), + Record(Symbol('no'), [2]), + Record(Symbol('hi'), [3]) ]), + (2,)) + self.assertEqual(parse('/ ').exec([ Record(Symbol('hi'), [1]), + Record(Symbol('no'), [2]), + Record(Symbol('hi'), [3]) ]), + (1, 0, 1)) diff --git a/implementations/rust/preserves-path/path.bin b/implementations/rust/preserves-path/path.bin index ca12e84..e5241a0 100644 --- a/implementations/rust/preserves-path/path.bin +++ b/implementations/rust/preserves-path/path.bin @@ -1,5 +1,5 @@ -´³schema·³version‘³ definitions·³Axis´³orµµ±values´³rec´³lit³values„´³tupleµ„„„„µ± descendants´³rec´³lit³ descendants„´³tupleµ„„„„µ±at´³rec´³lit³at„´³tupleµ´³named³key³any„„„„„µ±label´³rec´³lit³label„´³tupleµ„„„„µ±keys´³rec´³lit³keys„´³tupleµ„„„„µ±length´³rec´³lit³length„´³tupleµ„„„„µ± annotations´³rec´³lit³ annotations„´³tupleµ„„„„µ±embedded´³rec´³lit³embedded„´³tupleµ„„„„µ±parse´³rec´³lit³parse„´³tupleµ´³named³module´³seqof´³atom³Symbol„„„´³named³name´³atom³Symbol„„„„„„µ±unparse´³rec´³lit³unparse„´³tupleµ´³named³module´³seqof´³atom³Symbol„„„´³named³name´³atom³Symbol„„„„„„„„³Step´³orµµ±Axis´³refµ„³Axis„„µ±Filter´³refµ„³Filter„„„„³Filter´³orµµ±nop´³rec´³lit³nop„´³tupleµ„„„„µ±compare´³rec´³lit³compare„´³tupleµ´³named³op´³refµ„³ -Comparison„„´³named³literal³any„„„„„µ±regex´³rec´³lit³regex„´³tupleµ´³named³regex´³atom³String„„„„„„µ±test´³rec´³lit³test„´³tupleµ´³named³pred´³refµ„³ Predicate„„„„„„µ±real´³rec´³lit³real„´³tupleµ„„„„µ±int´³rec´³lit³int„´³tupleµ„„„„µ±kind´³rec´³lit³kind„´³tupleµ´³named³kind´³refµ„³ ValueKind„„„„„„„„³Selector´³seqof´³refµ„³Step„„³ Predicate´³orµµ±Selector´³refµ„³Selector„„µ±not´³rec´³lit³not„´³tupleµ´³named³pred´³refµ„³ Predicate„„„„„„µ±or´³rec´³lit³or„´³tupleµ´³named³preds´³seqof´³refµ„³ Predicate„„„„„„„µ±and´³rec´³lit³and„´³tupleµ´³named³preds´³seqof´³refµ„³ Predicate„„„„„„„„„³ ValueKind´³orµµ±Boolean´³lit³Boolean„„µ±Float´³lit³Float„„µ±Double´³lit³Double„„µ± SignedInteger´³lit³ SignedInteger„„µ±String´³lit³String„„µ± +´³schema·³version‘³ definitions·³Axis´³orµµ±values´³rec´³lit³values„´³tupleµ„„„„µ± descendants´³rec´³lit³ descendants„´³tupleµ„„„„µ±at´³rec´³lit³at„´³tupleµ´³named³key³any„„„„„µ±label´³rec´³lit³label„´³tupleµ„„„„µ±keys´³rec´³lit³keys„´³tupleµ„„„„µ±length´³rec´³lit³length„´³tupleµ„„„„µ± annotations´³rec´³lit³ annotations„´³tupleµ„„„„µ±embedded´³rec´³lit³embedded„´³tupleµ„„„„µ±parse´³rec´³lit³parse„´³tupleµ´³named³module´³seqof´³atom³Symbol„„„´³named³name´³atom³Symbol„„„„„„µ±unparse´³rec´³lit³unparse„´³tupleµ´³named³module´³seqof´³atom³Symbol„„„´³named³name´³atom³Symbol„„„„„„„„³Step´³orµµ±Axis´³refµ„³Axis„„µ±Filter´³refµ„³Filter„„µ±Function´³refµ„³Function„„„„³Filter´³orµµ±nop´³rec´³lit³nop„´³tupleµ„„„„µ±compare´³rec´³lit³compare„´³tupleµ´³named³op´³refµ„³ +Comparison„„´³named³literal³any„„„„„µ±regex´³rec´³lit³regex„´³tupleµ´³named³regex´³atom³String„„„„„„µ±test´³rec´³lit³test„´³tupleµ´³named³pred´³refµ„³ Predicate„„„„„„µ±real´³rec´³lit³real„´³tupleµ„„„„µ±int´³rec´³lit³int„´³tupleµ„„„„µ±kind´³rec´³lit³kind„´³tupleµ´³named³kind´³refµ„³ ValueKind„„„„„„„„³Function´³rec´³lit³count„´³tupleµ´³named³selector´³refµ„³Selector„„„„„³Selector´³seqof´³refµ„³Step„„³ Predicate´³orµµ±Selector´³refµ„³Selector„„µ±not´³rec´³lit³not„´³tupleµ´³named³pred´³refµ„³ Predicate„„„„„„µ±or´³rec´³lit³or„´³tupleµ´³named³preds´³seqof´³refµ„³ Predicate„„„„„„„µ±and´³rec´³lit³and„´³tupleµ´³named³preds´³seqof´³refµ„³ Predicate„„„„„„„„„³ ValueKind´³orµµ±Boolean´³lit³Boolean„„µ±Float´³lit³Float„„µ±Double´³lit³Double„„µ± SignedInteger´³lit³ SignedInteger„„µ±String´³lit³String„„µ± ByteString´³lit³ ByteString„„µ±Symbol´³lit³Symbol„„µ±Record´³lit³Record„„µ±Sequence´³lit³Sequence„„µ±Set´³lit³Set„„µ± Dictionary´³lit³ diff --git a/implementations/rust/preserves-path/src/parse.rs b/implementations/rust/preserves-path/src/parse.rs index 05d32f5..00efff6 100644 --- a/implementations/rust/preserves-path/src/parse.rs +++ b/implementations/rust/preserves-path/src/parse.rs @@ -93,6 +93,22 @@ fn parse_step<'a>(env: &Env, tokens: &'a [IOValue]) -> Result (), + Some(r) => match r.label().value().as_symbol() { + None => return Err(CompilationError::InvalidStep), + Some(t) => match t.as_str() { + "count" => return Ok(Some(( + path::Step::Function(Box::new(path::Function { + selector: parse_selector(env, r.fields())?, + })), + remainder + ))), + _ => return Err(CompilationError::InvalidStep), + } + } + } + match tokens[0].value().as_symbol() { None => return Err(CompilationError::InvalidStep), Some(t) => match t.as_str() { diff --git a/implementations/rust/preserves-path/src/step.rs b/implementations/rust/preserves-path/src/step.rs index f947148..caef8d6 100644 --- a/implementations/rust/preserves-path/src/step.rs +++ b/implementations/rust/preserves-path/src/step.rs @@ -95,15 +95,24 @@ struct KindStep { step: Node, } +#[derive(Debug)] +pub struct CountCollector { + count: usize, +} + +#[derive(Debug)] +struct CountStep { + step: Node, + counter: Node, +} + impl Node { fn new(s: S) -> Self { Node(Rc::new(RefCell::new(s))) } pub fn test(&self, ctxt: &mut Context, value: &IOValue) -> bool { - self.accept(ctxt, value); - self.finish(); - !self.reset().is_empty() + !self.exec(ctxt, value).is_empty() } pub fn accept(&self, ctxt: &mut Context, value: &IOValue) { @@ -145,6 +154,7 @@ impl StepMaker for path::Step { match self { path::Step::Axis(b) => (&**b).connect(step), path::Step::Filter(b) => (&**b).connect(step), + path::Step::Function(b) => (&**b).connect(step), } } } @@ -418,3 +428,40 @@ impl path::Selector { Ok(self.compile()?.exec(ctxt, value)) } } + +impl StepMaker for path::Function { + fn connect(&self, step: Node) -> Result { + // For now, there's just one function: `count`. + Ok(Node::new(CountStep { step, counter: self.selector.connect(CountCollector::new())? })) + } +} + +impl CountCollector { + pub fn new() -> Node { + Node::new(CountCollector { count: 0 }) + } +} + +impl Step for CountCollector { + fn accept(&mut self, _ctxt: &mut Context, _value: &IOValue) { + self.count += 1 + } + + fn finish(&mut self) { + } + + fn reset(&mut self) -> Vec { + let result = vec![IOValue::new(self.count)]; + self.count = 0; + result + } +} + +impl Step for CountStep { + fn accept(&mut self, ctxt: &mut Context, value: &IOValue) { + let count = &self.counter.exec(ctxt, value)[0]; + self.step.accept(ctxt, count) + } + + delegate_finish_and_reset!(self, self.step); +} diff --git a/path/path.bin b/path/path.bin index ca12e84..e5241a0 100644 --- a/path/path.bin +++ b/path/path.bin @@ -1,5 +1,5 @@ -´³schema·³version‘³ definitions·³Axis´³orµµ±values´³rec´³lit³values„´³tupleµ„„„„µ± descendants´³rec´³lit³ descendants„´³tupleµ„„„„µ±at´³rec´³lit³at„´³tupleµ´³named³key³any„„„„„µ±label´³rec´³lit³label„´³tupleµ„„„„µ±keys´³rec´³lit³keys„´³tupleµ„„„„µ±length´³rec´³lit³length„´³tupleµ„„„„µ± annotations´³rec´³lit³ annotations„´³tupleµ„„„„µ±embedded´³rec´³lit³embedded„´³tupleµ„„„„µ±parse´³rec´³lit³parse„´³tupleµ´³named³module´³seqof´³atom³Symbol„„„´³named³name´³atom³Symbol„„„„„„µ±unparse´³rec´³lit³unparse„´³tupleµ´³named³module´³seqof´³atom³Symbol„„„´³named³name´³atom³Symbol„„„„„„„„³Step´³orµµ±Axis´³refµ„³Axis„„µ±Filter´³refµ„³Filter„„„„³Filter´³orµµ±nop´³rec´³lit³nop„´³tupleµ„„„„µ±compare´³rec´³lit³compare„´³tupleµ´³named³op´³refµ„³ -Comparison„„´³named³literal³any„„„„„µ±regex´³rec´³lit³regex„´³tupleµ´³named³regex´³atom³String„„„„„„µ±test´³rec´³lit³test„´³tupleµ´³named³pred´³refµ„³ Predicate„„„„„„µ±real´³rec´³lit³real„´³tupleµ„„„„µ±int´³rec´³lit³int„´³tupleµ„„„„µ±kind´³rec´³lit³kind„´³tupleµ´³named³kind´³refµ„³ ValueKind„„„„„„„„³Selector´³seqof´³refµ„³Step„„³ Predicate´³orµµ±Selector´³refµ„³Selector„„µ±not´³rec´³lit³not„´³tupleµ´³named³pred´³refµ„³ Predicate„„„„„„µ±or´³rec´³lit³or„´³tupleµ´³named³preds´³seqof´³refµ„³ Predicate„„„„„„„µ±and´³rec´³lit³and„´³tupleµ´³named³preds´³seqof´³refµ„³ Predicate„„„„„„„„„³ ValueKind´³orµµ±Boolean´³lit³Boolean„„µ±Float´³lit³Float„„µ±Double´³lit³Double„„µ± SignedInteger´³lit³ SignedInteger„„µ±String´³lit³String„„µ± +´³schema·³version‘³ definitions·³Axis´³orµµ±values´³rec´³lit³values„´³tupleµ„„„„µ± descendants´³rec´³lit³ descendants„´³tupleµ„„„„µ±at´³rec´³lit³at„´³tupleµ´³named³key³any„„„„„µ±label´³rec´³lit³label„´³tupleµ„„„„µ±keys´³rec´³lit³keys„´³tupleµ„„„„µ±length´³rec´³lit³length„´³tupleµ„„„„µ± annotations´³rec´³lit³ annotations„´³tupleµ„„„„µ±embedded´³rec´³lit³embedded„´³tupleµ„„„„µ±parse´³rec´³lit³parse„´³tupleµ´³named³module´³seqof´³atom³Symbol„„„´³named³name´³atom³Symbol„„„„„„µ±unparse´³rec´³lit³unparse„´³tupleµ´³named³module´³seqof´³atom³Symbol„„„´³named³name´³atom³Symbol„„„„„„„„³Step´³orµµ±Axis´³refµ„³Axis„„µ±Filter´³refµ„³Filter„„µ±Function´³refµ„³Function„„„„³Filter´³orµµ±nop´³rec´³lit³nop„´³tupleµ„„„„µ±compare´³rec´³lit³compare„´³tupleµ´³named³op´³refµ„³ +Comparison„„´³named³literal³any„„„„„µ±regex´³rec´³lit³regex„´³tupleµ´³named³regex´³atom³String„„„„„„µ±test´³rec´³lit³test„´³tupleµ´³named³pred´³refµ„³ Predicate„„„„„„µ±real´³rec´³lit³real„´³tupleµ„„„„µ±int´³rec´³lit³int„´³tupleµ„„„„µ±kind´³rec´³lit³kind„´³tupleµ´³named³kind´³refµ„³ ValueKind„„„„„„„„³Function´³rec´³lit³count„´³tupleµ´³named³selector´³refµ„³Selector„„„„„³Selector´³seqof´³refµ„³Step„„³ Predicate´³orµµ±Selector´³refµ„³Selector„„µ±not´³rec´³lit³not„´³tupleµ´³named³pred´³refµ„³ Predicate„„„„„„µ±or´³rec´³lit³or„´³tupleµ´³named³preds´³seqof´³refµ„³ Predicate„„„„„„„µ±and´³rec´³lit³and„´³tupleµ´³named³preds´³seqof´³refµ„³ Predicate„„„„„„„„„³ ValueKind´³orµµ±Boolean´³lit³Boolean„„µ±Float´³lit³Float„„µ±Double´³lit³Double„„µ± SignedInteger´³lit³ SignedInteger„„µ±String´³lit³String„„µ± ByteString´³lit³ ByteString„„µ±Symbol´³lit³Symbol„„µ±Record´³lit³Record„„µ±Sequence´³lit³Sequence„„µ±Set´³lit³Set„„µ± Dictionary´³lit³ diff --git a/path/path.prs b/path/path.prs index c0c8d88..1c3faa0 100644 --- a/path/path.prs +++ b/path/path.prs @@ -9,7 +9,7 @@ Predicate = / . -Step = Axis / Filter . +Step = Axis / Filter / Function . Axis = / @@ -41,3 +41,7 @@ ValueKind = / =Record / =Sequence / =Set / =Dictionary / =Embedded . + +Function = +/ +.