fabulator-grammar 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -7,7 +7,7 @@ begin
7
7
  gem.email = "jgsmith@tamu.edu"
8
8
  gem.homepage = "http://github.com/jgsmith/ruby-fabulator-grammar"
9
9
  gem.authors = ["James Smith"]
10
- gem.add_dependency(%q<fabulator>, [">= 0.0.7"])
10
+ gem.add_dependency(%q<fabulator>, [">= 0.0.8"])
11
11
  # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
12
12
  # not sure how to add dependency of a library that's not a gem
13
13
  gem.requirements << 'bitset, 1.0 or greater'
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.3
1
+ 0.0.4
@@ -0,0 +1,133 @@
1
+ @library
2
+ Feature: Grammars embedded in libraries
3
+
4
+ Scenario: Parsing a grammar xml definition
5
+ Given a context
6
+ And the prefix m as "http://example.com/ns/grammar"
7
+ And the prefix f as "http://dh.tamu.edu/ns/fabulator/1.0#"
8
+ Given the library
9
+ """
10
+ <l:library
11
+ xmlns:l="http://dh.tamu.edu/ns/fabulator/library/1.0#"
12
+ xmlns:g="http://dh.tamu.edu/ns/fabulator/grammar/1.0#"
13
+ xmlns:f="http://dh.tamu.edu/ns/fabulator/1.0#"
14
+ l:ns="http://example.com/ns/grammar"
15
+ >
16
+ <g:grammar>
17
+ <g:context g:mode="normal">
18
+ <g:token g:name="LETTER" g:matches="[:alpha:]" />
19
+ </g:context>
20
+ <g:token g:name="NUMBER" g:matches="[:digit:]" />
21
+ <g:token g:name="LETTER" g:matches="[:upper:]" g:mode="upper"/>
22
+ <g:token g:name="LETTER" g:matches="[:lower:]" g:mode="lower"/>
23
+ <g:rule g:name="something">
24
+ <g:when g:matches="^^ [mode normal] LETTER NUMBER [mode upper] LETTER" />
25
+ </g:rule>
26
+ <g:rule g:name="something2">
27
+ <g:when g:matches="^^ [mode normal] LETTER NUMBER [mode upper] LETTER">
28
+ <g:result g:path="foo" f:select="NUMBER" />
29
+ </g:when>
30
+ </g:rule>
31
+ </g:grammar>
32
+ </l:library>
33
+ """
34
+ Then the expression (m:something?('a0A')) should equal [f:true()]
35
+ And the expression (m:something?('a0a')) should equal [f:false()]
36
+ And the expression (m:something('a0A')/NUMBER) should equal ['0']
37
+ And the expression (m:something2('a0A')/foo) should equal ['0']
38
+
39
+ @library
40
+ Scenario: Using a grammar for filters and constraints
41
+ Given a context
42
+ And the prefix m as "http://example.com/ns/grammar"
43
+ And the prefix f as "http://dh.tamu.edu/ns/fabulator/1.0#"
44
+ Given the library
45
+ """
46
+ <l:library
47
+ xmlns:l="http://dh.tamu.edu/ns/fabulator/library/1.0#"
48
+ xmlns:g="http://dh.tamu.edu/ns/fabulator/grammar/1.0#"
49
+ xmlns:f="http://dh.tamu.edu/ns/fabulator/1.0#"
50
+ l:ns="http://example.com/ns/grammar"
51
+ >
52
+ <g:grammar>
53
+ <g:context g:mode="normal">
54
+ <g:token g:name="LETTER" g:matches="[:alpha:]" />
55
+ </g:context>
56
+ <g:token g:name="NUMBER" g:matches="[:digit:]" />
57
+ <g:token g:name="LETTER" g:matches="[:upper:]" g:mode="upper"/>
58
+ <g:token g:name="LETTER" g:matches="[:lower:]" g:mode="lower"/>
59
+ <g:rule g:name="something">
60
+ <g:when g:matches="[mode normal] LETTER NUMBER [mode upper] LETTER" />
61
+ </g:rule>
62
+ <g:rule g:name="something2">
63
+ <g:when g:matches="[mode normal] LETTER NUMBER [mode upper] LETTER">
64
+ <g:result g:path="foo" f:select="NUMBER" />
65
+ </g:when>
66
+ </g:rule>
67
+ </g:grammar>
68
+ <l:mapping l:name="double">
69
+ <f:value-of f:select=". * 2" />
70
+ </l:mapping>
71
+ <l:function l:name="fctn">
72
+ <f:value-of f:select="$1 - $2" />
73
+ </l:function>
74
+ <l:action l:name="actn" l:has-actions="true">
75
+ <f:value-of f:select="f:eval($actions) * 3" />
76
+ </l:action>
77
+ </l:library>
78
+ """
79
+ And the statemachine
80
+ """
81
+ <f:application xmlns:f="http://dh.tamu.edu/ns/fabulator/1.0#"
82
+ xmlns:m="http://example.com/ns/grammar"
83
+ >
84
+ <f:view f:name="start">
85
+ <f:goes-to f:view="step1">
86
+ <f:params>
87
+ <f:param f:name="foo">
88
+ <f:filter f:name="m:something" />
89
+ <f:value>a0A</f:value>
90
+ </f:param>
91
+ </f:params>
92
+ <f:value f:path="barbell" f:select="m:double(3)" />
93
+ <f:value f:path="barboil">
94
+ <m:actn><f:value-of f:select="7" /></m:actn>
95
+ </f:value>
96
+ </f:goes-to>
97
+ </f:view>
98
+ <f:view f:name="step1">
99
+ <f:goes-to f:view="stop">
100
+ <f:params>
101
+ <f:param f:name="bar">
102
+ <f:filter f:name="trim" />
103
+ <f:constraint f:name="m:something" />
104
+ </f:param>
105
+ </f:params>
106
+ </f:goes-to>
107
+ </f:view>
108
+ <f:view f:name="stop" />
109
+ </f:application>
110
+ """
111
+ Then it should be in the 'start' state
112
+ When I run it with the following params:
113
+ | key | value |
114
+ | foo | bar a0a que |
115
+ Then it should be in the 'start' state
116
+ And the expression (/foo) should be nil
117
+ When I run it with the following params:
118
+ | key | value |
119
+ | foo | bara0Aque |
120
+ Then it should be in the 'step1' state
121
+ And the expression (/foo) should equal ['a0A']
122
+ And the expression (/barbell) should equal [6]
123
+ And the expression (/barboil) should equal [21]
124
+ When I run it with the following params:
125
+ | key | value |
126
+ | bar | a0a |
127
+ Then it should be in the 'step1' state
128
+ When I run it with the following params:
129
+ | key | value |
130
+ | bar | a0B |
131
+ Then it should be in the 'stop' state
132
+ And the expression (/bar) should equal ['a0B']
133
+ And the expression (m:fctn(3,2)) should equal [1]
@@ -2,7 +2,8 @@ Given /the grammar/ do |doc_xml|
2
2
  @context ||= Fabulator::Expr::Context.new
