fabulator-grammar 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +73 -0
- data/Rakefile +63 -0
- data/VERSION +1 -0
- data/features/grammar.feature +88 -0
- data/features/step_definitions/expression_steps.rb +103 -0
- data/features/step_definitions/grammar_steps.rb +18 -0
- data/features/step_definitions/template_steps.rb +25 -0
- data/features/step_definitions/xml_steps.rb +23 -0
- data/features/support/env.rb +7 -0
- data/lib/fabulator/grammar.rb +16 -0
- data/lib/fabulator/grammar/actions.rb +65 -0
- data/lib/fabulator/grammar/expr/any.rb +7 -0
- data/lib/fabulator/grammar/expr/char_set.rb +45 -0
- data/lib/fabulator/grammar/expr/rule.rb +36 -0
- data/lib/fabulator/grammar/expr/rule_ref.rb +13 -0
- data/lib/fabulator/grammar/expr/rules.rb +15 -0
- data/lib/fabulator/grammar/expr/sequence.rb +26 -0
- data/lib/fabulator/grammar/expr/text.rb +11 -0
- data/lib/fabulator/grammar/parser.rb +548 -0
- data/regex.racc +183 -0
- metadata +101 -0
data/README.markdown
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
Fabulator Grammar Extension
|
2
|
+
---------------------------
|
3
|
+
|
4
|
+
This extension provides basic support for regular expressions modeled
|
5
|
+
loosely after [Perl 6 grammars][].
|
6
|
+
|
7
|
+
For now, this extension provides a single function: 'match(regex, string)'.
|
8
|
+
This function returns true or false depending on whether or not the regular
|
9
|
+
expression matches the given string.
|
10
|
+
|
11
|
+
The current implementation compiled regular expressions into Ruby
|
12
|
+
RegExp objects for faster execution. This may change as new capabilities
|
13
|
+
are added that might not be supportable by pure Ruby regular expressions.
|
14
|
+
|
15
|
+
The goal of the grammar extension is to provide a rich environment for
|
16
|
+
writing parsers that can be integrated into the Fabulator environment.
|
17
|
+
|
18
|
+
Namespace: "http://dh.tamu.edu/ns/fabulator/grammar/1.0#"
|
19
|
+
|
20
|
+
|
21
|
+
Regular Expressions
|
22
|
+
===================
|
23
|
+
|
24
|
+
The following characters are considered special and should be escaped if
|
25
|
+
you are not using for their special meaning:
|
26
|
+
|
27
|
+
Parenthesis ( )
|
28
|
+
Brackets [ ]
|
29
|
+
Curly Brackets { }
|
30
|
+
Angle Brackets < >
|
31
|
+
Dot .
|
32
|
+
Question ?
|
33
|
+
Caret ^
|
34
|
+
Dollar $
|
35
|
+
Asterisk *
|
36
|
+
Plus +
|
37
|
+
|
38
|
+
|
39
|
+
Additionally, the hyphen '-' should be the first character in a character
|
40
|
+
class if it is matching itself instead of indicated a range. It can not
|
41
|
+
be used as either the beginning or end of a range at present.
|
42
|
+
|
43
|
+
Whitespace can separate tokens in the regular expression, but the amount of
|
44
|
+
Whitespace is not significant. All tokens assume the possible existance of
|
45
|
+
whitespace in the string being matched and will not fail due to the
|
46
|
+
whitespace being there.
|
47
|
+
|
48
|
+
[Perl 6 grammars]: http://feather.perl6.nl/syn/S05.html
|
49
|
+
|
50
|
+
LICENSE:
|
51
|
+
========
|
52
|
+
|
53
|
+
Copyright (c) 2010 Texas A&M University
|
54
|
+
|
55
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
56
|
+
a copy of this software and associated documentation files (the
|
57
|
+
'Software'), to deal in the Software without restriction, including
|
58
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
59
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
60
|
+
permit persons to whom the Software is furnished to do so, subject to
|
61
|
+
the following conditions:
|
62
|
+
|
63
|
+
The above copyright notice and this permission notice shall be
|
64
|
+
included in all copies or substantial portions of the Software.
|
65
|
+
|
66
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
67
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
68
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
69
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
70
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
71
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
72
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
73
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
begin
|
2
|
+
require 'jeweler'
|
3
|
+
Jeweler::Tasks.new do |gem|
|
4
|
+
gem.name = "fabulator-grammar"
|
5
|
+
gem.summary = %Q{Grammar extension to Fabulator.}
|
6
|
+
gem.description = %Q{The grammar Fabulator extension provides regular expression support.}
|
7
|
+
gem.email = "jgsmith@tamu.edu"
|
8
|
+
gem.homepage = "http://github.com/jgsmith/ruby-fabulator-grammar"
|
9
|
+
gem.authors = ["James Smith"]
|
10
|
+
gem.add_dependency(%q<fabulator>, [">= 0.0.1"])
|
11
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
12
|
+
end
|
13
|
+
rescue LoadError
|
14
|
+
puts "Jeweler (or a dependency) not available. This is only required if you plan to package fabulator-exhibit as a gem."
|
15
|
+
end
|
16
|
+
|
17
|
+
require 'rake'
|
18
|
+
require 'rake/rdoctask'
|
19
|
+
require 'rake/testtask'
|
20
|
+
|
21
|
+
require 'cucumber'
|
22
|
+
require 'cucumber/rake/task'
|
23
|
+
|
24
|
+
task :features => 'spec:integration'
|
25
|
+
|
26
|
+
namespace :spec do
|
27
|
+
|
28
|
+
desc "Run the Cucumber features"
|
29
|
+
Cucumber::Rake::Task.new(:integration) do |t|
|
30
|
+
t.fork = true
|
31
|
+
t.cucumber_opts = ['--format', (ENV['CUCUMBER_FORMAT'] || 'pretty')]
|
32
|
+
# t.feature_pattern = "#{extension_root}/features/**/*.feature"
|
33
|
+
t.profile = "default"
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
namespace :update do
|
39
|
+
desc "update the manifest"
|
40
|
+
task :manifest do
|
41
|
+
system %q[touch Manifest.txt; rake check_manifest | grep -v "(in " | patch]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
desc 'Generate documentation for the fabulator exhibit extension.'
|
46
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
47
|
+
rdoc.rdoc_dir = 'rdoc'
|
48
|
+
rdoc.title = 'Fabulator'
|
49
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
50
|
+
rdoc.rdoc_files.include('README')
|
51
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
52
|
+
end
|
53
|
+
|
54
|
+
# For extensions that are in transition
|
55
|
+
desc 'Test the fabulator exhibit extension.'
|
56
|
+
Rake::TestTask.new(:test) do |t|
|
57
|
+
t.libs << 'lib'
|
58
|
+
t.pattern = 'test/**/*_test.rb'
|
59
|
+
t.verbose = true
|
60
|
+
end
|
61
|
+
|
62
|
+
# Load any custom rakefiles for extension
|
63
|
+
Dir[File.dirname(__FILE__) + '/tasks/*.rake'].sort.each { |f| require f }
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1
|
@@ -0,0 +1,88 @@
|
|
1
|
+
Feature: Basic regex parsing
|
2
|
+
|
3
|
+
Scenario: Parsing a simple text string
|
4
|
+
Given a context
|
5
|
+
And the prefix g as "http://dh.tamu.edu/ns/fabulator/grammar/1.0#"
|
6
|
+
When I parse the regex (foo)
|
7
|
+
Then it should match "foo"
|
8
|
+
|
9
|
+
Scenario: Parsing a simple text string
|
10
|
+
Given a context
|
11
|
+
And the prefix g as "http://dh.tamu.edu/ns/fabulator/grammar/1.0#"
|
12
|
+
When I parse the regex (foo+)
|
13
|
+
Then it should match "foo"
|
14
|
+
And it should match "foooo"
|
15
|
+
And it should not match "fo"
|
16
|
+
|
17
|
+
Scenario: Parsing a simple text string
|
18
|
+
Given a context
|
19
|
+
And the prefix g as "http://dh.tamu.edu/ns/fabulator/grammar/1.0#"
|
20
|
+
When I parse the regex (foo+?)
|
21
|
+
Then it should match "fooooooo"
|
22
|
+
|
23
|
+
Scenario: Parsing a simple text string
|
24
|
+
Given a context
|
25
|
+
And the prefix g as "http://dh.tamu.edu/ns/fabulator/grammar/1.0#"
|
26
|
+
When I parse the regex (foo{1,4}$)
|
27
|
+
Then it should match "foo"
|
28
|
+
Then it should match "fooooo"
|
29
|
+
Then it should not match "foooooo"
|
30
|
+
Then it should not match "fo"
|
31
|
+
|
32
|
+
@chars
|
33
|
+
Scenario: Parsing a simple text string
|
34
|
+
Given a context
|
35
|
+
And the prefix g as "http://dh.tamu.edu/ns/fabulator/grammar/1.0#"
|
36
|
+
When I parse the regex (^[Ff]o[-a-zA-F01234-9]+?)
|
37
|
+
Then it should match "foo"
|
38
|
+
And it should match "Foo"
|
39
|
+
And it should match "FoF03z-"
|
40
|
+
And it should not match "hellofoo"
|
41
|
+
|
42
|
+
@chars
|
43
|
+
Scenario: Parsing a simple text string
|
44
|
+
Given a context
|
45
|
+
And the prefix g as "http://dh.tamu.edu/ns/fabulator/grammar/1.0#"
|
46
|
+
When I parse the regex (^[^0-9][a-z][0-9]$)
|
47
|
+
Then it should match "fo0"
|
48
|
+
And it should not match "0l0"
|
49
|
+
|
50
|
+
Scenario: Parsing a simple text string
|
51
|
+
Given a context
|
52
|
+
And the prefix g as "http://dh.tamu.edu/ns/fabulator/grammar/1.0#"
|
53
|
+
When I parse the regex (^[^0-9].[0-9]$)
|
54
|
+
Then it should match "fo0"
|
55
|
+
And it should not match "0l0"
|
56
|
+
And it should not match "00"
|
57
|
+
|
58
|
+
@chars
|
59
|
+
Scenario: Parsing a simple text string
|
60
|
+
Given a context
|
61
|
+
And the prefix g as "http://dh.tamu.edu/ns/fabulator/grammar/1.0#"
|
62
|
+
When I parse the regex ([\[-\]]o[a-z\]A-F01234-9]+?)
|
63
|
+
|
64
|
+
Scenario: Parsing a simple text string
|
65
|
+
Given a context
|
66
|
+
And the prefix g as "http://dh.tamu.edu/ns/fabulator/grammar/1.0#"
|
67
|
+
When I parse the regex (<foo><g:bar>*)
|
68
|
+
|
69
|
+
Scenario: Parsing a simple text string
|
70
|
+
Given a context
|
71
|
+
And the prefix g as "http://dh.tamu.edu/ns/fabulator/grammar/1.0#"
|
72
|
+
When I parse the regex (bar ( <foo> <g:bar> ) *)
|
73
|
+
|
74
|
+
Scenario: Adding two numbers together as a union
|
75
|
+
Given a context
|
76
|
+
And the prefix f as "http://dh.tamu.edu/ns/fabulator/1.0#"
|
77
|
+
And the prefix g as "http://dh.tamu.edu/ns/fabulator/grammar/1.0#"
|
78
|
+
When I run the expression (g:match('^foo', 'fooo'))
|
79
|
+
Then I should get 1 item
|
80
|
+
And item 0 should be true
|
81
|
+
|
82
|
+
Scenario: Adding two numbers together as a union
|
83
|
+
Given a context
|
84
|
+
And the prefix f as "http://dh.tamu.edu/ns/fabulator/1.0#"
|
85
|
+
And the prefix g as "http://dh.tamu.edu/ns/fabulator/grammar/1.0#"
|
86
|
+
When I run the expression (g:match('^foo', 'bfooo'))
|
87
|
+
Then I should get 1 item
|
88
|
+
And item 0 should be false
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
Transform /^(expression|context) \((.*)\)$/ do |n, arg|
|
4
|
+
@context ||= Fabulator::Expr::Context.new
|
5
|
+
@parser ||= Fabulator::Expr::Parser.new
|
6
|
+
@parser.parse(arg, @context)
|
7
|
+
end
|
8
|
+
|
9
|
+
Transform /^\[(.*)\]$/ do |arg|
|
10
|
+
@context ||= Fabulator::Expr::Context.new
|
11
|
+
@parser ||= Fabulator::Expr::Parser.new
|
12
|
+
@parser.parse(arg, @context)
|
13
|
+
end
|
14
|
+
|
15
|
+
Transform /^(\d+)$/ do |arg|
|
16
|
+
arg.to_i
|
17
|
+
end
|
18
|
+
|
19
|
+
Given 'a context' do
|
20
|
+
@context ||= Fabulator::Expr::Context.new
|
21
|
+
@parser ||= Fabulator::Expr::Parser.new
|
22
|
+
@grammar_parser ||= Fabulator::Grammar::Parser.new
|
23
|
+
end
|
24
|
+
|
25
|
+
Given /the prefix (\S+) as "([^"]+)"/ do |p,h|
|
26
|
+
@context ||= Fabulator::Expr::Context.new
|
27
|
+
@context.set_ns(p, h)
|
28
|
+
end
|
29
|
+
|
30
|
+
Given /that (\[.*\]) is set to (\[.*\])/ do |l,r|
|
31
|
+
@context.set_value(l, r)
|
32
|
+
end
|
33
|
+
|
34
|
+
When /I run the (expression \(.*\)) in the (context \(.*\))/ do |exp, cp|
|
35
|
+
@expr = exp
|
36
|
+
if cp.nil? || cp == ''
|
37
|
+
@result = []
|
38
|
+
@cp = @context.root
|
39
|
+
else
|
40
|
+
@cp = cp.run(@context).first || @context.root
|
41
|
+
@result = @expr.run(@context.with_root(@cp))
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
When /I run the (expression \(.*\))/ do |exp|
|
46
|
+
## assume '/' as the context here
|
47
|
+
@expr = exp
|
48
|
+
@cp = @data
|
49
|
+
#puts YAML::dump(@expr)
|
50
|
+
@result = @expr.run(@context.with_root(@cp))
|
51
|
+
#puts YAML::dump(@result)
|
52
|
+
end
|
53
|
+
|
54
|
+
When /I unify the types? (.*)/ do |ts|
|
55
|
+
types = ts.split(/\s*,\s*/)
|
56
|
+
typea = types.collect { |t|
|
57
|
+
pn = t.split(/:/, 2)
|
58
|
+
[ @context.get_ns(pn[0]), pn[1] ]
|
59
|
+
}
|
60
|
+
@type_result = Fabulator::ActionLib.unify_types(
|
61
|
+
types.collect { |t|
|
62
|
+
pn = t.split(/:/, 2)
|
63
|
+
[ @context.get_ns(pn[0]), pn[1] ]
|
64
|
+
}
|
65
|
+
)
|
66
|
+
end
|
67
|
+
|
68
|
+
Then /I should get the type (.*)/ do |t|
|
69
|
+
pn = t.split(/:/, 2)
|
70
|
+
@type_result[0].should == @context.get_ns(pn[0])
|
71
|
+
@type_result[1].should == pn[1]
|
72
|
+
end
|
73
|
+
|
74
|
+
Then /I should get (\d+) items?/ do |count|
|
75
|
+
@result.length.should == count
|
76
|
+
end
|
77
|
+
|
78
|
+
Then /item (\d+) should be (\[.*\])/ do |i,t|
|
79
|
+
test = t.run(@context.with_root(@cp)).first
|
80
|
+
#puts "Result: #{@result[i.to_i].to_s.class.to_s}"
|
81
|
+
@result[i.to_i].to_s.should == test.to_s
|
82
|
+
end
|
83
|
+
|
84
|
+
Then /item (\d+) should be false/ do |i|
|
85
|
+
(!!@result[i.to_i].value).should == false
|
86
|
+
end
|
87
|
+
|
88
|
+
Then /item (\d+) should be true/ do |i|
|
89
|
+
(!!@result[i.to_i].value).should == true
|
90
|
+
end
|
91
|
+
|
92
|
+
Then /the (expression \(.*\)) should equal (\[.*\])/ do |x, y|
|
93
|
+
a = x.run(@context)
|
94
|
+
b = y.run(@context)
|
95
|
+
#puts YAML::dump(a)
|
96
|
+
#puts YAML::dump(b)
|
97
|
+
#puts YAML::dump(@context)
|
98
|
+
a.first.value.should == b.first.value
|
99
|
+
end
|
100
|
+
|
101
|
+
Then /the (expression \(.*\)) should be nil/ do |x|
|
102
|
+
x.run(@context).first.should == nil
|
103
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
When /^I parse the regex \((.*)\)$/ do |regex|
|
4
|
+
@context ||= Fabulator::Expr::Context.new
|
5
|
+
@grammar_parser ||= Fabulator::Grammar::Parser.new
|
6
|
+
@regex = @grammar_parser.parse(regex, @context)
|
7
|
+
# puts YAML::dump(r)
|
8
|
+
# puts @regex.to_regex
|
9
|
+
# pending # express the regexp above with the code you wish you had
|
10
|
+
end
|
11
|
+
|
12
|
+
Then /^it should match "(.*)"$/ do |str|
|
13
|
+
str.should =~ @regex.to_regex
|
14
|
+
end
|
15
|
+
|
16
|
+
Then /^it should not match "(.*)"$/ do |str|
|
17
|
+
str.should_not =~ @regex.to_regex
|
18
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
Given /^the template$/ do |doc_xml|
|
2
|
+
@template_text = doc_xml
|
3
|
+
end
|
4
|
+
|
5
|
+
When /^I render the template$/ do
|
6
|
+
parser = Fabulator::Template::Parser.new
|
7
|
+
@template_result = parser.parse(@context, @template_text)
|
8
|
+
end
|
9
|
+
|
10
|
+
When /^I set the captions to:$/ do |caption_table|
|
11
|
+
captions = { }
|
12
|
+
caption_table.hashes.each do |h|
|
13
|
+
captions[h['path']] = h['caption']
|
14
|
+
end
|
15
|
+
|
16
|
+
@template_result.add_captions(captions)
|
17
|
+
end
|
18
|
+
|
19
|
+
Then /^the rendered text should equal$/ do |doc|
|
20
|
+
@template_result.to_s.should == %{<?xml version="1.0" encoding="UTF-8"?>\n} + doc + "\n"
|
21
|
+
end
|
22
|
+
|
23
|
+
Then /^the rendered html should equal$/ do |doc|
|
24
|
+
@template_result.to_html.should == doc
|
25
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
Given /the statemachine/ do |doc_xml|
|
2
|
+
@context ||= Fabulator::Expr::Context.new
|
3
|
+
|
4
|
+
if @sm.nil?
|
5
|
+
@sm = Fabulator::Core::StateMachine.new.compile_xml(doc_xml)
|
6
|
+
else
|
7
|
+
@sm.compile_xml(doc_xml)
|
8
|
+
end
|
9
|
+
@sm.init_context(@context)
|
10
|
+
end
|
11
|
+
|
12
|
+
When /I run it with the following params:/ do |param_table|
|
13
|
+
params = { }
|
14
|
+
param_table.hashes.each do |hash|
|
15
|
+
params[hash['key']] = hash['value']
|
16
|
+
end
|
17
|
+
@sm.run(params)
|
18
|
+
#puts YAML::dump(@sm)
|
19
|
+
end
|
20
|
+
|
21
|
+
Then /it should be in the '(.*)' state/ do |s|
|
22
|
+
@sm.state.should == s
|
23
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'fabulator/grammar/parser'
|
2
|
+
require 'fabulator/grammar/actions'
|
3
|
+
require 'fabulator/grammar/expr/rules'
|
4
|
+
require 'fabulator/grammar/expr/rule'
|
5
|
+
require 'fabulator/grammar/expr/rule_ref'
|
6
|
+
require 'fabulator/grammar/expr/text'
|
7
|
+
require 'fabulator/grammar/expr/sequence'
|
8
|
+
require 'fabulator/grammar/expr/char_set'
|
9
|
+
require 'fabulator/grammar/expr/any'
|
10
|
+
|
11
|
+
module Fabulator
|
12
|
+
module Grammar
|
13
|
+
class ParserError < StandardError
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
#require 'fabulator/grammar/actions/grammar'
|
2
|
+
|
3
|
+
module Fabulator
|
4
|
+
GRAMMAR_NS = "http://dh.tamu.edu/ns/fabulator/grammar/1.0#"
|
5
|
+
module Grammar
|
6
|
+
module Actions
|
7
|
+
class Lib
|
8
|
+
include Fabulator::ActionLib
|
9
|
+
|
10
|
+
register_namespace GRAMMAR_NS
|
11
|
+
|
12
|
+
#action 'grammar', Grammar
|
13
|
+
|
14
|
+
## reference a grammar name
|
15
|
+
function 'match' do |ctx, args|
|
16
|
+
# first arg is the regex or <rule name>
|
17
|
+
regex = args[0].to_s
|
18
|
+
parser = Fabulator::Grammar::Parser.new
|
19
|
+
compiled = parser.parse(regex, ctx).to_regex
|
20
|
+
if args[1].is_a?(Array)
|
21
|
+
args[1].collect{|a|
|
22
|
+
if a.to_s =~ compiled
|
23
|
+
ctx.root.anon_node(true)
|
24
|
+
else
|
25
|
+
ctx.root.anon_node(false)
|
26
|
+
end
|
27
|
+
}
|
28
|
+
elsif args[1].to_s =~ compiled
|
29
|
+
[ ctx.root.anon_node(true) ]
|
30
|
+
else
|
31
|
+
[ ctx.root.anon_node(false) ]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# function 'tokenize' do |ctx, args|
|
36
|
+
# end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# modifiers: g:minimal, g:ignore-case, g:space, g:ratchet
|
43
|
+
|
44
|
+
# need the concept of hypotheticals here
|
45
|
+
|
46
|
+
# <g:grammar g:namespace=''>
|
47
|
+
# <g:token|g:regex g:name=''>
|
48
|
+
# <g:literal />
|
49
|
+
# <g:capture>...</g:capture>
|
50
|
+
# <g:group>...</g:group>
|
51
|
+
# <g:before>...</g:before>
|
52
|
+
# <g:after>...</g:after>
|
53
|
+
# <g:not-before>...</g:not-before>
|
54
|
+
# <g:not-after>...</g:not-after>
|
55
|
+
# <g:alternatives>...</g:alternatives>
|
56
|
+
# <g:token />
|
57
|
+
# <g:one-or-more>...</g:one-or-more>
|
58
|
+
# <g:zero-or-more>...</g:zero-or-more>
|
59
|
+
# <g:zero-or-one>...</g:zero-or-one>
|
60
|
+
# <g:many g:min='' g:max=''>...</g:many>
|
61
|
+
# </g:token>
|
62
|
+
# <g:rule g:name=''>
|
63
|
+
# <g:when><g:pattern>...</g:pattern>...</g:when>
|
64
|
+
# </g:rule>
|
65
|
+
# </g:grammar>
|