sexpr 0.5.1 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (89) hide show
  1. data/CHANGELOG.md +16 -0
  2. data/Gemfile +3 -3
  3. data/Gemfile.lock +14 -14
  4. data/Rakefile +0 -4
  5. data/examples/bool_expr/bool_expr.rb +1 -95
  6. data/lib/sexpr/grammar/matching.rb +3 -3
  7. data/lib/sexpr/grammar/parsing.rb +1 -1
  8. data/lib/sexpr/grammar/tagging.rb +16 -13
  9. data/lib/sexpr/loader.rb +1 -0
  10. data/lib/sexpr/matcher.rb +3 -2
  11. data/lib/sexpr/matcher/many.rb +16 -1
  12. data/lib/sexpr/matcher/non_terminal.rb +31 -0
  13. data/lib/sexpr/matcher/rule.rb +3 -6
  14. data/lib/sexpr/node.rb +1 -1
  15. data/lib/sexpr/parser.rb +1 -1
  16. data/lib/sexpr/processor.rb +11 -1
  17. data/lib/sexpr/processor/helper.rb +1 -1
  18. data/lib/sexpr/processor/sexpr_coercions.rb +6 -14
  19. data/lib/sexpr/rewriter.rb +0 -8
  20. data/lib/sexpr/version.rb +3 -3
  21. data/sexpr.gemspec +3 -3
  22. data/sexpr.noespec +13 -6
  23. data/spec/integration/bool_expr/test_not_push.rb +38 -0
  24. data/spec/integration/bool_expr/test_parsing.rb +15 -0
  25. data/spec/integration/bool_expr/test_tagging.rb +25 -0
  26. data/spec/integration/bool_expr/test_validation.rb +15 -0
  27. data/spec/{test_readme_examples.rb → integration/test_readme_examples.rb} +0 -0
  28. data/spec/spec_helper.rb +1 -1
  29. data/spec/unit/grammar/matching/test_compile_rule.rb +55 -0
  30. data/spec/{grammar → unit/grammar}/matching/test_compile_rule_defn.rb +0 -0
  31. data/spec/unit/grammar/matching/test_compile_rules.rb +45 -0
  32. data/spec/{grammar → unit/grammar}/options/test_install_parser.rb +0 -0
  33. data/spec/{grammar → unit/grammar}/options/test_install_path.rb +0 -0
  34. data/spec/{grammar → unit/grammar}/options/test_install_root.rb +0 -0
  35. data/spec/{grammar → unit/grammar}/tagging/test_looks_a_sexpr.rb +0 -0
  36. data/spec/{grammar → unit/grammar}/tagging/test_mod2rulename.rb +0 -0
  37. data/spec/{grammar → unit/grammar}/tagging/test_rule2modname.rb +0 -0
  38. data/spec/{grammar → unit/grammar}/tagging/test_tag_sexpr.rb +0 -0
  39. data/spec/unit/grammar/tagging/test_tagging_module_for.rb +72 -0
  40. data/spec/{grammar → unit/grammar}/test_new.rb +0 -0
  41. data/spec/{grammar → unit/grammar}/test_parse.rb +0 -0
  42. data/spec/{grammar → unit/grammar}/test_sexpr.rb +0 -0
  43. data/spec/{matcher → unit/matcher}/alternative/test_eat.rb +1 -1
  44. data/spec/{matcher → unit/matcher}/alternative/test_match_q.rb +0 -0
  45. data/spec/{matcher → unit/matcher}/many/test_eat.rb +0 -0
  46. data/spec/{matcher → unit/matcher}/many/test_initialize.rb +0 -0
  47. data/spec/{matcher → unit/matcher}/many/test_match_q.rb +0 -0
  48. data/spec/unit/matcher/non_terminal/test_eat.rb +31 -0
  49. data/spec/unit/matcher/non_terminal/test_match_q.rb +32 -0
  50. data/spec/{matcher → unit/matcher}/reference/test_eat.rb +0 -0
  51. data/spec/{matcher → unit/matcher}/reference/test_match_q.rb +0 -0
  52. data/spec/unit/matcher/rule/test_eat.rb +17 -0
  53. data/spec/unit/matcher/rule/test_match_q.rb +17 -0
  54. data/spec/{matcher → unit/matcher}/sequence/test_eat.rb +0 -0
  55. data/spec/{matcher → unit/matcher}/sequence/test_match_q.rb +0 -0
  56. data/spec/{matcher → unit/matcher}/terminal/test_eat.rb +1 -1
  57. data/spec/{matcher → unit/matcher}/terminal/test_match_q.rb +0 -0
  58. data/spec/{matcher → unit/matcher}/terminal/test_terminal_match.rb +0 -0
  59. data/spec/{node → unit/node}/test_sexpr_body.rb +0 -0
  60. data/spec/{node → unit/node}/test_sexpr_copy.rb +0 -0
  61. data/spec/{node → unit/node}/test_sexpr_type.rb +0 -0
  62. data/spec/{node → unit/node}/test_tracking_markers.rb +0 -0
  63. data/spec/{parser → unit/parser}/citrus/test_new.rb +0 -0
  64. data/spec/{parser → unit/parser}/citrus/test_parse.rb +0 -0
  65. data/spec/{parser → unit/parser}/citrus/test_recognize.rb +0 -0
  66. data/spec/{parser → unit/parser}/citrus/test_registration.rb +0 -0
  67. data/spec/{parser → unit/parser}/citrus/test_to_sexpr.rb +0 -0
  68. data/spec/{parser → unit/parser}/test_factor.rb +0 -0
  69. data/spec/{parser → unit/parser}/test_input_text.rb +0 -0
  70. data/spec/{processor → unit/processor}/helper/test_call.rb +0 -0
  71. data/spec/{processor → unit/processor}/test_apply.rb +0 -0
  72. data/spec/{processor → unit/processor}/test_build_helper_chain.rb +0 -0
  73. data/spec/{processor → unit/processor}/test_call.rb +0 -0
  74. data/spec/unit/processor/test_grammar.rb +56 -0
  75. data/spec/{processor → unit/processor}/test_helper.rb +0 -0
  76. data/spec/{processor → unit/processor}/test_sexpr_coercions.rb +6 -2
  77. data/spec/{processor → unit/processor}/test_use.rb +0 -0
  78. data/spec/{rewriter → unit/rewriter}/test_copy_and_apply.rb +0 -0
  79. data/spec/{test_load.rb → unit/test_load.rb} +3 -2
  80. data/spec/{test_rewriter.rb → unit/test_rewriter.rb} +0 -0
  81. data/spec/{test_sexpr.rb → unit/test_sexpr.rb} +1 -1
  82. data/tasks/gem.rake +9 -4
  83. data/tasks/test.rake +26 -0
  84. metadata +163 -117
  85. data/spec/grammar/matching/test_compile_rule.rb +0 -23
  86. data/spec/matcher/rule/test_eat.rb +0 -21
  87. data/spec/matcher/rule/test_match_q.rb +0 -24
  88. data/tasks/spec_test.rake +0 -71
  89. data/tasks/unit_test.rake +0 -76