3
3
 
4
4
  if @grammar.nil?
5
- @grammar = Fabulator::Grammar::Actions::Grammar.new.compile_xml(doc_xml, @context)
5
+ @grammar = Fabulator::Grammar::Actions::Grammar.new
6
+ @grammar.compile_xml(doc_xml, @context)
6
7
  else
7
8
  @grammar.compile_xml(doc_xml, @context)
8
9
  end
@@ -10,3 +11,40 @@ Given /the grammar/ do |doc_xml|
10
11
  # puts YAML::dump(@grammar)
11
12
  end
12
13
 
14
+ Given /the library/ do |doc_xml|
15
+ @context ||= Fabulator::Expr::Context.new
16
+
17
+ if @library.nil?
18
+ @library = Fabulator::Lib::Lib.new
19
+ @library.compile_xml(doc_xml, @context)
20
+ else
21
+ @library.compile_xml(doc_xml, @context)
22
+ end
23
+
24
+ @library.register_library
25
+ end
26
+
27
+ Given /the statemachine/ do |doc_xml|
28
+ @context ||= Fabulator::Expr::Context.new
29
+
30
+ if @sm.nil?
31
+ @sm = Fabulator::Core::StateMachine.new
32
+ @sm.compile_xml(doc_xml)
33
+ else
34
+ @sm.compile_xml(doc_xml)
35
+ end
36
+ @sm.init_context(@context)
37
+ end
38
+
39
+ When /I run it with the following params:/ do |param_table|
40
+ params = { }
41
+ param_table.hashes.each do |hash|
42
+ params[hash['key']] = hash['value']
43
+ end
44
+ @sm.run(params)
45
+ end
46
+
47
+ Then /it should be in the '(.*)' state/ do |s|
48
+ @sm.state.should == s
49
+ end
50
+
@@ -4,5 +4,6 @@ $: << File.expand_path(File.dirname(__FILE__))+'/../../lib'
4
4
  $: << File.expand_path(File.dirname(__FILE__))+'/../../../fabulator/lib'
