excel_to_code 0.1.23 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/src/commands/excel_to_c.rb +39 -92
- data/src/commands/excel_to_ruby.rb +9 -35
- data/src/commands/excel_to_x.rb +515 -536
- data/src/compile/c/a.out +0 -0
- data/src/compile/c/compile_named_reference_setters.rb +4 -6
- data/src/compile/c/compile_to_c.rb +34 -21
- data/src/compile/c/compile_to_c_header.rb +7 -7
- data/src/compile/c/excel_to_c_runtime.c +8 -4
- data/src/compile/c/map_formulae_to_c.rb +85 -86
- data/src/compile/c/map_values_to_c.rb +7 -1
- data/src/compile/c/map_values_to_c_structs.rb +1 -1
- data/src/compile/ruby/compile_to_ruby.rb +14 -11
- data/src/compile/ruby/compile_to_ruby_unit_test.rb +17 -10
- data/src/compile/ruby/map_formulae_to_ruby.rb +56 -56
- data/src/compile/ruby/map_values_to_ruby.rb +14 -2
- data/src/excel/area.rb +6 -8
- data/src/excel/excel_functions/hlookup.rb +1 -1
- data/src/excel/excel_functions/vlookup.rb +1 -1
- data/src/excel/formula_peg.rb +1 -1
- data/src/excel/formula_peg.txt +1 -1
- data/src/excel/reference.rb +4 -3
- data/src/excel/table.rb +4 -4
- data/src/extract.rb +1 -0
- data/src/extract/check_for_unknown_functions.rb +2 -2
- data/src/extract/extract_array_formulae.rb +9 -9
- data/src/extract/extract_everything.rb +140 -0
- data/src/extract/extract_formulae.rb +30 -20
- data/src/extract/extract_named_references.rb +37 -22
- data/src/extract/extract_relationships.rb +16 -3
- data/src/extract/extract_shared_formulae.rb +8 -11
- data/src/extract/extract_shared_formulae_targets.rb +1 -6
- data/src/extract/extract_shared_strings.rb +21 -8
- data/src/extract/extract_simple_formulae.rb +11 -6
- data/src/extract/extract_table.rb +26 -13
- data/src/extract/extract_values.rb +35 -11
- data/src/extract/extract_worksheet_dimensions.rb +13 -3
- data/src/extract/extract_worksheet_names.rb +16 -3
- data/src/extract/extract_worksheet_table_relationships.rb +16 -4
- data/src/extract/simple_extract_from_xml.rb +9 -11
- data/src/rewrite.rb +3 -0
- data/src/rewrite/ast_copy_formula.rb +5 -1
- data/src/rewrite/ast_expand_array_formulae.rb +71 -59
- data/src/rewrite/caching_formula_parser.rb +110 -0
- data/src/rewrite/rewrite_array_formulae.rb +21 -14
- data/src/rewrite/rewrite_cell_references_to_include_sheet.rb +41 -13
- data/src/rewrite/rewrite_shared_formulae.rb +17 -18
- data/src/rewrite/rewrite_values_to_ast.rb +2 -0
- data/src/rewrite/rewrite_whole_row_column_references_to_areas.rb +28 -25
- data/src/simplify.rb +1 -0
- data/src/simplify/count_formula_references.rb +22 -23
- data/src/simplify/emergency_array_formula_replace_indirect_bodge.rb +44 -0
- data/src/simplify/identify_dependencies.rb +7 -8
- data/src/simplify/identify_repeated_formula_elements.rb +5 -6
- data/src/simplify/inline_formulae.rb +48 -48
- data/src/simplify/map_formulae_to_values.rb +197 -79
- data/src/simplify/remove_cells.rb +13 -6
- data/src/simplify/replace_arithmetic_on_ranges.rb +42 -28
- data/src/simplify/replace_arrays_with_single_cells.rb +11 -5
- data/src/simplify/replace_column_with_column_number.rb +31 -23
- data/src/simplify/replace_common_elements_in_formulae.rb +16 -17
- data/src/simplify/replace_indirects_with_references.rb +26 -21
- data/src/simplify/replace_named_references.rb +26 -31
- data/src/simplify/replace_offsets_with_references.rb +33 -34
- data/src/simplify/replace_ranges_with_array_literals.rb +48 -20
- data/src/simplify/replace_shared_strings.rb +15 -13
- data/src/simplify/replace_string_join_on_ranges.rb +7 -9
- data/src/simplify/replace_table_references.rb +16 -11
- data/src/simplify/replace_values_with_constants.rb +6 -4
- data/src/simplify/simplify_arithmetic.rb +33 -19
- data/src/simplify/sort_into_calculation_order.rb +13 -13
- data/src/simplify/wrap_formulae_that_return_arrays_and_are_not_in_arrays.rb +21 -13
- metadata +19 -2
@@ -1,15 +1,28 @@
|
|
1
|
-
|
1
|
+
require 'nokogiri'
|
2
2
|
|
3
|
-
class ExtractValues <
|
4
|
-
|
5
|
-
|
3
|
+
class ExtractValues < Nokogiri::XML::SAX::Document
|
4
|
+
|
5
|
+
def self.extract(*args)
|
6
|
+
self.new.extract(*args)
|
7
|
+
end
|
8
|
+
|
9
|
+
def extract(sheet_name, input)
|
10
|
+
@sheet_name = sheet_name.to_sym
|
11
|
+
@output = {}
|
12
|
+
@parsing = false
|
13
|
+
@fp = CachingFormulaParser.instance
|
14
|
+
Nokogiri::XML::SAX::Parser.new(self).parse(input)
|
15
|
+
@output
|
16
|
+
end
|
17
|
+
|
18
|
+
attr_accessor :ref, :type, :value
|
6
19
|
|
7
20
|
def start_element(name,attributes)
|
8
21
|
if name == "v"
|
9
|
-
|
10
|
-
|
22
|
+
@parsing = true
|
23
|
+
@value = []
|
11
24
|
elsif name == "c"
|
12
|
-
@ref = attributes.assoc('r').last
|
25
|
+
@ref = attributes.assoc('r').last.to_sym
|
13
26
|
type = attributes.assoc('t')
|
14
27
|
@type = type ? type.last : "n"
|
15
28
|
end
|
@@ -17,13 +30,24 @@ class ExtractValues < SimpleExtractFromXML
|
|
17
30
|
|
18
31
|
def end_element(name)
|
19
32
|
return unless name == "v"
|
20
|
-
|
21
|
-
|
33
|
+
@parsing = false
|
34
|
+
value = @value.join
|
35
|
+
ast = case @type
|
36
|
+
when 'b'; value == "1" ? [:boolean_true] : [:boolean_false]
|
37
|
+
when 's'; [:shared_string,value.to_i]
|
38
|
+
when 'n'; [:number,value.to_f]
|
39
|
+
when 'e'; [:error,value.to_sym]
|
40
|
+
when 'str'; [:string,value.gsub(/_x[0-9A-F]{4}_/,'').freeze]
|
41
|
+
else
|
42
|
+
$stderr.puts "Type #{type} not known #{@sheet_name} #{@ref}"
|
43
|
+
exit
|
44
|
+
end
|
45
|
+
@output[[@sheet_name, @ref]] = @fp.map(ast)
|
22
46
|
end
|
23
47
|
|
24
48
|
def characters(string)
|
25
|
-
return unless parsing
|
26
|
-
|
49
|
+
return unless @parsing
|
50
|
+
@value << string
|
27
51
|
end
|
28
52
|
|
29
53
|
end
|
@@ -1,11 +1,21 @@
|
|
1
|
-
|
1
|
+
require 'nokogiri'
|
2
2
|
|
3
|
-
class ExtractWorksheetDimensions <
|
3
|
+
class ExtractWorksheetDimensions < Nokogiri::XML::SAX::Document
|
4
|
+
|
5
|
+
def self.extract(*args)
|
6
|
+
self.new.extract(*args)
|
7
|
+
end
|
8
|
+
|
9
|
+
def extract(input)
|
10
|
+
parser = Nokogiri::XML::SAX::Parser.new(self)
|
11
|
+
parser.parse(input)
|
12
|
+
@output
|
13
|
+
end
|
4
14
|
|
5
15
|
# FIXME: Is there an elegant way to abort once we have found the dimension tag?
|
6
16
|
def start_element(name,attributes)
|
7
17
|
return false unless name == "dimension"
|
8
|
-
output
|
18
|
+
@output = attributes.assoc('ref').last
|
9
19
|
end
|
10
20
|
|
11
21
|
end
|
@@ -1,10 +1,23 @@
|
|
1
|
-
|
1
|
+
require 'nokogiri'
|
2
2
|
|
3
|
-
class ExtractWorksheetNames <
|
3
|
+
class ExtractWorksheetNames < Nokogiri::XML::SAX::Document
|
4
|
+
|
5
|
+
attr_accessor :input, :output
|
6
|
+
|
7
|
+
def self.extract(*args)
|
8
|
+
self.new.extract(*args)
|
9
|
+
end
|
10
|
+
|
11
|
+
def extract(input)
|
12
|
+
@input, @output = input, {}
|
13
|
+
parser = Nokogiri::XML::SAX::Parser.new(self)
|
14
|
+
parser.parse(input)
|
15
|
+
output
|
16
|
+
end
|
4
17
|
|
5
18
|
def start_element(name,attributes)
|
6
19
|
return false unless name == "sheet"
|
7
|
-
output
|
20
|
+
output[attributes.assoc('name').last] = attributes.assoc('r:id').last
|
8
21
|
end
|
9
22
|
|
10
23
|
end
|
@@ -1,10 +1,22 @@
|
|
1
|
-
|
1
|
+
require 'nokogiri'
|
2
2
|
|
3
|
-
|
3
|
+
|
4
|
+
class ExtractWorksheetTableRelationships < Nokogiri::XML::SAX::Document
|
5
|
+
|
6
|
+
def self.extract(*args)
|
7
|
+
self.new.extract(*args)
|
8
|
+
end
|
9
|
+
|
10
|
+
def extract(input)
|
11
|
+
@output = []
|
12
|
+
@parsing = false
|
13
|
+
Nokogiri::XML::SAX::Parser.new(self).parse(input)
|
14
|
+
@output
|
15
|
+
end
|
4
16
|
|
5
17
|
def start_element(name,attributes)
|
6
18
|
return false unless name == "tablePart"
|
7
|
-
output
|
19
|
+
@output << "#{attributes.assoc('r:id').last}"
|
8
20
|
end
|
9
21
|
|
10
|
-
end
|
22
|
+
end
|
@@ -2,18 +2,16 @@ require 'nokogiri'
|
|
2
2
|
|
3
3
|
class SimpleExtractFromXML < Nokogiri::XML::SAX::Document
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
def self.extract(input,output)
|
8
|
-
self.new.extract(input,output)
|
5
|
+
def self.extract(*args)
|
6
|
+
self.new.extract(*args)
|
9
7
|
end
|
10
8
|
|
11
|
-
def extract(input
|
12
|
-
@
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
output
|
9
|
+
def extract(sheet_name, input)
|
10
|
+
@sheet_name = sheet_name
|
11
|
+
@output = {}
|
12
|
+
@parsing = false
|
13
|
+
Nokogiri::XML::SAX::Parser.new(self).parse(input)
|
14
|
+
@output
|
17
15
|
end
|
18
16
|
|
19
|
-
end
|
17
|
+
end
|
data/src/rewrite.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require_relative "excel/formula_peg"
|
1
2
|
require_relative "rewrite/rewrite_formulae_to_ast"
|
2
3
|
require_relative "rewrite/rewrite_worksheet_names"
|
3
4
|
require_relative "rewrite/rewrite_whole_row_column_references_to_areas"
|
@@ -9,3 +10,5 @@ require_relative "rewrite/rewrite_relationship_id_to_filename"
|
|
9
10
|
require_relative "rewrite/rewrite_merge_formulae_and_values"
|
10
11
|
require_relative "rewrite/rewrite_cell_references_to_include_sheet"
|
11
12
|
require_relative "rewrite/rewrite_named_reference_names"
|
13
|
+
require_relative "rewrite/caching_formula_parser"
|
14
|
+
|
@@ -10,11 +10,15 @@ class AstCopyFormula
|
|
10
10
|
self.columns_to_move = 0
|
11
11
|
end
|
12
12
|
|
13
|
+
DO_NOT_MAP = {:number => true, :string => true, :blank => true, :null => true, :error => true, :boolean_true => true, :boolean_false => true, :operator => true, :comparator => true}
|
14
|
+
|
13
15
|
def copy(ast)
|
14
16
|
return ast unless ast.is_a?(Array)
|
15
17
|
operator = ast[0]
|
16
18
|
if respond_to?(operator)
|
17
19
|
send(operator,*ast[1..-1])
|
20
|
+
elsif DO_NOT_MAP[operator]
|
21
|
+
return ast
|
18
22
|
else
|
19
23
|
[operator,*ast[1..-1].map {|a| copy(a) }]
|
20
24
|
end
|
@@ -39,4 +43,4 @@ class AstCopyFormula
|
|
39
43
|
raise NotSupportedException.new("Row ranges not suported in AstCopyFormula")
|
40
44
|
end
|
41
45
|
|
42
|
-
end
|
46
|
+
end
|
@@ -5,39 +5,44 @@ class AstExpandArrayFormulae
|
|
5
5
|
def map(ast)
|
6
6
|
return ast unless ast.is_a?(Array)
|
7
7
|
operator = ast[0]
|
8
|
-
if respond_to?(operator)
|
9
|
-
|
10
|
-
|
11
|
-
[operator,*ast[1..-1].map {|a| map(a) }]
|
12
|
-
end
|
8
|
+
send(operator, ast) if respond_to?(operator)
|
9
|
+
ast.each {|a| map(a) }
|
10
|
+
ast
|
13
11
|
end
|
14
12
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
return
|
13
|
+
# Format [:arithmetic, left, operator, right]
|
14
|
+
def arithmetic(ast)
|
15
|
+
ast.each {|a| map(a) }
|
16
|
+
return unless array?(ast[1], ast[3])
|
19
17
|
|
20
|
-
|
21
|
-
[
|
22
|
-
|
18
|
+
ast.replace(
|
19
|
+
map_arrays([ast[1],ast[3]]) do |arrayed|
|
20
|
+
[:arithmetic,arrayed[0],ast[2],arrayed[1]]
|
21
|
+
end
|
22
|
+
)
|
23
23
|
end
|
24
24
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
return
|
29
|
-
|
30
|
-
|
31
|
-
[
|
32
|
-
|
25
|
+
# Format [:comparison, left, operator, right]
|
26
|
+
def comparison(ast)
|
27
|
+
ast.each {|a| map(a) }
|
28
|
+
return unless array?(ast[1], ast[3])
|
29
|
+
|
30
|
+
ast.replace(
|
31
|
+
map_arrays([ast[1],ast[3]]) do |arrayed|
|
32
|
+
[:comparison,arrayed[0],ast[2],arrayed[1]]
|
33
|
+
end
|
34
|
+
)
|
33
35
|
end
|
34
36
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
37
|
+
# Format [:string_join, stringA, stringB, ...]
|
38
|
+
def string_join(ast)
|
39
|
+
ast.each {|a| map(a) }
|
40
|
+
return unless array?(*ast[1..-1])
|
41
|
+
ast.replace(
|
42
|
+
map_arrays(ast[1..-1]) do |arrayed_strings|
|
43
|
+
[:string_join, *arrayed_strings]
|
44
|
+
end
|
45
|
+
)
|
41
46
|
end
|
42
47
|
|
43
48
|
def map_arrays(arrays, &block)
|
@@ -58,56 +63,62 @@ class AstExpandArrayFormulae
|
|
58
63
|
return [:array, *max_rows.times.map do |row|
|
59
64
|
[:row, *max_columns.times.map do |column|
|
60
65
|
block.call(arrays.map do |a|
|
61
|
-
a[row][column] || [:error, "#N/A"]
|
66
|
+
(a[row] && a[row][column]) || CachingFormulaParser.map([:error, :"#N/A"])
|
62
67
|
end)
|
63
68
|
end]
|
64
69
|
end]
|
65
70
|
end
|
66
71
|
|
67
|
-
FUNCTIONS_THAT_ACCEPT_RANGES_FOR_ALL_ARGUMENTS =
|
72
|
+
FUNCTIONS_THAT_ACCEPT_RANGES_FOR_ALL_ARGUMENTS = {AVERAGE: true, COUNT: true, COUNTA: true, MAX: true, MIN: true, SUM: true, SUMPRODUCT: true, MMULT: true}
|
68
73
|
|
69
|
-
|
70
|
-
|
71
|
-
|
74
|
+
# Format [:function, function_name, arg1, arg2, ...]
|
75
|
+
def function(ast)
|
76
|
+
name = ast[1]
|
77
|
+
arguments = ast[2..-1]
|
78
|
+
if FUNCTIONS_THAT_ACCEPT_RANGES_FOR_ALL_ARGUMENTS.has_key?(name)
|
79
|
+
ast.each { |a| map(a) }
|
80
|
+
return # No need to alter anything
|
72
81
|
elsif respond_to?("map_#{name.downcase}")
|
73
|
-
|
82
|
+
# These typically have some arguments that accept ranges, but not all
|
83
|
+
send("map_#{name.downcase}",ast)
|
74
84
|
else
|
75
|
-
function_that_does_not_accept_ranges(
|
85
|
+
function_that_does_not_accept_ranges(ast)
|
76
86
|
end
|
77
87
|
end
|
78
88
|
|
79
|
-
def function_that_does_not_accept_ranges(
|
80
|
-
return
|
81
|
-
|
89
|
+
def function_that_does_not_accept_ranges(ast)
|
90
|
+
return if ast.length == 2
|
91
|
+
name = ast[1]
|
92
|
+
arguments = ast[2..-1]
|
93
|
+
array_map(ast, *Array.new(arguments.length,false))
|
82
94
|
end
|
83
95
|
|
84
|
-
def map_match(
|
85
|
-
|
86
|
-
a
|
96
|
+
def map_match(ast)
|
97
|
+
array_map(ast, false, true, false)
|
87
98
|
end
|
88
99
|
|
89
|
-
def map_subtotal(
|
90
|
-
array_map
|
100
|
+
def map_subtotal(ast)
|
101
|
+
array_map ast, false, *Array.new(ast.length-3,true)
|
91
102
|
end
|
92
103
|
|
93
|
-
def map_index(
|
94
|
-
array_map
|
104
|
+
def map_index(ast)
|
105
|
+
array_map ast, true, false, false
|
95
106
|
end
|
96
107
|
|
97
|
-
def map_sumif(
|
98
|
-
array_map
|
108
|
+
def map_sumif(ast)
|
109
|
+
array_map ast, true, false, true
|
99
110
|
end
|
100
111
|
|
101
|
-
def map_sumifs(
|
102
|
-
if
|
103
|
-
array_map
|
112
|
+
def map_sumifs(ast)
|
113
|
+
if ast.length > 5
|
114
|
+
array_map ast, true, true, false, *([true,false]*((ast.length-5)/2))
|
104
115
|
else
|
105
|
-
array_map
|
116
|
+
array_map ast, true, true, false
|
106
117
|
end
|
107
118
|
end
|
108
119
|
|
109
|
-
def map_vlookup(
|
110
|
-
array_map
|
120
|
+
def map_vlookup(ast)
|
121
|
+
array_map ast, false, true, false, false
|
111
122
|
end
|
112
123
|
|
113
124
|
private
|
@@ -121,15 +132,16 @@ class AstExpandArrayFormulae
|
|
121
132
|
true
|
122
133
|
end
|
123
134
|
|
124
|
-
def array_map(
|
125
|
-
|
126
|
-
|
135
|
+
def array_map(ast,*ok_to_be_an_array)
|
136
|
+
ast.each { |a| map(a) }
|
137
|
+
|
138
|
+
return if no_need_to_array?(ast[2..-1],ok_to_be_an_array)
|
127
139
|
|
128
140
|
# Turn the relevant arguments into ruby arrays and store the dimensions
|
129
141
|
# Enumerable#max and Enumerable#min don't return Enumerators, so can't do it using those methods
|
130
142
|
max_rows = 1
|
131
143
|
max_columns = 1
|
132
|
-
args =
|
144
|
+
args = ast[2..-1].map.with_index do |a,i|
|
133
145
|
unless ok_to_be_an_array[i]
|
134
146
|
a = array_ast_to_ruby_array(a)
|
135
147
|
r = a.length
|
@@ -147,17 +159,17 @@ class AstExpandArrayFormulae
|
|
147
159
|
args = args.map.with_index { |a,i| (!ok_to_be_an_array[i] && a.first.length == 1) ? Array.new(max_columns,a.flatten(1)).transpose : a }
|
148
160
|
|
149
161
|
# Now iterate through
|
150
|
-
|
162
|
+
ast.replace( [:array, *max_rows.times.map do |row|
|
151
163
|
[:row, *max_columns.times.map do |column|
|
152
|
-
[:function,
|
164
|
+
[:function, ast[1], *args.map.with_index do |a,i|
|
153
165
|
if ok_to_be_an_array[i]
|
154
166
|
a
|
155
167
|
else
|
156
|
-
a[row][column] || [:error, "#N/A"]
|
168
|
+
a[row][column] || [:error, :"#N/A"]
|
157
169
|
end
|
158
170
|
end]
|
159
171
|
end]
|
160
|
-
end]
|
172
|
+
end])
|
161
173
|
end
|
162
174
|
|
163
175
|
def array?(*args)
|
@@ -0,0 +1,110 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
|
3
|
+
class CachingFormulaParser
|
4
|
+
include Singleton
|
5
|
+
|
6
|
+
def self.parse(*args)
|
7
|
+
instance.parse(*args)
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.map(*args)
|
11
|
+
instance.map(*args)
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
@number_cache = {}
|
16
|
+
@string_cache = {}
|
17
|
+
@percentage_cache = {}
|
18
|
+
@error_cache = {}
|
19
|
+
@operator_cache = {}
|
20
|
+
@comparator_cache = {}
|
21
|
+
@sheet_reference_cache = {}
|
22
|
+
end
|
23
|
+
|
24
|
+
def parse(text)
|
25
|
+
ast = Formula.parse(text)
|
26
|
+
if ast
|
27
|
+
map(ast.to_ast[1])
|
28
|
+
else
|
29
|
+
nil
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def map(ast)
|
34
|
+
return ast unless ast.is_a?(Array)
|
35
|
+
ast[1] = ast[1].to_sym if ast[0] == :function
|
36
|
+
if respond_to?(ast[0])
|
37
|
+
ast = send(ast[0], ast)
|
38
|
+
else
|
39
|
+
ast.each.with_index do |a,i|
|
40
|
+
next unless a.is_a?(Array)
|
41
|
+
a[1] = a[1].to_sym if a[0] == :function
|
42
|
+
ast[i] = map(a)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
ast
|
46
|
+
end
|
47
|
+
|
48
|
+
def sheet_reference(ast)
|
49
|
+
ast[1] = ast[1].to_sym
|
50
|
+
ast[2] = map(ast[2])
|
51
|
+
@sheet_reference_cache[ast] ||= ast
|
52
|
+
end
|
53
|
+
|
54
|
+
def cell(ast)
|
55
|
+
ast[1] = ast[1].to_sym
|
56
|
+
ast
|
57
|
+
end
|
58
|
+
|
59
|
+
def area(ast)
|
60
|
+
ast[1] = ast[1].to_sym
|
61
|
+
ast[2] = ast[2].to_sym
|
62
|
+
ast
|
63
|
+
end
|
64
|
+
|
65
|
+
def number(ast)
|
66
|
+
ast[1] = ast[1].to_f
|
67
|
+
@number_cache[ast] ||= ast
|
68
|
+
end
|
69
|
+
|
70
|
+
def percentage(ast)
|
71
|
+
ast[1] = ast[1].to_f
|
72
|
+
@percentage_cache[ast] ||= ast
|
73
|
+
end
|
74
|
+
|
75
|
+
def string(ast)
|
76
|
+
return @string_cache[ast] ||= ast
|
77
|
+
end
|
78
|
+
|
79
|
+
TRUE = [:boolean_true]
|
80
|
+
FALSE = [:boolean_false]
|
81
|
+
BLANK = [:blank]
|
82
|
+
|
83
|
+
def boolean_true(ast)
|
84
|
+
TRUE
|
85
|
+
end
|
86
|
+
|
87
|
+
def boolean_false(ast)
|
88
|
+
FALSE
|
89
|
+
end
|
90
|
+
|
91
|
+
def error(ast)
|
92
|
+
ast[1] = ast[1].to_sym
|
93
|
+
@error_cache[ast] ||= ast
|
94
|
+
end
|
95
|
+
|
96
|
+
def blank(ast)
|
97
|
+
BLANK
|
98
|
+
end
|
99
|
+
|
100
|
+
def operator(ast)
|
101
|
+
ast[1] = ast[1].to_sym
|
102
|
+
@operator_cache[ast] ||= ast
|
103
|
+
end
|
104
|
+
|
105
|
+
def comparator(ast)
|
106
|
+
ast[1] = ast[1].to_sym
|
107
|
+
@comparator_cache[ast] ||= ast
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|