@@ -1,3 +1,19 @@
1
+ # 0.6.0 / 2013-09-26
2
+
3
+ * Major enhancements (possibly breaking changes)
4
+
5
+ * All grammar rules are now proper Matcher::Rule instances. A NonTerminal
6
+ matcher is added and is used for non-terminal rule productions and matching
7
+ (all but Terminal and Alternative).
8
+ * `Rewriter.sexpr_grammar` and `Rewriter#sexpr_grammar` have been removed.
9
+ `Processor.grammar` and `Processor#grammar` are provided instead.
10
+
11
+ * Minor enhancements
12
+
13
+ * Added `Grammar#tagging_module_for(rulename)` that returns the user-defined
14
+ module used to tag a given grammar rule production.
15
+ * `Grammar#looks_a_sexpr?(arg)` became public.
16
+
1
17
  # 0.5.1 / 2012-03-13
2
18
 
3
19
  * Minor enhancements
data/Gemfile CHANGED
@@ -1,9 +1,9 @@
1
1
  source 'http://rubygems.org'
2
2
 
3
3
  group :development do
4
- gem "epath", "~> 0.0.1"
4
+ gem "path", "~> 1.3"
5
5
  gem "citrus", "~> 2.4"
6
- gem "rake", "~> 0.9.2"
7
- gem "rspec", "~> 2.8"
6
+ gem "rake", "~> 10.0"
7
+ gem "rspec", "~> 2.10"
8
8
  gem "wlang", "~> 0.10.2"