5
5
 
6
6
  require 'fabulator'
7
+ require 'fabulator/lib'
7
8
  require 'fabulator/grammar'
8
9
  require 'spec/expectations'
@@ -5,14 +5,18 @@ module Fabulator
5
5
 
6
6
  namespace GRAMMAR_NS
7
7
 
8
+ element :grammar
9
+
8
10
  contains :rule
9
11
  contains :token
10
12
  contains :context
11
13
 
14
+ contained_in Fabulator::FAB_LIB_NS, :library
15
+
12
16
  has_actions
13
17
 
14
18
  def compile_xml(xml, context = nil)
15
- super
19
+ super
16
20
 
17
21
  @modes = { :default => { } }
18
22
 
@@ -33,8 +37,6 @@ module Fabulator
33
37
 
34
38
  @tokens = nil
35
39
  @rules = nil
36
-
37
- self
38
40
  end
39
41
 
40
42
  def add_rule(r, m = :default)
@@ -53,7 +55,7 @@ module Fabulator
53
55
  cursor.anchored = true
54
56
  ret = do_parse(nom, cursor)
55
57
  cursor.do_skip
56
- cursor.eof ? ret : nil
58
+ cursor.eof? ? ret : nil
57
59
  end
58
60
 
59
61
  def match(ctx, nom, s)
@@ -61,13 +63,67 @@ module Fabulator
61
63
  !do_parse(nom, cursor).nil?
62
64
  end
63
65
 
66
+ def run_function(context, nom, args)
67
+ # treat these as mappings
68
+ strings = args.collect{ |a| a.to_s }
69
+ matching = false
70
+ if nom =~ /\?$/
71
+ matching = true
72
+ end
73
+ nom.gsub!(/\?$/, '')
74
+ ret = matching ? strings.collect{ |s| self.match(context, nom, s) } :
75
+ strings.collect{ |s| self.parse(context, nom, s) }
76
+ #ret -= [ nil ]
77
+ if matching
78
+ ret = ret.collect{ |r| context.root.anon_node(!!r, [FAB_NS, 'boolean']) }
79
+ else
80
+ while ret.select{ |r| r.name.nil? && r.value.nil? }.size > 0
81
+ ret = ret.collect{ |r|
82
+ if r.name.nil? && r.value.nil?
83
+ r.children
84
+ else
85
+ r
86
+ end
87
+ }.flatten - [ nil ]
88
+ end
89
+ if !ret.empty?
90
+ new_ret = ret.first.roots['data'].anon_node(nil)
91
+ ret.each do |r|
92
+ new_ret.add_child(r)
93
+ end
94
+ end
95
+ ret = [ new_ret ]
96
+ end
97
+ ret
98
+ end
99
+
100
+ def run_filter(ctx, nom)
101
+ # runs as a parser and replaces the string with the resulting
102
+ # content that matched
103
+ source = ctx.root.to_s
104
+ cursor = Fabulator::Grammar::Cursor.new(self, ctx, source)
105
+ ret = do_parse(nom, cursor)
106
+ ctx.root.value = ret.nil? ? '' : source[cursor.start .. cursor.pos-1]
107
+ end
108
+
109
+ def run_constraint(ctx, nom)
110
+ # runs as a match and requires full anchored matching
111
+ !self.parse(ctx, nom, ctx.root.to_s).nil?
112
+ end
113
+
64
114
  protected
