rubypeg 0.0.2

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/README ADDED
@@ -0,0 +1,67 @@
1
+ = RubyPeg
2
+
3
+ RubyPeg helps you to create readable Parsing Expression Grammars (PEG) in ruby.
4
+
5
+ This software is (c) 2010 Green on Black Ltd and distributed under the open source MIT[http://www.opensource.org/licenses/mit-license.php] licence. (See LICENCE for the wording).
6
+
7
+ If you like this code, employ us: http://www.greenonblack.com
8
+
9
+ = An example
10
+
11
+ class Arithmetic < RubyPeg
12
+
13
+ def root
14
+ arithmetic
15
+ end
16
+
17
+ def arithmetic
18
+ node :arithmetic do
19
+ one_or_more { expression } && ignore { terminal(/\z/) }
20
+ end
21
+ end
22
+
23
+ def expression
24
+ subtraction || addition || division || multiplication || brackets || number
25
+ end
26
+
27
+ def brackets
28
+ node :brackets do
29
+ ignore { terminal("(") } && spacing && expression && spacing && ignore { terminal(")") }
30
+ end
31
+ end
32
+
33
+ def multiplication
34
+ node :multiplication do
35
+ (brackets || number) && spacing && ignore { terminal("*") } && spacing && expression
36
+ end
37
+ end
38
+
39
+ def division
40
+ node :division do
41
+ (brackets || number) && spacing && ignore { terminal("/") } && spacing && expression
42
+ end
43
+ end
44
+
45
+ def addition
46
+ node :addition do
47
+ (brackets || number) && spacing && ignore { terminal("+") } && spacing && expression
48
+ end
49
+ end
50
+
51
+ def subtraction
52
+ node :subtraction do
53
+ (brackets || number) && spacing && ignore { terminal("-") } && spacing && expression
54
+ end
55
+ end
56
+
57
+ def number
58
+ terminal(/[-+]?[0-9]+\.?[0-9]*([eE][-+]?[0-9]+)?/)
59
+ end
60
+
61
+ def spacing
62
+ ignore { terminal(/[ \t]*/) }
63
+ end
64
+
65
+ end
66
+
67
+ Arithmetic.parse("(1+1)+2").to_ast # [:arithmetic,[:addition,[:brackets,[:addition,'1','1']],'2']]
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env ruby
2
+ require 'optparse'
3
+
4
+ $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/../lib"))
5
+ require 'textpeg2rubypeg'
6
+ require 'textpeg'
7
+ require 'rubypeg'
8
+
9
+ if ARGV[0]
10
+ input = IO.readlines(ARGV[0]).join
11
+ ruby = TextPeg2RubyPeg.new
12
+ peg = TextPeg.parse(input)
13
+ peg.visit(ruby)
14
+ if ARGV[1]
15
+ File.open(ARGV[1],'w') { |f| f.puts ruby.to_ruby }
16
+ else
17
+ $stdout.puts ruby.to_ruby
18
+ end
19
+ else
20
+ $stdout.puts "Usage: text-peg2ruby-peg [text-peg-input-filename] [ruby-peg-output-filename]"
21
+ $stdout.puts "Help: Compiles text format parsing expression grammars into a ruby parser."
22
+ $stdout.puts " See http://github.com/tamc/ruby-peg for more information."
23
+ $stdout.puts "Version: 0.0.1"
24
+ end
@@ -0,0 +1,57 @@
1
+ require 'rubypeg'
2
+
3
+ class Arithmetic < RubyPeg
4
+
5
+ def root
6
+ arithmetic
7
+ end
8
+
9
+ def arithmetic
10
+ node :arithmetic do
11
+ one_or_more { expression } && ignore { terminal(/\z/) }
12
+ end
13
+ end
14
+
15
+ def expression
16
+ subtraction || addition || division || multiplication || brackets || number
17
+ end
18
+
19
+ def brackets
20
+ node :brackets do
21
+ ignore { terminal("(") } && spacing && expression && spacing && ignore { terminal(")") }
22
+ end
23
+ end
24
+
25
+ def multiplication
26
+ node :multiplication do
27
+ (brackets || number) && spacing && ignore { terminal("*") } && spacing && expression
28
+ end
29
+ end
30
+
31
+ def division
32
+ node :division do
33
+ (brackets || number) && spacing && ignore { terminal("/") } && spacing && expression
34
+ end
35
+ end
36
+
37
+ def addition
38
+ node :addition do
39
+ (brackets || number) && spacing && ignore { terminal("+") } && spacing && expression
40
+ end
41
+ end
42
+
43
+ def subtraction
44
+ node :subtraction do
45
+ (brackets || number) && spacing && ignore { terminal("-") } && spacing && expression
46
+ end
47
+ end
48
+
49
+ def number
50
+ terminal(/[-+]?[0-9]+\.?[0-9]*([eE][-+]?[0-9]+)?/)
51
+ end
52
+
53
+ def spacing
54
+ ignore { terminal(/[ \t]*/) }
55
+ end
56
+
57
+ end
@@ -0,0 +1,9 @@
1
+ arithmetic := expression+ `/\z/
2
+ expression = subtraction | addition | division | multiplication | brackets | number
3
+ brackets := `"(" spacing expression spacing `")"
4
+ multiplication := (brackets | number ) spacing `'*' spacing expression
5
+ division := (brackets | number ) spacing `'/' spacing expression
6
+ addition := (brackets | number ) spacing `'+' spacing expression
7
+ subtraction := (brackets | number ) spacing `'-' spacing expression
8
+ number = /[-+]?[0-9]+\.?[0-9]*([eE][-+]?[0-9]+)?/
9
+ spacing = `/[ \t]*/
@@ -0,0 +1,49 @@
1
+ $:.unshift File.join(File.dirname(__FILE__), *%w[.])
2
+ $:.unshift File.join(File.dirname(__FILE__), *%w[.. .. lib])
3
+ require 'rubypeg'
4
+ require 'arithmetic_peg'
5
+
6
+ describe Arithmetic do
7
+
8
+ def check(text)
9
+ puts
10
+ e = Arithmetic.new
11
+ e.parse(text)
12
+ e.pretty_print_cache
13
+ puts
14
+ end
15
+
16
+ it "parses decimal numbers, including those in scientific notation" do
17
+ Arithmetic.parse("1").to_ast.should == [:arithmetic,'1']
18
+ Arithmetic.parse("103.287").to_ast.should == [:arithmetic,'103.287']
19
+ Arithmetic.parse("-1.0E-27").to_ast.should == [:arithmetic,'-1.0E-27']
20
+ end
21
+
22
+ it "parses sums of decimal numbers" do
23
+ Arithmetic.parse("1+1").to_ast.should == [:arithmetic,[:addition,'1','1']]
24
+ end
25
+
26
+ it "parses series of sums" do
27
+ Arithmetic.parse("1+1+2").to_ast.should == [:arithmetic,[:addition,'1',[:addition,'1','2']]]
28
+ end
29
+
30
+ it "parses a series of sums with brackets" do
31
+ Arithmetic.parse("(1+1)+2").to_ast.should == [:arithmetic,[:addition,[:brackets,[:addition,'1','1']],'2']]
32
+ Arithmetic.parse("1+(1+2)").to_ast.should == [:arithmetic,[:addition,'1',[:brackets,[:addition,'1','2']]]]
33
+ Arithmetic.parse("(1+1)+(1+2)").to_ast.should == [:arithmetic,[:addition,[:brackets,[:addition,'1','1']],[:brackets,[:addition,'1','2']]]]
34
+ Arithmetic.parse("((1+1)+(1+2))").to_ast.should == [:arithmetic,[:brackets,[:addition,[:brackets,[:addition,'1','1']],[:brackets,[:addition,'1','2']]]]]
35
+ end
36
+
37
+ it "parses subtractions" do
38
+ Arithmetic.parse("1-1").to_ast.should == [:arithmetic,[:subtraction,'1','1']]
39
+ end
40
+
41
+ it "parses multiplication" do
42
+ Arithmetic.parse("1*1").to_ast.should == [:arithmetic,[:multiplication,'1','1']]
43
+ end
44
+
45
+ it "parses division" do
46
+ Arithmetic.parse("1/1").to_ast.should == [:arithmetic,[:division,'1','1']]
47
+ end
48
+
49
+ end
@@ -0,0 +1,40 @@
1
+ $:.unshift File.join(File.dirname(__FILE__), *%w[.. .. lib])
2
+ $:.unshift File.join(File.dirname(__FILE__), *%w[.])
3
+ require 'rubypeg'
4
+ require 'arithmetic_peg'
5
+
6
+ module TerminalNode
7
+ def build(builder)
8
+ self.to_f
9
+ end
10
+ end
11
+
12
+ class CalculationEngine
13
+
14
+ def addition(left,right)
15
+ left.build(self) + right.build(self)
16
+ end
17
+
18
+ def subtraction(left,right)
19
+ left.build(self) - right.build(self)
20
+ end
21
+
22
+ def multiplication(left,right)
23
+ left.build(self) * right.build(self)
24
+ end
25
+
26
+ def division(left,right)
27
+ left.build(self) / right.build(self)
28
+ end
29
+
30
+ end
31
+
32
+ class Calculator
33
+
34
+ def self.calculate(sum)
35
+ ast = Arithmetic.parse(sum)
36
+ answer = ast.build(CalculationEngine.new)
37
+ answer
38
+ end
39
+
40
+ end
@@ -0,0 +1,26 @@
1
+ $:.unshift File.join(File.dirname(__FILE__), *%w[.])
2
+ require 'calculator'
3
+
4
+ describe Calculator do
5
+
6
+ it "should know that 1 + 1 = 2.0" do
7
+ Calculator.calculate("1+1").should == 2.0
8
+ end
9
+
10
+ it "should know that 5.0 - 1.3 = 3.7" do
11
+ Calculator.calculate("5.0 - 1.3").should == 3.7
12
+ end
13
+
14
+ it "should know that -12.3e-27 * 5 = 6.15e-26" do
15
+ Calculator.calculate("-12.3e-27*5").should == -6.15e-26
16
+ end
17
+
18
+ it "should know that 1000/10 = 100" do
19
+ Calculator.calculate("1000/10").should == 100
20
+ end
21
+
22
+ it "should know that (1+3)*(8*2) = 64" do
23
+ Calculator.calculate("(1+3)*(8*2)").should == 64
24
+ end
25
+
26
+ end
@@ -0,0 +1,35 @@
1
+ formula := expression+
2
+ expression = string_join | arithmetic | comparison | thing
3
+ thing = function | brackets | string | any_reference | percentage | number | boolean | named_reference | prefix
4
+ function := /[A-Z]+/ `'(' expression? (`',' expression)* `')'
5
+ brackets := `'(' expression `')'
6
+ string_join := thing (`"&" thing)+
7
+ arithmetic := thing (operator thing)+
8
+ comparison := thing comparator thing
9
+ comparator := '>=' | '<=' | '<>' | '>' | '<' | '='
10
+ string := `'"' /[^"]*/ `'"'
11
+ any_reference = table_reference | quoted_sheet_reference | sheet_reference | sheetless_reference
12
+ percentage := /[-+]?[0-9]+\.?[0-9]*/ `'%'
13
+ number := /[-+]?[0-9]+\.?[0-9]*([eE][-+]?[0-9]+)?/
14
+ operator := '+' | '-' | '/' | '*' | '^'
15
+ table_reference = qualified_table_reference | local_table_reference
16
+ qualified_table_reference := /[a-zA-Z0-9]+/ table_column
17
+ local_table_reference := table_column
18
+ table_column = `'[' /[^\u005d]*/ `']'
19
+ named_reference := /[a-zA-Z][\w_.]+/
20
+ quoted_sheet_reference := single_quoted_string `'!' sheetless_reference
21
+ sheet_reference := /[a-zA-Z][\w_]+/ `'!' sheetless_reference
22
+ single_quoted_string = `"'" /[^']*/ `"'"
23
+ sheetless_reference = column_range | row_range | area | cell
24
+ column_range := column `':' column
25
+ row_range := row `':' row
26
+ area := reference `':' reference
27
+ cell := reference
28
+ row = /\$?\d+/
29
+ column = /\$?[A-Z]+/
30
+ reference = /\$?[A-Z]+\$?[0-9]+/
31
+ boolean = boolean_true | boolean_false
32
+ boolean_true := `'TRUE'
33
+ boolean_false := `'FALSE'
34
+ prefix := /[-+]/
35
+
@@ -0,0 +1,139 @@
1
+ $:.unshift File.join(File.dirname(__FILE__), *%w[.])
2
+ $:.unshift File.join(File.dirname(__FILE__), *%w[.. .. lib])
3
+ require 'textpeg2rubypeg'
4
+ text_peg = IO.readlines(File.join(File.dirname(__FILE__),'excel_peg.txt')).join
5
+ ast = TextPeg.parse(text_peg)
6
+ # puts ast.to_ast
7
+ # exit
8
+ builder = TextPeg2RubyPeg.new
9
+ ruby = ast.build(builder)
10
+ Kernel.eval(ruby)
11
+
12
+ describe Formula do
13
+
14
+ def check(text)
15
+ puts
16
+ e = Formula.new
17
+ e.parse(text)
18
+ e.pretty_print_cache
19
+ puts
20
+ end
21
+
22
+ it "returns formulas" do
23
+ Formula.parse('1+1').to_ast.first.should == :formula
24
+ end
25
+
26
+ it "returns cells" do
27
+ Formula.parse('$A$1').to_ast.should == [:formula,[:cell,'$A$1']]
28
+ Formula.parse('A1').to_ast.should == [:formula,[:cell,'A1']]
29
+ Formula.parse('$A1').to_ast.should == [:formula,[:cell,'$A1']]
30
+ Formula.parse('A$1').to_ast.should == [:formula,[:cell,'A$1']]
31
+ Formula.parse('AAA1123').to_ast.should == [:formula,[:cell,'AAA1123']]
32
+ end
33
+
34
+ it "returns areas" do
35
+ Formula.parse('$A$1:$Z$1').to_ast.should == [:formula,[:area,'$A$1','$Z$1']]
36
+ Formula.parse('A1:$Z$1').to_ast.should == [:formula,[:area,'A1','$Z$1']]
37
+ end
38
+
39
+ it "returns row ranges" do
40
+ Formula.parse('$1:$1000').to_ast.should == [:formula,[:row_range,'$1','$1000']]
41
+ Formula.parse('1000:1').to_ast.should == [:formula,[:row_range,'1000','1']]
42
+ end
43
+
44
+ it "returns column ranges" do
45
+ Formula.parse('$C:$AZ').to_ast.should == [:formula,[:column_range,'$C','$AZ']]
46
+ Formula.parse('C:AZ').to_ast.should == [:formula,[:column_range,'C','AZ']]
47
+ end
48
+
49
+ it "returns references to other sheets" do
50
+ Formula.parse('sheet1!$A$1').to_ast.should == [:formula,[:sheet_reference,'sheet1',[:cell,'$A$1']]]
51
+ Formula.parse('sheet1!$A$1:$Z$1').to_ast.should == [:formula,[:sheet_reference,'sheet1',[:area,'$A$1','$Z$1']]]
52
+ Formula.parse('sheet1!$1:$1000').to_ast.should == [:formula,[:sheet_reference,'sheet1',[:row_range,'$1','$1000']]]
53
+ Formula.parse('sheet1!$C:$AZ').to_ast.should == [:formula,[:sheet_reference,'sheet1',[:column_range,'$C','$AZ']]]
54
+ end
55
+
56
+ it "returns references to other sheets with extended names" do
57
+ Formula.parse("'sheet 1'!$A$1").to_ast.should == [:formula,[:quoted_sheet_reference,'sheet 1',[:cell,'$A$1']]]
58
+ Formula.parse("'sheet 1'!$A$1:$Z$1").to_ast.should == [:formula,[:quoted_sheet_reference,'sheet 1',[:area,'$A$1','$Z$1']]]
59
+ Formula.parse("'sheet 1'!$1:$1000").to_ast.should == [:formula,[:quoted_sheet_reference,'sheet 1',[:row_range,'$1','$1000']]]
60
+ Formula.parse("'sheet 1'!$C:$AZ").to_ast.should == [:formula,[:quoted_sheet_reference,'sheet 1',[:column_range,'$C','$AZ']]]
61
+ end
62
+
63
+ it "returns numbers" do
64
+ Formula.parse("1").to_ast.should == [:formula,[:number,'1']]
65
+ Formula.parse("103.287").to_ast.should == [:formula,[:number,'103.287']]
66
+ Formula.parse("-1.0E-27").to_ast.should == [:formula,[:number,'-1.0E-27']]
67
+ end
68
+
69
+ it "returns percentages" do
70
+ Formula.parse("1%").to_ast.should == [:formula,[:percentage,'1']]
71
+ Formula.parse("103.287%").to_ast.should == [:formula,[:percentage,'103.287']]
72
+ Formula.parse("-1.0%").to_ast.should == [:formula,[:percentage,'-1.0']]
73
+ end
74
+
75
+ it "returns strings" do
76
+ Formula.parse('"A handy string"').to_ast.should == [:formula,[:string,"A handy string"]]
77
+ Formula.parse('"$A$1"').to_ast.should == [:formula,[:string,"$A$1"]]
78
+ end
79
+
80
+ it "returns string joins" do
81
+ Formula.parse('"A handy string"&$A$1').to_ast.should == [:formula,[:string_join,[:string,"A handy string"],[:cell,'$A$1']]]
82
+ Formula.parse('$A$1&"A handy string"').to_ast.should == [:formula,[:string_join,[:cell,'$A$1'],[:string,"A handy string"]]]
83
+ Formula.parse('$A$1&"A handy string"&$A$1').to_ast.should == [:formula,[:string_join,[:cell,'$A$1'],[:string,"A handy string"],[:cell,'$A$1'],]]
84
+ Formula.parse('$A$1&$A$1&$A$1').to_ast.should == [:formula,[:string_join,[:cell,'$A$1'],[:cell,'$A$1'],[:cell,'$A$1'],]]
85
+ Formula.parse('"GW"&ISERR($AA$1)').to_ast.should == [:formula,[:string_join,[:string,'GW'],[:function,'ISERR',[:cell,'$AA$1']]]]
86
+ end
87
+
88
+ it "returns numeric operations" do
89
+ Formula.parse('$A$1+$A$2+1').to_ast.should == [:formula,[:arithmetic,[:cell,'$A$1'],[:operator,"+"],[:cell,'$A$2'],[:operator,"+"],[:number,'1']]]
90
+ Formula.parse('$A$1-$A$2-1').to_ast.should == [:formula,[:arithmetic,[:cell,'$A$1'],[:operator,"-"],[:cell,'$A$2'],[:operator,"-"],[:number,'1']]]
91
+ Formula.parse('$A$1*$A$2*1').to_ast.should == [:formula,[:arithmetic,[:cell,'$A$1'],[:operator,"*"],[:cell,'$A$2'],[:operator,"*"],[:number,'1']]]
92
+ Formula.parse('$A$1/$A$2/1').to_ast.should == [:formula,[:arithmetic,[:cell,'$A$1'],[:operator,"/"],[:cell,'$A$2'],[:operator,"/"],[:number,'1']] ]
93
+ Formula.parse('$A$1^$A$2^1').to_ast.should == [:formula,[:arithmetic,[:cell,'$A$1'],[:operator,"^"],[:cell,'$A$2'],[:operator,"^"],[:number,'1']]]
94
+ end
95
+
96
+ it "returns expressions in brackets" do
97
+ Formula.parse('($A$1+$A$2)').to_ast.should == [:formula,[:brackets,[:arithmetic,[:cell,'$A$1'],[:operator,"+"],[:cell,'$A$2']]]]
98
+ Formula.parse('($A$1+$A$2)+2').to_ast.should == [:formula, [:arithmetic, [:brackets, [:arithmetic, [:cell,'$A$1'], [:operator,"+"], [:cell,'$A$2']]], [:operator,"+"], [:number,'2']]]
99
+ Formula.parse('($A$1+$A$2)+(2+(1*2))').to_ast.should == [:formula, [:arithmetic, [:brackets, [:arithmetic, [:cell,'$A$1'], [:operator,"+"], [:cell,'$A$2']]], [:operator,"+"], [:brackets, [:arithmetic, [:number,'2'], [:operator,'+'] ,[:brackets, [:arithmetic, [:number,'1'], [:operator,'*'], [:number,'2']]]]]]]
100
+ end
101
+
102
+ it "returns comparisons" do
103
+ Formula.parse('$A$1>$A$2').to_ast.should == [:formula,[:comparison,[:cell,'$A$1'],[:comparator,">"],[:cell,'$A$2']]]
104
+ Formula.parse('$A$1<$A$2').to_ast.should == [:formula,[:comparison,[:cell,'$A$1'],[:comparator,"<"],[:cell,'$A$2']]]
105
+ Formula.parse('$A$1=$A$2').to_ast.should == [:formula,[:comparison,[:cell,'$A$1'],[:comparator,"="],[:cell,'$A$2']]]
106
+ Formula.parse('$A$1>=$A$2').to_ast.should == [:formula,[:comparison,[:cell,'$A$1'],[:comparator,">="],[:cell,'$A$2']]]
107
+ Formula.parse('$A$1<=$A$2').to_ast.should == [:formula,[:comparison,[:cell,'$A$1'],[:comparator,"<="],[:cell,'$A$2']]]
108
+ Formula.parse('$A$1<>$A$2').to_ast.should == [:formula,[:comparison,[:cell,'$A$1'],[:comparator,"<>"],[:cell,'$A$2']]]
109
+ end
110
+
111
+ it "returns functions" do
112
+ Formula.parse('PI()').to_ast.should == [:formula,[:function,'PI']]
113
+ Formula.parse('ERR($A$1)').to_ast.should == [:formula,[:function,'ERR',[:cell,'$A$1']]]
114
+ Formula.parse('SUM($A$1,sheet1!$1:$1000,1)').to_ast.should == [:formula,[:function,'SUM',[:cell,'$A$1'],[:sheet_reference,'sheet1',[:row_range,'$1','$1000']],[:number,'1']]]
115
+ end
116
+
117
+ it "returns fully qualified structured references (i.e., Table[column])" do
118
+ Formula.parse('DeptSales[Sale Amount]').to_ast.should == [:formula,[:qualified_table_reference,'DeptSales','Sale Amount']]
119
+ #Formula.parse("DeptSales[Sale'] Amount]").to_ast.should == [:formula,[:qualified_table_reference,'DeptSales','Sale Amount']]
120
+ end
121
+
122
+ it "returns booleans" do
123
+ Formula.parse("TRUE*FALSE").to_ast.should == [:formula,[:arithmetic,[:boolean_true],[:operator,'*'],[:boolean_false]]]
124
+ end
125
+
126
+ it "returns prefixes (+/-)" do
127
+ Formula.parse("-(3-1)").to_ast.should == [:formula,[:prefix,'-'],[:brackets,[:arithmetic,[:number,'3'],[:operator,'-'],[:number,'1']]]]
128
+ end
129
+
130
+ it "returns local structured references (i.e., [column])" do
131
+ Formula.parse('[Sale Amount]').to_ast.should == [:formula,[:local_table_reference,'Sale Amount']]
132
+ #Formula.parse("DeptSales[Sale'] Amount]").to_ast.should == [:formula,[:qualified_table_reference,'DeptSales','Sale Amount']]
133
+ end
134
+
135
+ it "returns named references" do
136
+ Formula.parse('EF.NaturalGas.N2O').to_ast.should == [:formula,[:named_reference,'EF.NaturalGas.N2O']]
137
+ end
138
+
139
+ end