excel_to_code 0.1.23 → 0.2.0
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.
- 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
|