65
115
 
66
116
  def do_parse(nom, cursor)
67
117
  obj = get_rule(:default, nom)
68
118
  return nil if obj.nil?
69
119
 
70
- obj.parse(cursor)
120
+ begin
121
+ obj.parse(cursor)
122
+ rescue Fabulator::Grammar::RejectParse
123
+ return nil
124
+ else
125
+ return cursor.context.root
126
+ end
71
127
  end
72
128
 
73
129
  end
@@ -0,0 +1,23 @@
1
+ module Fabulator
2
+ module Grammar
3
+ module Actions
4
+ class Result < Fabulator::Action
5
+ attr_accessor :select, :name
6
+
7
+ namespace Fabulator::GRAMMAR_NS
8
+ attribute :path, :static => true
9
+ has_select
10
+ has_actions
11
+
12
+ def run(context, autovivify = false)
13
+ @context.with(context) do |ctx|
14
+ values = self.has_actions? ? self.run_actions(ctx) : @select.run(ctx,false)
15
+ if !values.nil?
16
+ ctx.with_root(ctx.root.roots['result']).set_value(self.path, values)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -21,29 +21,29 @@ module Fabulator
21
21
  # try each when...
22
22
  best_attempt = nil
23
23
  @choices.each do |choice|
24
- cursor.attempt do |c|
25
- ret = choice.parse(c)
26
- if !ret.nil?
27
- score = choice.score(cursor.context, ret)
28
- if best_attempt.nil? || best_attempt[:score] < score
29
- best_attempt = {
30
- :score => score,
31
- :choice => choice,
32
- :ret => ret
33
- }
34
- end
24
+ ret = cursor.attempt { |c| choice.parse(c) }
25
+ if !ret.nil?
26
+ score = choice.score(cursor.context, ret)
27
+ if best_attempt.nil? || best_attempt[:score] < score
28
+ best_attempt = {
29
+ :score => score,
30
+ :choice => choice,
31
+ :ret => ret
32
+ }
35
33
  end
36
34
  end
37
35
  end
38
- return nil if best_attempt.nil?
36
+ raise Fabulator::Grammar::RejectParse if best_attempt.nil?
39
37
  choice = best_attempt[:choice]
40
38
  ret = best_attempt[:ret]
41
39
  if choice.has_actions?
42
- ctx = cursor.context.with_root(cursor.context.root.anon_node(nil))
43
- ctx.merge_data(ret)
44
- choice.run(ctx)
40
+ cursor.context.root.roots['result'] = cursor.context.root.roots['result'].anon_node(nil)
41
+ new_ret = cursor.context.root.roots['result']
42
+ choice.run(cursor.context.with_root(ret))
43
+ cursor.set_result(new_ret)
44
+ else
45
+ cursor.set_result(ret)
45
46
  end
46
- return ret
47
47
  end
48
48
  end
49
49
  end
@@ -25,9 +25,7 @@ module Fabulator
25
25
 
26
26
  def score(context, data)
27
27
  return 0 if @score.nil?
28
- ctx = context.with_root(context.root.anon_node(nil))
29
- ctx.merge_data(data)
30
- (self.score(ctx).value rescue 0)
28
+ (@score.run(context.with_root(context.root.roots['result'])).value rescue 0)
31
29
  end
32
30
  end
33
31
  end
@@ -7,6 +7,7 @@ module Fabulator
7
7
  require 'fabulator/grammar/actions/rule'
8
8
  require 'fabulator/grammar/actions/token'
9
9
  require 'fabulator/grammar/actions/when'
10
+ require 'fabulator/grammar/actions/result'
10
11
 
11
12
  module Grammar
12
13
  module Actions
