fabulator-grammar 0.0.3 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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