babel_bridge 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,62 @@
1
+ module BabelBridge
2
+ # Rules define one or more patterns (RuleVariants) to match for a given non-terminal
3
+ class Rule
4
+ attr_accessor :name, :variants, :parser, :node_class
5
+
6
+ private
7
+ # creates a subclass of the NonTerminalNode for this Rule's node_class
8
+ def create_node_class
9
+ class_name = "#{parser.module_name}_#{name}_node".camelize
10
+ parser.const_set class_name, Class.new(NonTerminalNode)
11
+ end
12
+
13
+ # creates a new sub_class of the node_class for a variant
14
+ def create_next_node_variant_class
15
+ rule_variant_class_name = "#{name}_node#{self.variants.length+1}".camelize
16
+ parser.const_set rule_variant_class_name, Class.new(node_class)
17
+ end
18
+
19
+ public
20
+ def initialize(name,parser)
21
+ @name = name
22
+ @variants = []
23
+ @parser = parser
24
+ @node_class = create_node_class
25
+ end
26
+
27
+ def add_variant(pattern, &block)
28
+ rule_variant_class = create_next_node_variant_class
29
+ variants << RuleVariant.new(pattern, self, rule_variant_class)
30
+ rule_variant_class.class_eval &block if block
31
+ rule_variant_class
32
+ end
33
+
34
+ def parse(node)
35
+ if cached = node.parser.cached(name,node.next)
36
+ return cached == :no_match ? nil : cached # return nil if cached==:no_matched
37
+ end
38
+
39
+ variants.each do |v|
40
+ if match = v.parse(node)
41
+ node.parser.cache_match(name,match)
42
+ return match
43
+ end
44
+ end
45
+ node.parser.cache_no_match(name,node.next)
46
+ nil
47
+ end
48
+
49
+ # inspect returns a string which approximates the syntax for generating the rule and all its variants
50
+ def inspect
51
+ variants.collect do |v|
52
+ "rule #{name.inspect}, #{v.inspect}"
53
+ end.join("\n")
54
+ end
55
+
56
+ # returns a more human-readable explanation of the rule
57
+ def to_s
58
+ "rule #{name.inspect}, node_class: #{node_class}\n\t"+
59
+ "#{variants.collect {|v|v.to_s}.join("\n\t")}"
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,45 @@
1
+ module BabelBridge
2
+
3
+ # Each Rule has one or more RuleVariant
4
+ # Rules attempt to match each of their Variants in order. The first one to succeed returns true and the Rule succeeds.
5
+ class RuleVariant
6
+ attr_accessor :pattern, :rule, :variant_node_class
7
+
8
+ def initialize(pattern, rule, variant_node_class=nil)
9
+ @pattern = pattern
10
+ @rule = rule
11
+ @variant_node_class = variant_node_class
12
+ end
13
+
14
+ # convert the pattern into a set of lamba functions
15
+ def pattern_elements
16
+ @pattern_elements||=pattern.collect { |match| PatternElement.new match, self }
17
+ end
18
+
19
+ # returns a Node object if it matches, nil otherwise
20
+ def parse(parent_node)
21
+ #return parse_nongreedy_optional(src,offset,parent_node) # nongreedy optionals break standard PEG
22
+ node = variant_node_class.new(parent_node)
23
+
24
+ pattern_elements.each do |pe|
25
+ match=pe.parse(node)
26
+
27
+ # if parse failed
28
+ if !match
29
+ if pe.terminal
30
+ # log failures on Terminal patterns for debug output if overall parse fails
31
+ node.parser.log_parsing_failure(node.next,:pattern=>pe.match,:node=>node)
32
+ end
33
+ return nil
34
+ end
35
+
36
+ # parse succeeded, add to node and continue
37
+ node.add_match(match,pe.name)
38
+ end
39
+ node.post_match
40
+ end
41
+
42
+ def inspect; pattern.collect {|a| a.inspect}.join(', '); end
43
+ def to_s; "variant_class: #{variant_node_class}, pattern: #{inspect}"; end
44
+ end
45
+ end
@@ -0,0 +1,36 @@
1
+ require "readline"
2
+
3
+ module BabelBridge
4
+ class Shell
5
+ attr_accessor :parser
6
+ def initialize(parser)
7
+ @parser = parser
8
+ end
9
+
10
+ def evaluate(parse_tree_node)
11
+ parse_tree_node.evaluate
12
+ rescue Exception => e
13
+ @stderr.puts "Error evaluating parse tree: #{e}\n "+e.backtrace.join("\n ")
14
+ end
15
+
16
+ # if block is provided, successful parsers are yield to block
17
+ # Otherwise, succuessful parsers are sent the "eval" method
18
+ def start(options={},&block)
19
+ @stdout = options[:stdout] || $stdout
20
+ @stderr = options[:stdout] || @stdout
21
+ @stdin = options[:stdin] || $stdin
22
+ while line = @stdin == $stdin ? Readline.readline("> ", true) : @stdin.gets
23
+ ret = parser.parse line.strip
24
+ if ret
25
+ if block
26
+ yield ret
27
+ else
28
+ @stdout.puts " => #{evaluate(ret).inspect}"
29
+ end
30
+ else
31
+ @stderr.puts parser.parser_failure_info
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,26 @@
1
+ module BabelBridge
2
+ module StringExtensions
3
+ def camelize
4
+ self.split("_").collect {|a| a.capitalize}.join
5
+ end
6
+
7
+ def first_lines(n)
8
+ lines=self.split("\n",-1)
9
+ lines.length<=n ? self : lines[0..n-1].join("\n")
10
+ end
11
+
12
+ def last_lines(n)
13
+ lines=self.split("\n",-1)
14
+ lines.length<=n ? self : lines[-n..-1].join("\n")
15
+ end
16
+
17
+ def line_col(offset)
18
+ lines=self[0..offset-1].split("\n")
19
+ return lines.length, lines[-1].length
20
+ end
21
+ end
22
+ end
23
+
24
+ class String
25
+ include BabelBridge::StringExtensions
26
+ end
@@ -0,0 +1,90 @@
1
+ module BabelBridge
2
+ class Tools
3
+ class << self
4
+
5
+ # Takes an array of Strings and Regexp and generates a new Regexp
6
+ # that matches the or ("|") of all strings and Regexp
7
+ def array_to_or_regexp_string(array)
8
+ new_re=array.flatten.collect do |op|
9
+ "("+case op
10
+ when Regexp then op.source
11
+ when String, Symbol then Regexp.escape(op.to_s)
12
+ end+")"
13
+ end.sort{|a|a.length}.join('|')
14
+ end
15
+
16
+ def array_to_anchored_or_regexp(array)
17
+ Regexp.new "^"+array_to_or_regexp_string(array)+"$"
18
+ end
19
+
20
+ def array_to_or_regexp(array)
21
+ Regexp.new array_to_or_regexp_string(array)
22
+ end
23
+ end
24
+ end
25
+
26
+ class BinaryOperatorProcessor
27
+ attr_accessor :node_class, :exact_operator_precedence, :regexp_operator_precedence, :right_operators
28
+ def initialize(operator_precedence,node_class,right_operators)
29
+ @right_operators_regexp= right_operators && Tools::array_to_anchored_or_regexp(right_operators)
30
+ @node_class=node_class
31
+ @exact_operator_precedence={}
32
+ @regexp_operator_precedence=[]
33
+
34
+ operator_precedence.each_with_index do |op_level,i|
35
+ (op_level.kind_of?(Array) ? op_level : [op_level]).each do |op|
36
+ case op
37
+ when String, Symbol then @exact_operator_precedence[op.to_s] = i
38
+ when Regexp then @regexp_operator_precedence << [op,i]
39
+ end
40
+ end
41
+ end
42
+ end
43
+
44
+ def operator_precedence(operator_string)
45
+ p = @exact_operator_precedence[operator_string]
46
+ return p if p
47
+ @regexp_operator_precedence.each do |regexp,p|
48
+ return p if operator_string[regexp]
49
+ end
50
+ raise "operator #{operator_string.inspect} didn't match #{@exact_operator_precedence} or #{@regexp_operator_precedence}"
51
+ end
52
+
53
+ # associativity =
54
+ # :left => operators of the same precidence execut from left to right
55
+ # :right => operators of the same precidence execut from right to left
56
+ def index_of_lowest_precedence(operators,associativity=:left)
57
+ lowest = lowest_precedence = nil
58
+ operators.each_with_index do |operator,i|
59
+ operator_string = operator.to_s
60
+ precedence = operator_precedence(operator_string)
61
+ right_associative = @right_operators_regexp && operator_string[@right_operators_regexp]
62
+ if !lowest || (right_associative ? precedence < lowest_precedence : precedence <= lowest_precedence)
63
+ lowest = i
64
+ lowest_precedence = precedence
65
+ end
66
+ end
67
+ lowest
68
+ end
69
+
70
+ # generates a tree of nodes of the specified node_class
71
+ # The nodes have access to the following useful methods:
72
+ # self.left -> return the left operand parse-tree-node
73
+ # self.right -> return the right operand parse-tree-node
74
+ # self.operator_node -> return the operator parse-tree-node
75
+ # self.operator -> return the operator as a ruby symbol
76
+ def generate_tree(operands, operators, parent_node)
77
+ return operands[0] if operands.length==1
78
+
79
+ i = index_of_lowest_precedence(operators)
80
+
81
+ operator = operators[i]
82
+ new_operand = node_class.new(parent_node)
83
+ new_operand.add_match generate_tree(operands[0..i], operators[0..i-1],new_operand), :left
84
+ new_operand.add_match operators[i], :operator_node
85
+ new_operand.add_match generate_tree(operands[i+1..-1], operators[i+1..-1],new_operand), :right
86
+ new_operand
87
+ end
88
+
89
+ end
90
+ end
@@ -0,0 +1,3 @@
1
+ module BabelBridge
2
+ VERSION = "0.3.0"
3
+ end
@@ -1,5 +1,5 @@
1
- require File.dirname(__FILE__) + "/../lib/babel_bridge"
2
- require File.dirname(__FILE__) + "/test_helper"
1
+ require File.expand_path(File.join(File.dirname(__FILE__),"..","lib","babel_bridge"))
2
+ require File.expand_path(File.join(File.dirname(__FILE__),"test_helper"))
3
3
 