9
9
  end
@@ -2,17 +2,17 @@ GEM
2
2
  remote: http://rubygems.org/
3
3
  specs:
4
4
  citrus (2.4.1)
5
- diff-lcs (1.1.3)
6
- epath (0.0.1)
7
- rake (0.9.2.2)
8
- rspec (2.8.0)
9
- rspec-core (~> 2.8.0)
10
- rspec-expectations (~> 2.8.0)
11
- rspec-mocks (~> 2.8.0)
12
- rspec-core (2.8.0)
13
- rspec-expectations (2.8.0)
14
- diff-lcs (~> 1.1.2)
15
- rspec-mocks (2.8.0)
5
+ diff-lcs (1.2.4)
6
+ path (1.3.3)
7
+ rake (10.1.0)
8
+ rspec (2.14.1)
9
+ rspec-core (~> 2.14.0)
10
+ rspec-expectations (~> 2.14.0)
11
+ rspec-mocks (~> 2.14.0)
12
+ rspec-core (2.14.5)
13
+ rspec-expectations (2.14.2)
14
+ diff-lcs (>= 1.1.3, < 2.0)
15
+ rspec-mocks (2.14.3)
16
16
  wlang (0.10.2)
17
17
 
18
18
  PLATFORMS
@@ -21,7 +21,7 @@ PLATFORMS
21
21
 
22
22
  DEPENDENCIES
23
23
  citrus (~> 2.4)
24
- epath (~> 0.0.1)
25
- rake (~> 0.9.2)
26
- rspec (~> 2.8)
24
+ path (~> 1.3)
25
+ rake (~> 10.0)
26
+ rspec (~> 2.10)
27
27
  wlang (~> 0.10.2)
data/Rakefile CHANGED
@@ -1,7 +1,3 @@
1
- # Dynamically load the gem spec
2
- $gemspec_file = File.expand_path('../sexpr.gemspec', __FILE__)
3
- $gemspec = Kernel.eval(File.read($gemspec_file))
4
-
5
1
  # We run tests by default
6
2
  task :default => :test
7
3
 
@@ -56,98 +56,4 @@ module BoolExpr
56
56
 
57
57
  end # class NotPushProcessor
58
58
 