@@ -19,7 +20,7 @@ module Fabulator
19
20
  structural 'token', Token
20
21
  structural 'when', When
21
22
 
22
- # action 'result', Result
23
+ action 'result', Result
23
24
 
24
25
  ## reference a grammar name
25
26
  function 'match' do |ctx, args|
@@ -1,11 +1,12 @@
1
1
  module Fabulator
2
2
  module Grammar
3
3
  class Cursor
4
- attr_accessor :mode, :skip
4
+ attr_accessor :mode, :skip, :start
5
5
 
6
6
  def initialize(g,ctx,s)
7
7
  @source = s
8
8
  @grammar = g
9
+ @start = 0
9
10
  @curpos = 0
10
11
  @end = @source.length-1
11
12
  @line = 0
@@ -14,6 +15,8 @@ module Fabulator
14
15
  @mode = :default
15
16
  @skip = nil
16
17
  @context = ctx.with_root(ctx.root.anon_node(nil))
18
+ @context.root.roots['result'] = @context.root
19
+ @context.root.axis = 'result'
17
20
  end
18
21
 
19
22
  def context
@@ -30,7 +33,7 @@ module Fabulator
30
33
  end
31
34
  end
32
35
 
33
- def eof
36
+ def eof?
34
37
  @curpos > @end
35
38
  end
36
39
 
@@ -51,7 +54,10 @@ module Fabulator
51
54
  end
52
55
 
53
56
  def point
54
- { :curpos => @curpos, :line => @line, :col => @col, :root => @context.root, :mode => @mode, :anchored => @anchored, :skip => @skip }
57
+ r = { :curpos => @curpos, :line => @line, :col => @col, :root => @context.root, :mode => @mode, :anchored => @anchored, :skip => @skip, :result => @context.root.roots['result'] }
58
+ @context.root.roots['result'] = r[:result].anon_node(nil)
59
+ @context.root = @context.root.roots['result']
60
+ r
55
61
  end
56
62
 
57
63
  def point=(p)
@@ -62,19 +68,63 @@ module Fabulator
62
68
  @anchored = p[:anchored]
63
69
  @skip = p[:skip]
64
70
  @context.root = p[:root]
71
+ @context.root.roots['result'] = p[:result]
65
72
  end
66
73
 
67
74
  def attempt(&block)
68
75
  saved = self.point
69
- ret = yield self
70
- if ret.nil?
76
+ begin
77
+ yield self
78
+ rescue Fabulator::Grammar::RejectParse
71
79
  self.point = saved
72
80
  return nil
73
81
  end
74
-
82
+
83
+ ret = @context.root.roots['result']
84
+ @context.root = saved[:root]
85
+ @context.root.roots['result'] = saved[:result]
75
86
  return ret
76
87
  end
77
88
 
89
+ def set_result(r)
90
+ r = [ r ] unless r.is_a?(Array)
91
+ if r.size > 1
92
+ @context.root = @context.root.anon_node(nil)
93
+ r.each { |rr| @context.root.add_child(rr) }
94
+ elsif !r.empty?
95
+ @context.root = r.first
96
+ end
97
+ @context.root.roots['result'] = @context.root
98
+ end
99
+
100
+ def name_result(nom = nil)
101
+ return if nom.nil?
102
+ return if @context.root.value.nil? && @context.root.children.empty?
103
+ if @context.root.value.nil?
104
+ @context.root.name = nom
105
+ else
106
+ if @context.root.name.nil?
107
+ @context.root.name = nom
108
+ else
109
+ n = @context.root.anon_node(nil)
110
+ n.name = nom
111
+ n.add_child(@context.root)
112
+ @context.root = n
113
+ @context.root.roots['result'] = @context.root
114
+ end
115
+ end
116
+ end
117
+
118
+ def rename_result(nom = nil)
119
+ return if nom.nil?
120
+ return if @context.root.value.nil? && @context.root.children.empty?
121
+ # if @context.root.children.select{ |c| !c.name.nil? }.empty?
122
+ # @context.root.children.each { |c| c.name = nom }
123
+ # else
124
+ @context.root.name = nom
125
+ # end
126
+ end
127
+
78
128
  def find_rule(nom)