4
4
  class BBTests < TestHelper
5
5
 
@@ -287,7 +287,9 @@ class BBTests < TestHelper
287
287
  rule :foo, {:parser=>lambda do |parent_node|
288
288
  offset=parent_node.next
289
289
  src=parent_node.src
290
- if src.index(/[A-Z]+/,offset)==offset
290
+
291
+ # Note, the \A anchors the search at the beginning of the string
292
+ if src[offset..-1].index(/\A[A-Z]+/)==0
291
293
  endpattern=$~.to_s
292
294
  if i=src.index(endpattern,offset+endpattern.length)
293
295
  BabelBridge::TerminalNode.new(parent_node,i+endpattern.length-offset,"endpattern")
@@ -299,6 +301,7 @@ class BBTests < TestHelper
299
301
  assert parser.parse("END this is in the middle END")
300
302
  assert_equal "END this is in END",parser.parse("END this is in END the middle END",0,:foo).text
301
303
  assert_nil parser.parse("END this is in the middle EN")
304
+ assert_nil parser.parse(" END this is in the middle END")
302
305
  end
303
306
 
304
307
  def test_poly
@@ -378,6 +381,38 @@ class BBTests < TestHelper
378
381
  assert_equal({:match=>";",:could=>true}, BabelBridge::Parser.could.match(";"))
379
382
  end
380
383
 
384
+ def test_ignore_whitespace
385
+ parser=new_parser do
386
+ ignore_whitespace
387
+ rule :pair, "foo", "bar"
388
+ end
389
+ assert parser.parse("foobar")
390
+ assert parser.parse("foo bar")
391
+ assert parser.parse("foobar ")
392
+ assert parser.parse("foo bar ")
393
+ end
394
+
395
+ def test_binary_operator_rule
396
+ parser=new_parser do
397
+ binary_operators_rule :bin_op, :int, [[:+, "-"], [:/, :*], "**"], :right_operators => ["**"] do
398
+ def evaluate
399
+ "(#{left.evaluate}#{operator}#{right.evaluate})"
400
+ end
401
+ end
402
+
403
+ rule :int, /[-]?[0-9]+/ do
404
+ def evaluate; to_s; end
405
+ end
406
+ end
407
+ assert_equal "(1+2)", parser.parse("1+2").evaluate
408
+ assert_equal "((1+2)+3)", parser.parse("1+2+3").evaluate
409
+ assert_equal "(1+(2*3))", parser.parse("1+2*3").evaluate
410
+ assert_equal "((1*2)+3)", parser.parse("1*2+3").evaluate
411
+ assert_equal "(5**6)", parser.parse("5**6").evaluate
412
+ assert_equal "((1-2)+((3*4)/(5**6)))", parser.parse("1-2+3*4/5**6").evaluate
413
+ assert_equal "(5**(6**7))", parser.parse("5**6**7").evaluate
414
+ end
415
+
381
416
  def disabled_test_recursive_block
382
417
  # PEG does have this problem, so this isn't really an error
383
418
  # But maybe in the future we'll handle it better.
@@ -391,6 +426,7 @@ class BBTests < TestHelper
391
426
  end
392
427
 
393
428
 
429
+
394
430
  def regex_performance
395
431
  parser=new_parser do
396
432
  rule :foo, many(:element)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: babel_bridge
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2010-11-28 00:00:00.000000000Z
12
+ date: 2010-11-28 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description: ! "Babel Bridge is an object oriented parser generator for parsing expression
15
15
  grammars (PEG). \nGenerate memoizing packrat parsers 100% in Ruby code with a simple
@@ -24,8 +24,24 @@ files:
24
24
  - test/test_bb.rb
25
25
  - test/test_helper.rb
26
26
  - lib/babel_bridge.rb
27
+ - lib/nodes/empty_node.rb
28
+ - lib/nodes/many_node.rb
29
+ - lib/nodes/node.rb
30
+ - lib/nodes/non_terminal_node.rb
31
+ - lib/nodes/terminal_node.rb
27
32
  - lib/nodes.rb
33
+ - lib/parser.rb
28
34
  - lib/pattern_element.rb
35
+ - lib/rule.rb
36
+ - lib/rule_variant.rb
37
+ - lib/shell.rb
38
+ - lib/string.rb
39
+ - lib/tools.rb
40
+ - lib/version.rb
41
+ - examples/indention_grouping.rb
42
+ - examples/indention_grouping_test.txt
43
+ - examples/turing/test.rb
44
+ - examples/turing/turing.rb
29
45
  homepage: http://babel-bridge.rubyforge.org
30
46
  licenses: []
31
47
  post_install_message:
@@ -46,7 +62,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
46
62
  version: '0'
47
63
  requirements: []
48
64
  rubyforge_project: babel-bridge
49
- rubygems_version: 1.8.10
65
+ rubygems_version: 1.8.24
50
66
  signing_key:
51
67
  specification_version: 3
52
68
  summary: A Ruby-based parser-generator based on Parsing Expression Grammars.