59
- end # module BoolExpr
60
-
61
- describe BoolExpr do
62
- subject{ BoolExpr }
63
-
64
- describe "the parsing feature" do
65
-
66
- it 'parses boolean expressions without error' do
67
- subject.parse("x and y").should be_a(Citrus::Match)
68
- subject.parse("not(y)").should be_a(Citrus::Match)
69
- subject.parse("not(true)").should be_a(Citrus::Match)
70
- end
71
-
72
- it 'provides a shortcut to get s-expressions directly' do
73
- subject.sexpr("x and y").should eq([:bool_and, [:var_ref, "x"], [:var_ref, "y"]])
74
- end
75
-
76
- end # parsing
77
-
78
- describe "the tagging feature" do
79
-
80
- it 'tags parsing results with the Sexpr module' do
81
- sexpr = subject.sexpr("x and y")
82
- sexpr.should be_a(Sexpr)
83
- sexpr.sexpr_type.should eq(:bool_and)
84
- sexpr.sexpr_body.should eq([[:var_ref, "x"], [:var_ref, "y"]])
85
- end
86
-
87
- it 'tags parsing results with user modules' do
88
- subject.sexpr("x and y").should be_a(BoolExpr::And)
89
- end
90
-
91
- it 'allows tagging manually' do
92
- subject.sexpr([:bool_lit, true]).should be_a(BoolExpr::Lit)
93
- end
94
-
95
- it 'applies tagging recursively' do
96
- sexpr = subject.sexpr([:bool_not, [:bool_lit, true]])
97
- sexpr.last.should be_a(BoolExpr::Lit)
98
- end
99
-
100
- end # taggging
101
-
102
- describe 'the validation feature' do
103
-
104
- it 'validates s-expressions' do
105
- subject.match?([:bool_lit, true]).should be_true
106
- subject.match?([:bool_lit, "x"]).should be_false
107
- end
108
-
109
- it 'validates s-expressions against specific rules' do
110
- subject[:bool_lit].match?([:bool_lit, true]).should be_true
111
- subject[:bool_and].match?([:bool_lit, true]).should be_false
112
- end
113
-
114
- end # validating
115
-
116
- describe BoolExpr::NotPushProcessor do
117
-
118
- def _(expr)
119
- BoolExpr.sexpr(expr)
120
- end
121
-
122
- def rw(expr)
123
- BoolExpr::NotPushProcessor.new.call(expr)
124
- end
125
-
126
- it 'does nothing on variable references' do
127
- rw("not x").should eq([:bool_not, [:var_ref, "x"]])
128
- end
129
-
130
- it 'rewrites literals through negating them' do
131
- rw("not true").should eq(_ "false")
132
- rw("not false").should eq(_ "true")
133
- end
134
-
135
- it 'rewrites not through removing them' do
136
- rw("not not true").should eq(_ "true")
137
- end
138
-
139
- it 'rewrites or through and of negated terms' do
140
- rw("not(x or y)").should eq(_ "not(x) and not(y)")
141
- end
142
-
143
- it 'rewrites and through or of negated terms' do
144
- rw("not(x and y)").should eq(_ "not(x) or not(y)")
145
- end
146
-
147
- it 'rewrites recursively' do
148
- rw("not(x and not(y))").should eq(_ "not(x) or y")
149
- end
150
-
151
- end # rewriting
152
-
153
- end if defined?(RSpec)
59
+ end # module BoolExpr
@@ -15,7 +15,7 @@ module Sexpr
15
15
  end
16
16
  alias :=== :match?
17
17
 
18
- private
18
+ private
19
19
 
20
20
  def compile_rules(rules)