79
129
  r = @grammar.get_rule(@mode, nom)
80
130
  if r.nil? && @mode.to_s != 'default'
@@ -105,13 +155,13 @@ module Fabulator
105
155
  end
106
156
 
107
157
  def match_token(regex)
108
- res = nil
109
158
  do_skip
110
159
  if @source[@curpos .. @end] =~ %r{^(#{regex})}
111
- res = $1.to_s
112
- @curpos += res.length
160
+ @context.root.value = $1.to_s
161
+ @curpos += @context.root.value.length
162
+ else
163
+ raise Fabulator::Grammar::RejectParse
113
164
  end
114
- res
115
165
  end
116
166
  end
117
167
  end
@@ -11,16 +11,16 @@ module Fabulator
11
11
  end
12
12
 
13
13
  def parse(source)
14
- ret = nil
14
+ ret = false
15
15
  case @anchor
16
16
  when :start_of_string:
17
- ret = source.pos == 0 ? {} : nil
17
+ ret = source.pos == 0
18
18
  when :start_of_line:
19
19
  when :end_of_string:
20
- ret = source.eof ? {} : nil
20
+ ret = source.eof?
21
21
  when :end_of_line:
22
22
  end
23
- ret
23
+ raise Fabulator::Grammar::RejectParse unless ret
24
24
  end
25
25
  end
26
26
  end
@@ -11,12 +11,12 @@ module Fabulator
11
11
  end
12
12
 
13
13
  def parse(source)
14
- ret = nil
14
+ ret = false
15
15
  source.attempt do |c|
16
16
  ret = @sequence.parse(c)
17
- nil
17
+ raise Fabulator::Grammar::RejectParse
18
18
  end
19
- ret.nil? ? nil : {}
19
+ raise Fabulator::Grammar::RejectParse unless ret
20
20
  end
21
21
  end
22
22
 
@@ -30,12 +30,12 @@ module Fabulator
30
30
  end
31
31
 
32
32
  def parse(source)
33
- ret = nil
33
+ ret = false
34
34
  source.attempt do |c|
35
35
  ret = @sequence.parse(c)
36
- nil
36
+ raise Fabulator::Grammar::RejectParse
37
37
  end
38
- ret.nil? ? {} : nil
38
+ raise Fabulator::Grammar::RejectParse if ret
39
39
  end
40
40
  end
41
41
 
@@ -19,21 +19,29 @@ module Fabulator
19
19
  cursor.anchored = true
20
20
  alternative.parse(cursor)
21
21
  }
22
- return ret unless ret.nil?
22
+ if !ret.nil?
23
+ s.set_result(ret)
24
+ return
25
+ end
23
26
  end
24
27
  else
25
- while !s.eof
28
+ while !s.eof?
29
+ start = s.pos
26
30
  @alternatives.each do |alternative|
27
31
  ret = s.attempt { |cursor|
28
32
  cursor.anchored = true
29
33
  alternative.parse(cursor)
30
34
  }
31
- return ret unless ret.nil?
35
+ if !ret.nil?
36
+ s.start = start
37
+ s.set_result(ret)
38
+ return
39
+ end
32
40
  end
33
41
  s.advance_position(1)
34
42
  end
35
43
  end
36
- return nil
44
+ raise Fabulator::Grammar::RejectParse
37
45
  end
38
46
  end
39
47
  end
@@ -12,32 +12,27 @@ module Fabulator
12
12
  end
13
13
 
14
14
  def parse(source)
15
- results = { }
15
+ results = [ ]
16
16
  @sequences.each do |sequence|
17
- r = sequence.parse(source)
18
- return nil if r.nil? # we fail
19
- nom = (sequence.name rescue nil)
20
- rr = { }
21
- if nom.nil?
22
- rr = r
23
- elsif !r.nil?
24
- if !(r.is_a?(Hash) || r.is_a?(Array)) || !r.empty?
25
- rr[nom] = r
26
- end
27
- end
28
- if rr.is_a?(Hash) && !rr.empty?
29
- rr.each_pair do |k,v|
30
- if results[k].nil?
31
- results[k] = v
32
- elsif results[k].is_a?(Array)
33
- results[k] << v
34
- else
35
- results[k] = [ results[k], v ]
36
- end
17
+ res = source.attempt{ |s| sequence.parse(s) }
18
+ raise Fabulator::Grammar::RejectParse if res.nil?
19
+ res.children.each do |c|
20
+ if c.name.nil?
21
+ c.name = sequence.name if c.name.nil? && !sequence.name.nil?
22
+ res.prune(c)
23
+ results << c if !c.children.empty? || !c.value.nil?
37
24
  end
38
25
  end
26
+ res.name = sequence.name unless sequence.name.nil?
27
+ results << res if !res.children.empty? || !res.value.nil?
28
+ end
29
+ # now we want to merge all of the results
30
+ if results.size > 1
31
+ results = results.select{ |r| !r.name.nil? }
32
+ end
33
+ if !results.empty?
34
+ source.set_result(results)
39
35
  end
40
- results
41
36
  end
42
37
  end
43
38
  end
@@ -6,9 +6,12 @@ module Fabulator
6
6
  @mode = m
7
7
  end
8
8
 
9
+ def name
10
+ nil
11
+ end
12
+
9
13
  def parse(s)
10
14
  s.mode = @mode
11
- {}
12
15
  end
13
16
  end
14
17
  end
@@ -16,9 +16,13 @@ module Fabulator::Grammar::Expr
16
16
 
17
17
  def parse(cursor)
18
18
  rule = cursor.find_rule(@name)
19
- return nil if rule.nil?
19
+ raise Fabulator::Grammar::RejectParse if rule.nil?
20
20
  # we have @name as the path prefix for this part?
21
- cursor.attempt { |c| rule.parse(c) }
21
+ ret = cursor.attempt { |c| rule.parse(c) }
22
+
23
+ raise Fabulator::Grammar::RejectParse if ret.nil?
24
+
25
+ cursor.set_result(ret)
22
26
  end
23
27
  end
24
28
  end
@@ -2,55 +2,64 @@ module Fabulator
2
2
  module Grammar
3
3
  module Expr
4
4
  class RuleSequence
5
+
6
+ ## TODO: allow lookahead here so we can bound the greediness
7
+ ## we also need to do non-greedy versions
8
+ ## we don't have reasonable backtracking yet
9
+
5
10
  def initialize(hypo, atom, quant = nil)
6
11
  @name = hypo
7
12
  @atom = atom
8
13
  @quantifier = quant
9
14
  end
10
15
 
11
- # eventually, this can be the hypo name
16
+ # this is the "hypothetical" name in the grammar
12
17
  def name
13
18
  @name.nil? ? (@atom.name rescue nil) : @name
14
19
  end
15
20
 
16
21
  def parse(source)
17
22
  if @quantifier.nil?
18
- return @atom.parse(source)
19
- end
20
-
21
- case @quantifier.first
22
- when '?'.to_sym: return @atom.parse(source) || { }
23
- when :s:
24
- ret = [ ]
25
- r = @atom.parse(source)
26
- while !r.nil?
27
- ret << r
28
- if @quantifier[1].nil?
29
- r = @atom.parse(source)
30
- else
31
- r = @quantifier[1].parse(source)
32
- if !r.nil?
33
- r = @atom.parse(source)
23
+ @atom.parse(source)
24
+ else
25
+ case @quantifier.first
26
+ when '?'.to_sym:
27
+ r = source.attempt{ |c| @atom.parse(source) }
28
+ source.set_result(r)
29
+ when :s:
30
+ ret = [ ]
31
+ r = source.attempt { |c| @atom.parse(c) }
32
+ while !r.nil?
33
+ ret << r
34
+ if @quantifier[1].nil?
35
+ r = source.attempt { |c| @atom.parse(c) }
36
+ else
37
+ r = source.attempt{ |c| @quantifier[1].parse(c) }
38
+ if !r.nil?
39
+ r = source.attempt{ |c| @atom.parse(c) }
40
+ end
34
41
  end
35
42
  end
36
- end
37
- return ret.empty? ? nil : ret
38
- when 's?'.to_sym:
39
- ret = [ ]
40
- r = @atom.parse(source)
41
- while !r.nil?
42
- ret << r
43
- if @quantifier[1].nil?
44
- r = @atom.parse(source)
45
- else
46
- r = @quantifier[1].parse(source)
47
- if !r.nil?
48
- r = @atom.parse(source)
43
+ raise Fabulator::Grammar::RejectParse if ret.empty?
44
+ source.set_result(ret)
45
+ when 's?'.to_sym:
46
+ ret = [ ]
47
+ r = source.attempt{ |c| @atom.parse(c) }
48
+ while !r.nil?
49
+ ret << r
50
+ if @quantifier[1].nil?
51
+ r = source.attempt{ |c| @atom.parse(c) }
52
+ else
53
+ r = source.attempt{ |c| @quantifier[1].parse(c) }
54
+ if !r.nil?
55
+ r = source.attempt{ |c| @atom.parse(c) }
56
+ end
49
57
  end
50
58
  end
51
- end
52
- return ret.empty? ? {} : ret
59
+ source.set_result(ret) unless ret.empty?
60
+ end
53
61
  end
62
+ source.name_result(@atom.name)
54
63
  end
55
64
  end
56
65
  end
@@ -8,7 +8,6 @@ module Fabulator
8
8
 
9
9
  def parse(cursor)
10
10
  cursor.skip = @skip
11
- {}
12
11
  end
13
12
  end
14
13
  end
@@ -15,12 +15,7 @@ module Fabulator::Grammar::Expr
15
15
  end
16
16
 
17
17
  def parse(cursor)
18
- res = cursor.match_token(self.to_regex)
19
- if res.nil?
20
- return nil
21
- else
22
- return res
23
- end
18
+ cursor.match_token(self.to_regex)
24
19
  end
25
20
  end
26
21
  end
@@ -21,5 +21,7 @@ module Fabulator
21
21
  module Grammar
22
22
  class ParserError < StandardError
23
23
  end
24
+ class RejectParse < StandardError
25
+ end
24
26
  end
25
27
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fabulator-grammar
3
3
  version: !ruby/object:Gem::Version
4
- hash: 25
4
+ hash: 23
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 3
10
- version: 0.0.3
9
+ - 4
10
+ version: 0.0.4
11
11
  platform: ruby
12
12
  authors:
13
13
  - James Smith
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-09-07 00:00:00 +00:00
18
+ date: 2010-09-11 00:00:00 +00:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -26,12 +26,12 @@ dependencies:
26
26
  requirements:
27
27
  - - ">="
28
28
  - !ruby/object:Gem::Version
29
- hash: 17
29
+ hash: 15
30
30
  segments:
31
31
  - 0
32
32
  - 0
33
- - 7
34
- version: 0.0.7
33
+ - 8
34
+ version: 0.0.8
35
35
  type: :runtime
36
36
  version_requirements: *id001
37
37
  description: The grammar Fabulator extension provides regular expression support.
@@ -48,6 +48,7 @@ files:
48
48
  - Rakefile
49
49
  - VERSION
50
50
  - features/grammar.feature
51
+ - features/library.feature
51
52
  - features/step_definitions/expression_steps.rb
52
53
  - features/step_definitions/grammar_steps.rb
53
54
  - features/step_definitions/template_steps.rb
@@ -58,6 +59,7 @@ files:
58
59
  - lib/fabulator/grammar/actions.rb
59
60
  - lib/fabulator/grammar/actions/context.rb
60
61
  - lib/fabulator/grammar/actions/grammar.rb
62
+ - lib/fabulator/grammar/actions/result.rb
61
63
  - lib/fabulator/grammar/actions/rule.rb
62
64
  - lib/fabulator/grammar/actions/token.rb
63
65
  - lib/fabulator/grammar/actions/when.rb