21
21
  Hash[rules.map{|k,v|
@@ -26,10 +26,10 @@ module Sexpr
26
26
  def compile_rule(name, defn)
27
27
  case rule = compile_rule_defn(defn)
28
28
  when Matcher::Terminal, Matcher::Alternative
29
- rule
30
29
  else
31
- Matcher::Rule.new(name, rule)
30
+ rule = Matcher::NonTerminal.new(name, rule)
32
31
  end
32
+ Matcher::Rule.new(name, rule)
33
33
  end
34
34
 
35
35
  def compile_rule_defn(arg, grammar = self)
@@ -11,7 +11,7 @@ module Sexpr
11
11
  parser!.parse(input, options)
12
12
  end
13
13
 
14
- private
14
+ private
15
15
 
16
16
  def parser!
17
17
  unless p = parser
@@ -19,6 +19,15 @@ module Sexpr
19
19
  nil
20
20
  end
21
21
 
22
+ def tagging_module_for(rulename)
23
+ if ref = tagging_reference
24
+ modname = rule2modname(rulename)
25
+ ref.const_get(modname, false) rescue default_tagging_module
26
+ else
27
+ default_tagging_module
28
+ end
29
+ end
30
+
22
31
  def sexpr(input, markers = nil)
23
32
  case input
24
33
  when Array
@@ -29,7 +38,11 @@ module Sexpr
29
38
  end
30
39
  end
31
40
 
32
- private
41
+ def looks_a_sexpr?(arg)
42
+ arg.is_a?(Array) and arg.first.is_a?(Symbol)
43
+ end
44
+
45
+ private
33
46
 
34
47
  def tag_sexpr(sexpr, markers = nil, force = false)
35
48
  return sexpr unless looks_a_sexpr?(sexpr)
@@ -53,21 +66,11 @@ module Sexpr
53
66
  end
54
67
 
55
68
  def tag_sexpr_with_user_module(sexpr)
56
- if ref = tagging_reference
57
- rulename = sexpr.first
58
- modname = rule2modname(rulename)
59
- tag = ref.const_get(modname, false) rescue default_tagging_module
60
- sexpr.extend(tag) if tag
61
- elsif tag = default_tagging_module
62
- sexpr.extend(tag)
63
- end
69
+ tag = tagging_module_for(sexpr.first)
70
+ sexpr.extend(tag) if tag
64
71
  sexpr
65
72
  end
66
73
 
67
- def looks_a_sexpr?(arg)
68
- arg.is_a?(Array) and arg.first.is_a?(Symbol)
69
- end
70
-
71
74
  def parser!
72
75
  raise NoParserError, "No parser set.", caller
73
76
  end
@@ -0,0 +1 @@
1
+
@@ -10,6 +10,7 @@ end # module Sexpr
10
10
  require_relative "matcher/alternative"
11
11
  require_relative "matcher/many"
12
12
  require_relative "matcher/reference"
13
- require_relative "matcher/rule"
14
13
  require_relative "matcher/sequence"
15
- require_relative "matcher/terminal"
14
+ require_relative "matcher/terminal"
15
+ require_relative "matcher/non_terminal"
16
+ require_relative "matcher/rule"
@@ -28,11 +28,26 @@ module Sexpr
28
28
  i >= @min ? last : nil
29
29
  end
30
30
 
31
+ def parse(sexp, to = [])
32
+ i, last, got = 0, sexp, []
33
+ while sexp && (@max.nil? || i < @max)
34
+ if res = @term.parse(sexp, got)
35
+ last = res
36
+ i += 1
37
+ end
38
+ sexp = res
39
+ end
40
+ if i >= @min
41
+ got.each{|x| to << x }
42
+ last
43
+ end
44
+ end
45
+
31
46
  def inspect
32
47
  "(many #{term.inspect}, #{min}, #{max})"
33
48
  end
34
49
 
35
- private
50
+ private
36
51
 
37
52
  def minmax(min, max)
38
53
  case min
@@ -0,0 +1,31 @@
1
+ module Sexpr
2
+ module Matcher
3
+ class NonTerminal
4
+ include Matcher
5
+
6
+ attr_reader :name
7
+ attr_reader :defn
8
+
9
+ def initialize(name, defn)
10
+ @name = name
11
+ @defn = defn
12
+ end
13
+
14
+ def match?(sexp)
15
+ return nil unless sexp.is_a?(Array)
16
+ return false unless sexp.first && (sexp.first == name)
17
+ defn.match?(sexp[1..-1])
18
+ end
19
+
20
+ def eat(sexp)
21
+ return nil unless match?(sexp.first)
22
+ sexp[1..-1]
23
+ end
24
+
25
+ def inspect
26
+ "(non-terminal #{name}, #{defn.inspect})"
27
+ end
28
+
29
+ end # class NonTerminal
30
+ end # module Matcher
31
+ end # module Sexpr
@@ -12,14 +12,11 @@ module Sexpr
12
12
  end
13
13
 
14
14
  def match?(sexp)
15
- return nil unless sexp.is_a?(Array)
16
- return false unless sexp.first && (sexp.first == name)
17
- defn.match?(sexp[1..-1])
15
+ defn.match?(sexp)
18
16
  end
19
17
 
20
18
  def eat(sexp)
21
- return nil unless match?(sexp.first)
22
- sexp[1..-1]
19
+ defn.eat(sexp)
23
20
  end
24
21
 
25
22
  def inspect
@@ -28,4 +25,4 @@ module Sexpr
28
25
 
29
26
  end # class Rule
30
27
  end # module Matcher
31
- end # module Sexpr
28
+ end # module Sexpr
@@ -31,7 +31,7 @@ module Sexpr
31
31
  end
32
32
  alias :dup :sexpr_copy
33
33
 
34
- private
34
+ private
35
35
 
36
36
  def sexpr_copy_tagging(copy)
37
37
  (class << self; self; end).included_modules.each do |mod|
@@ -28,7 +28,7 @@ module Sexpr
28
28
 
29
29
  end # class methods
30
30
 
31
- private
31
+ private
32
32
 
33
33
  def input_text(input)
34
34
  case input
@@ -6,6 +6,12 @@ module Sexpr
6
6
 
7
7
  class << self
8
8
 
9
+ def grammar(grammar = nil)
10
+ @grammar = grammar if grammar
11
+ @grammar ||= (@grammar || superclass.grammar) rescue Sexpr
12
+ @grammar
13
+ end
14
+
9
15
  def preprocessors
10
16
  @preprocessors ||= superclass.preprocessors.dup rescue [ ]
11
17
  end
@@ -46,6 +52,10 @@ module Sexpr
46
52
  @options = options || {}
47
53
  end
48
54
 
55
+ def grammar
56
+ self.class.grammar
57
+ end
58
+
49
59
  def call(sexpr)
50
60
  apply(preprocess(sexpr))
51
61
  end
@@ -62,7 +72,7 @@ module Sexpr
62
72
  raise UnexpectedSexprError, "Unexpected sexpr: #{sexpr.inspect}"
63
73
  end
64
74
 
65
- private
75
+ private
66
76
 
67
77
  def preprocess(sexpr)
68
78
  preprocessors = self.class.preprocessors
@@ -16,7 +16,7 @@ module Sexpr
16
16
  yield(processor, sexpr)
17
17
  end
18
18
 
19
- private
19
+ private
20
20
 
21
21
  def next_call(processor, sexpr, toplevel)
22
22
  if nic = next_in_chain
@@ -5,40 +5,32 @@ module Sexpr
5
5
  module Methods
6
6
 
7
7
  def parse(*args)
8
- sexpr_grammar.parse(*args)
8
+ grammar.parse(*args)
9
9
  end
10
10
 
11
11
  def sexpr(*args)
12
- sexpr_grammar.sexpr(*args)
12
+ grammar.sexpr(*args)
13
13
  end
14
14
 
15
15
  end
16
16
 
17
17
  def call(processor, sexpr, &bl)
18
+ g = processor.class.grammar
19
+
18
20
  # input coercion
19
- sexpr = grammar(processor).sexpr(sexpr)
21
+ sexpr = g.sexpr(sexpr)
20
22
 
21
23
  # recursive call
22
24
  sexpr = next_call(processor, sexpr, bl)
23
25
 
24
26
  # output coercion
25
27
  if sexpr.is_a?(Array) and sexpr.first.is_a?(Symbol)
26
- grammar(processor).sexpr(sexpr)
28
+ g.sexpr(sexpr)
27
29
  else
28
30
  sexpr
29
31
  end
30
32
  end
31
33
 
32
- private
33
-
34
- def grammar(processor)
35
- if processor.respond_to?(:sexpr_grammar)
36
- processor.sexpr_grammar
37
- else
38
- Sexpr
39
- end
40
- end
41
-
42
34
  end # class SexprCoercions
43
35
  end # class Processor
44
36
  end # module Sexpr