csv_plus_plus 0.0.5 → 0.1.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/CHANGELOG.md +7 -0
- data/README.md +1 -0
- data/lib/csv_plus_plus/cell.rb +24 -8
- data/lib/csv_plus_plus/cli.rb +29 -16
- data/lib/csv_plus_plus/cli_flag.rb +10 -2
- data/lib/csv_plus_plus/code_section.rb +22 -3
- data/lib/csv_plus_plus/color.rb +19 -5
- data/lib/csv_plus_plus/google_options.rb +6 -2
- data/lib/csv_plus_plus/graph.rb +0 -1
- data/lib/csv_plus_plus/language/ast_builder.rb +68 -0
- data/lib/csv_plus_plus/language/benchmarked_compiler.rb +65 -0
- data/lib/csv_plus_plus/language/builtins.rb +46 -0
- data/lib/csv_plus_plus/language/cell_value.tab.rb +1 -2
- data/lib/csv_plus_plus/language/code_section.tab.rb +1 -2
- data/lib/csv_plus_plus/language/compiler.rb +74 -86
- data/lib/csv_plus_plus/language/entities/boolean.rb +3 -2
- data/lib/csv_plus_plus/language/entities/cell_reference.rb +10 -3
- data/lib/csv_plus_plus/language/entities/entity.rb +20 -8
- data/lib/csv_plus_plus/language/entities/function.rb +6 -4
- data/lib/csv_plus_plus/language/entities/function_call.rb +4 -3
- data/lib/csv_plus_plus/language/entities/number.rb +6 -4
- data/lib/csv_plus_plus/language/entities/runtime_value.rb +9 -8
- data/lib/csv_plus_plus/language/entities/string.rb +6 -4
- data/lib/csv_plus_plus/language/references.rb +22 -5
- data/lib/csv_plus_plus/language/runtime.rb +80 -22
- data/lib/csv_plus_plus/language/scope.rb +29 -38
- data/lib/csv_plus_plus/language/syntax_error.rb +10 -5
- data/lib/csv_plus_plus/lexer/lexer.rb +25 -12
- data/lib/csv_plus_plus/lexer/tokenizer.rb +35 -11
- data/lib/csv_plus_plus/modifier.rb +38 -13
- data/lib/csv_plus_plus/modifier.tab.rb +2 -2
- data/lib/csv_plus_plus/options.rb +17 -2
- data/lib/csv_plus_plus/row.rb +15 -4
- data/lib/csv_plus_plus/template.rb +10 -6
- data/lib/csv_plus_plus/version.rb +1 -1
- data/lib/csv_plus_plus/writer/excel.rb +2 -9
- data/lib/csv_plus_plus/writer/file_backer_upper.rb +22 -20
- data/lib/csv_plus_plus/writer/google_sheet_builder.rb +8 -10
- data/lib/csv_plus_plus/writer/google_sheets.rb +4 -10
- data/lib/csv_plus_plus/writer/rubyxl_builder.rb +23 -15
- data/lib/csv_plus_plus/writer/rubyxl_modifier.rb +15 -8
- data/lib/csv_plus_plus.rb +21 -3
- metadata +5 -2
@@ -1,12 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'benchmark'
|
4
3
|
require 'csv'
|
4
|
+
# TODO: move some of these out to csv_plus_plus.rb
|
5
5
|
require_relative '../cell'
|
6
6
|
require_relative '../modifier'
|
7
7
|
require_relative '../modifier.tab'
|
8
8
|
require_relative '../row'
|
9
9
|
require_relative '../template'
|
10
|
+
require_relative 'benchmarked_compiler'
|
10
11
|
require_relative 'code_section.tab'
|
11
12
|
require_relative 'entities'
|
12
13
|
require_relative 'runtime'
|
@@ -14,59 +15,73 @@ require_relative 'scope'
|
|
14
15
|
|
15
16
|
module CSVPlusPlus
|
16
17
|
module Language
|
17
|
-
# Encapsulates the parsing and building of objects (+Template+ -> +Row+ -> +Cell+).
|
18
|
-
#
|
19
|
-
#
|
18
|
+
# Encapsulates the parsing and building of objects (+Template+ -> +Row+ -> +Cell+). Variable resolution is delegated
|
19
|
+
# to the +Scope+
|
20
|
+
#
|
21
|
+
# @attr_reader options [Options] The +Options+ to compile with
|
22
|
+
# @attr_reader runtime [Runtime] The runtime execution
|
23
|
+
# @attr_reader scope [Scope] +Scope+ for variable resolution
|
20
24
|
class Compiler
|
21
25
|
attr_reader :timings, :benchmark, :options, :runtime, :scope
|
22
26
|
|
23
27
|
# Create a compiler and make sure it gets cleaned up
|
24
|
-
|
25
|
-
|
26
|
-
|
28
|
+
#
|
29
|
+
# @param runtime [Runtime] The initial +Runtime+ for the compiler
|
30
|
+
# @param options [Options]
|
31
|
+
def self.with_compiler(runtime:, options:, &block)
|
32
|
+
compiler = new(options:, runtime:)
|
27
33
|
if options.verbose
|
28
|
-
|
34
|
+
::CSVPlusPlus::Language::BenchmarkedCompiler.with_benchmarks(compiler) do |c|
|
29
35
|
block.call(c)
|
30
36
|
end
|
31
37
|
else
|
32
|
-
yield(
|
38
|
+
yield(compiler)
|
33
39
|
end
|
34
40
|
ensure
|
35
41
|
runtime.cleanup!
|
36
42
|
end
|
37
43
|
|
38
|
-
#
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
block.call(compiler)
|
43
|
-
[compiler.timings.reduce(:+)]
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
# initialize
|
48
|
-
def initialize(runtime:, options:, scope: nil, benchmark: nil)
|
44
|
+
# @param runtime [Runtime]
|
45
|
+
# @param options [Options]
|
46
|
+
# @param scope [Scope, nil]
|
47
|
+
def initialize(runtime:, options:, scope: nil)
|
49
48
|
@options = options
|
50
49
|
@runtime = runtime
|
51
50
|
@scope = scope || ::CSVPlusPlus::Language::Scope.new(runtime:)
|
52
|
-
@benchmark = benchmark
|
53
|
-
@timings = [] if benchmark
|
54
51
|
end
|
55
52
|
|
56
|
-
#
|
57
|
-
def
|
53
|
+
# Write the compiled results
|
54
|
+
def outputting!
|
55
|
+
@runtime.start_at_csv!
|
56
|
+
yield
|
57
|
+
end
|
58
|
+
|
59
|
+
# Compile a template and return a +::CSVPlusPlus::Template+ instance ready to be written with a +Writer+
|
60
|
+
#
|
61
|
+
# @return [Template]
|
62
|
+
def compile_template
|
58
63
|
parse_code_section!
|
59
64
|
rows = parse_csv_section!
|
60
65
|
|
61
|
-
::CSVPlusPlus::Template.new(rows
|
66
|
+
::CSVPlusPlus::Template.new(rows:).tap do |t|
|
62
67
|
t.validate_infinite_expands(@runtime)
|
63
68
|
expanding { t.expand_rows! }
|
64
69
|
resolve_all_cells!(t)
|
65
70
|
end
|
66
71
|
end
|
67
72
|
|
68
|
-
#
|
73
|
+
# @return [String]
|
74
|
+
def to_s
|
75
|
+
"Compiler(options: #{@options}, runtime: #{@runtime}, scope: #{@scope})"
|
76
|
+
end
|
77
|
+
|
78
|
+
protected
|
79
|
+
|
80
|
+
# Parses the input file and returns a +CodeSection+
|
81
|
+
#
|
82
|
+
# @return [CodeSection]
|
69
83
|
def parse_code_section!
|
84
|
+
@runtime.start!
|
70
85
|
parsing_code_section do |input|
|
71
86
|
code_section, csv_section = ::CSVPlusPlus::Language::CodeSectionParser.new.parse(input, self)
|
72
87
|
# TODO: infer a type
|
@@ -82,20 +97,48 @@ module CSVPlusPlus
|
|
82
97
|
@scope.code_section
|
83
98
|
end
|
84
99
|
|
85
|
-
#
|
100
|
+
# Parse the CSV section and return an array of +Row+s
|
101
|
+
#
|
102
|
+
# @return [Array<Row>]
|
86
103
|
def parse_csv_section!
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
end
|
104
|
+
@runtime.start_at_csv!
|
105
|
+
@runtime.map_rows(::CSV.new(runtime.input)) do |csv_row|
|
106
|
+
parse_row(csv_row)
|
91
107
|
end
|
92
108
|
ensure
|
93
109
|
# we're done with the file and everything is in memory
|
94
110
|
@runtime.cleanup!
|
95
111
|
end
|
96
112
|
|
113
|
+
# Iterates through each cell of each row and resolves it's variable and function references.
|
114
|
+
#
|
115
|
+
# @param template [Template]
|
116
|
+
# @return [Array<Entity>]
|
117
|
+
def resolve_all_cells!(template)
|
118
|
+
@runtime.start_at_csv!
|
119
|
+
@runtime.map_rows(template.rows, cells_too: true) do |cell|
|
120
|
+
cell.ast = @scope.resolve_cell_value if cell.ast
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# Expanding rows
|
125
|
+
def expanding
|
126
|
+
@runtime.start_at_csv!
|
127
|
+
yield
|
128
|
+
end
|
129
|
+
|
130
|
+
private
|
131
|
+
|
132
|
+
def parsing_code_section
|
133
|
+
csv_section = yield(@runtime.input.read)
|
134
|
+
@runtime.rewrite_input!(csv_section)
|
135
|
+
end
|
136
|
+
|
97
137
|
# Using the current +@runtime+ and the given +csv_row+ parse it into a +Row+ of +Cell+s
|
98
138
|
# +csv_row+ should have already been run through a CSV parser and is an array of strings
|
139
|
+
#
|
140
|
+
# @param csv_row [Array<Array<String>>]
|
141
|
+
# @return [Row]
|
99
142
|
def parse_row(csv_row)
|
100
143
|
row_modifier = ::CSVPlusPlus::Modifier.new(row_level: true)
|
101
144
|
|
@@ -109,61 +152,6 @@ module CSVPlusPlus
|
|
109
152
|
|
110
153
|
::CSVPlusPlus::Row.new(@runtime.row_index, cells, row_modifier)
|
111
154
|
end
|
112
|
-
|
113
|
-
# workflow when resolving the values of all cells
|
114
|
-
def resolve_all_cells!(template)
|
115
|
-
workflow(stage: 'Resolving each cell') do
|
116
|
-
@runtime.map_rows(template.rows, cells_too: true) do |cell|
|
117
|
-
cell.ast = @scope.resolve_cell_value if cell.ast
|
118
|
-
end
|
119
|
-
end
|
120
|
-
end
|
121
|
-
|
122
|
-
# workflow when writing results
|
123
|
-
def outputting!(&block)
|
124
|
-
workflow(stage: 'Writing the spreadsheet') do
|
125
|
-
block.call
|
126
|
-
end
|
127
|
-
end
|
128
|
-
|
129
|
-
# to_s
|
130
|
-
def to_s
|
131
|
-
"Compiler(options: #{@options}, runtime: #{@runtime}, scope: #{@scope})"
|
132
|
-
end
|
133
|
-
|
134
|
-
private
|
135
|
-
|
136
|
-
# workflow when parsing the code section
|
137
|
-
def parsing_code_section(&block)
|
138
|
-
workflow(
|
139
|
-
stage: 'Parsing code section',
|
140
|
-
processing_code_section: true
|
141
|
-
) do
|
142
|
-
csv_section = block.call(@runtime.input.read)
|
143
|
-
@runtime.rewrite_input!(csv_section)
|
144
|
-
end
|
145
|
-
end
|
146
|
-
|
147
|
-
# workflow when expanding rows
|
148
|
-
def expanding(&block)
|
149
|
-
workflow(stage: 'Expanding rows') do
|
150
|
-
block.call
|
151
|
-
end
|
152
|
-
end
|
153
|
-
|
154
|
-
def workflow(stage:, processing_code_section: false, &block)
|
155
|
-
@runtime.init!(processing_code_section ? 1 : (@runtime.length_of_code_section || 1))
|
156
|
-
|
157
|
-
ret = nil
|
158
|
-
if @benchmark
|
159
|
-
@timings << @benchmark.report(stage) { ret = block.call }
|
160
|
-
else
|
161
|
-
ret = block.call
|
162
|
-
end
|
163
|
-
|
164
|
-
ret
|
165
|
-
end
|
166
155
|
end
|
167
|
-
# rubocop:enable Metrics/ClassLength
|
168
156
|
end
|
169
157
|
end
|
@@ -6,10 +6,11 @@ module CSVPlusPlus
|
|
6
6
|
module Language
|
7
7
|
module Entities
|
8
8
|
# A boolean value
|
9
|
+
#
|
10
|
+
# @attr_reader value [true, false]
|
9
11
|
class Boolean < Entity
|
10
12
|
attr_reader :value
|
11
13
|
|
12
|
-
# initialize
|
13
14
|
# @param value [String, Boolean]
|
14
15
|
def initialize(value)
|
15
16
|
super(:boolean)
|
@@ -22,7 +23,7 @@ module CSVPlusPlus
|
|
22
23
|
@value.to_s.upcase
|
23
24
|
end
|
24
25
|
|
25
|
-
# @return [
|
26
|
+
# @return [boolean]
|
26
27
|
def ==(other)
|
27
28
|
super && value == other.value
|
28
29
|
end
|
@@ -5,21 +5,28 @@ require_relative './entity'
|
|
5
5
|
module CSVPlusPlus
|
6
6
|
module Language
|
7
7
|
module Entities
|
8
|
-
##
|
9
8
|
# A reference to a cell
|
9
|
+
#
|
10
|
+
# @attr_reader cell_reference [String] The cell reference in A1 format
|
10
11
|
class CellReference < Entity
|
11
12
|
attr_reader :cell_reference
|
12
13
|
|
13
|
-
#
|
14
|
+
# @param cell_reference [String] The cell reference in A1 format
|
14
15
|
def initialize(cell_reference)
|
15
16
|
super(:cell_reference)
|
17
|
+
|
16
18
|
@cell_reference = cell_reference
|
17
19
|
end
|
18
20
|
|
19
|
-
#
|
21
|
+
# @return [String]
|
20
22
|
def to_s
|
21
23
|
@cell_reference
|
22
24
|
end
|
25
|
+
|
26
|
+
# @return [Boolean]
|
27
|
+
def ==(other)
|
28
|
+
super && @cell_reference == other.cell_reference
|
29
|
+
end
|
23
30
|
end
|
24
31
|
end
|
25
32
|
end
|
@@ -6,22 +6,28 @@ module CSVPlusPlus
|
|
6
6
|
module Language
|
7
7
|
module Entities
|
8
8
|
# A basic building block of the abstract syntax tree (AST)
|
9
|
+
#
|
10
|
+
# @attr_reader id [Symbol] The identifier of the entity. For functions this is the function name,
|
11
|
+
# for variables it's the variable name
|
12
|
+
# @attr_reader type [Symbol] The type of the entity. Valid values are defined in +::CSVPlusPlus::Language::Types+
|
9
13
|
class Entity
|
10
14
|
attr_reader :id, :type
|
11
15
|
|
12
|
-
# @param type [String, Symbol]
|
13
|
-
# @param id [String]
|
16
|
+
# @param type [::String, Symbol]
|
17
|
+
# @param id [::String, nil]
|
14
18
|
def initialize(type, id: nil)
|
15
19
|
@type = type.to_sym
|
16
20
|
@id = id.downcase.to_sym if id
|
17
21
|
end
|
18
22
|
|
19
|
-
# @return [
|
23
|
+
# @return [boolean]
|
20
24
|
def ==(other)
|
21
25
|
self.class == other.class && @type == other.type && @id == other.id
|
22
26
|
end
|
23
27
|
|
24
28
|
# Respond to predicates that correspond to types like #boolean?, #string?, etc
|
29
|
+
#
|
30
|
+
# @param method_name [Symbol] The +method_name+ to respond to
|
25
31
|
def method_missing(method_name, *_arguments)
|
26
32
|
if method_name =~ /^(\w+)\?$/
|
27
33
|
t = ::Regexp.last_match(1)
|
@@ -32,7 +38,10 @@ module CSVPlusPlus
|
|
32
38
|
end
|
33
39
|
|
34
40
|
# Respond to predicates by type (entity.boolean?, entity.string?, etc)
|
35
|
-
#
|
41
|
+
#
|
42
|
+
# @param method_name [Symbol] The +method_name+ to respond to
|
43
|
+
#
|
44
|
+
# @return [boolean]
|
36
45
|
def respond_to_missing?(method_name, *_arguments)
|
37
46
|
(method_name =~ /^(\w+)\?$/ && a_type?(::Regexp.last_match(1))) || super
|
38
47
|
end
|
@@ -44,19 +53,22 @@ module CSVPlusPlus
|
|
44
53
|
end
|
45
54
|
end
|
46
55
|
|
47
|
-
# An entity that can take other entities as arguments
|
56
|
+
# An entity that can take other entities as arguments. Current use cases for this
|
57
|
+
# are function calls and function definitions
|
58
|
+
#
|
59
|
+
# @attr_reader arguments [Array<Entity>] The arguments supplied to this entity.
|
48
60
|
class EntityWithArguments < Entity
|
49
61
|
attr_reader :arguments
|
50
62
|
|
51
|
-
# @param type [String, Symbol]
|
52
|
-
# @param id [String]
|
63
|
+
# @param type [::String, Symbol]
|
64
|
+
# @param id [::String]
|
53
65
|
# @param arguments [Array<Entity>]
|
54
66
|
def initialize(type, id: nil, arguments: [])
|
55
67
|
super(type, id:)
|
56
68
|
@arguments = arguments
|
57
69
|
end
|
58
70
|
|
59
|
-
# @return [
|
71
|
+
# @return [boolean]
|
60
72
|
def ==(other)
|
61
73
|
super && @arguments == other.arguments
|
62
74
|
end
|
@@ -6,24 +6,26 @@ module CSVPlusPlus
|
|
6
6
|
module Language
|
7
7
|
module Entities
|
8
8
|
# A function definition
|
9
|
+
#
|
10
|
+
# @attr_reader body [Entity] The body of the function. +body+ can contain variable references
|
11
|
+
# from +@arguments+
|
9
12
|
class Function < EntityWithArguments
|
10
13
|
attr_reader :body
|
11
14
|
|
12
|
-
# Create a function
|
13
15
|
# @param id [Symbool, String] the name of the function - what it will be callable by
|
14
|
-
# @param arguments [Array
|
16
|
+
# @param arguments [Array<Symbol>]
|
15
17
|
# @param body [Entity]
|
16
18
|
def initialize(id, arguments, body)
|
17
19
|
super(:function, id:, arguments: arguments.map(&:to_sym))
|
18
20
|
@body = body
|
19
21
|
end
|
20
22
|
|
21
|
-
#
|
23
|
+
# @return [String]
|
22
24
|
def to_s
|
23
25
|
"def #{@id.to_s.upcase}(#{arguments_to_s}) #{@body}"
|
24
26
|
end
|
25
27
|
|
26
|
-
#
|
28
|
+
# @return [boolean]
|
27
29
|
def ==(other)
|
28
30
|
super && @body == other.body
|
29
31
|
end
|
@@ -5,17 +5,18 @@ module CSVPlusPlus
|
|
5
5
|
module Entities
|
6
6
|
# A function call
|
7
7
|
class FunctionCall < EntityWithArguments
|
8
|
-
#
|
8
|
+
# @param id [String] The name of the function
|
9
|
+
# @param arguments [Array<Entity>] The arguments to the function
|
9
10
|
def initialize(id, arguments)
|
10
11
|
super(:function_call, id:, arguments:)
|
11
12
|
end
|
12
13
|
|
13
|
-
#
|
14
|
+
# @return [String]
|
14
15
|
def to_s
|
15
16
|
"#{@id.to_s.upcase}(#{arguments_to_s})"
|
16
17
|
end
|
17
18
|
|
18
|
-
#
|
19
|
+
# @return [boolean]
|
19
20
|
def ==(other)
|
20
21
|
super && @id == other.id
|
21
22
|
end
|
@@ -3,14 +3,16 @@
|
|
3
3
|
module CSVPlusPlus
|
4
4
|
module Language
|
5
5
|
module Entities
|
6
|
-
##
|
7
6
|
# A number value
|
7
|
+
#
|
8
|
+
# @attr_reader value [Numeric] The parsed number value
|
8
9
|
class Number < Entity
|
9
10
|
attr_reader :value
|
10
11
|
|
11
|
-
#
|
12
|
+
# @param value [String, Numeric] Either a +String+ that looks like a number, or an already parsed Numeric
|
12
13
|
def initialize(value)
|
13
14
|
super(:number)
|
15
|
+
|
14
16
|
@value =
|
15
17
|
if value.instance_of?(::String)
|
16
18
|
value.include?('.') ? Float(value) : Integer(value, 10)
|
@@ -19,12 +21,12 @@ module CSVPlusPlus
|
|
19
21
|
end
|
20
22
|
end
|
21
23
|
|
22
|
-
#
|
24
|
+
# @return [String]
|
23
25
|
def to_s
|
24
26
|
@value.to_s
|
25
27
|
end
|
26
28
|
|
27
|
-
#
|
29
|
+
# @return [boolean]
|
28
30
|
def ==(other)
|
29
31
|
super && value == other.value
|
30
32
|
end
|
@@ -3,21 +3,22 @@
|
|
3
3
|
module CSVPlusPlus
|
4
4
|
module Language
|
5
5
|
module Entities
|
6
|
-
|
7
|
-
# A runtime value
|
8
|
-
#
|
9
|
-
# These are values which can be materialized at any point via the +resolve_fn+
|
6
|
+
# A runtime value. These are values which can be materialized at any point via the +resolve_fn+
|
10
7
|
# which takes an ExecutionContext as a param
|
8
|
+
#
|
9
|
+
# @attr_reader resolve_fn [lambda] A lambda that is called when the runtime value is resolved
|
11
10
|
class RuntimeValue < Entity
|
12
|
-
attr_reader :resolve_fn
|
11
|
+
attr_reader :arguments, :resolve_fn
|
13
12
|
|
14
|
-
#
|
15
|
-
def initialize(resolve_fn)
|
13
|
+
# @param resolve_fn [lambda] A lambda that is called when the runtime value is resolved
|
14
|
+
def initialize(resolve_fn, arguments: nil)
|
16
15
|
super(:runtime_value)
|
16
|
+
|
17
|
+
@arguments = arguments
|
17
18
|
@resolve_fn = resolve_fn
|
18
19
|
end
|
19
20
|
|
20
|
-
#
|
21
|
+
# @return [String]
|
21
22
|
def to_s
|
22
23
|
'(runtime_value)'
|
23
24
|
end
|
@@ -3,23 +3,25 @@
|
|
3
3
|
module CSVPlusPlus
|
4
4
|
module Language
|
5
5
|
module Entities
|
6
|
-
##
|
7
6
|
# A string value
|
7
|
+
#
|
8
|
+
# @attr_reader value [String]
|
8
9
|
class String < Entity
|
9
10
|
attr_reader :value
|
10
11
|
|
11
|
-
#
|
12
|
+
# @param value [String] The string that has been parsed out of the template
|
12
13
|
def initialize(value)
|
13
14
|
super(:string)
|
15
|
+
|
14
16
|
@value = value.gsub(/^"|"$/, '')
|
15
17
|
end
|
16
18
|
|
17
|
-
#
|
19
|
+
# @return [String]
|
18
20
|
def to_s
|
19
21
|
"\"#{@value}\""
|
20
22
|
end
|
21
23
|
|
22
|
-
#
|
24
|
+
# @return [boolean]
|
23
25
|
def ==(other)
|
24
26
|
super && value == other.value
|
25
27
|
end
|
@@ -6,10 +6,19 @@ require_relative './scope'
|
|
6
6
|
module CSVPlusPlus
|
7
7
|
module Language
|
8
8
|
# References in an AST that need to be resolved
|
9
|
+
#
|
10
|
+
# @attr functions [Array<Entities::Function>] Functions references
|
11
|
+
# @attr variables [Array<Entities::Variable>] Variable references
|
9
12
|
class References
|
10
13
|
attr_accessor :functions, :variables
|
11
14
|
|
12
|
-
# Extract references from an AST
|
15
|
+
# Extract references from an AST and return them in a new +References+ object
|
16
|
+
#
|
17
|
+
# @param ast [Entity] An +Entity+ to do a depth first search on for references. Entities can be
|
18
|
+
# infinitely deep because they can contain other function calls as params to a function call
|
19
|
+
# @param code_section [CodeSection] The +CodeSection+ containing all currently defined functions
|
20
|
+
#
|
21
|
+
# @return [References]
|
13
22
|
def self.extract(ast, code_section)
|
14
23
|
new.tap do |refs|
|
15
24
|
::CSVPlusPlus::Graph.depth_first_search(ast) do |node|
|
@@ -22,8 +31,14 @@ module CSVPlusPlus
|
|
22
31
|
end
|
23
32
|
|
24
33
|
# Is the node a resolvable reference?
|
34
|
+
#
|
35
|
+
# @param node [Entity] The node to check if it's resolvable
|
36
|
+
#
|
37
|
+
# @return [boolean]
|
38
|
+
# TODO: move this into the Entity subclasses
|
25
39
|
def self.function_reference?(node, code_section)
|
26
|
-
node.function_call? && (code_section.defined_function?(node.id)
|
40
|
+
node.function_call? && (code_section.defined_function?(node.id) \
|
41
|
+
|| ::CSVPlusPlus::Language::Builtins::FUNCTIONS.key?(node.id))
|
27
42
|
end
|
28
43
|
|
29
44
|
private_class_method :function_reference?
|
@@ -34,17 +49,19 @@ module CSVPlusPlus
|
|
34
49
|
@variables = []
|
35
50
|
end
|
36
51
|
|
37
|
-
#
|
52
|
+
# Are there any references to be resolved?
|
53
|
+
#
|
54
|
+
# @return [boolean]
|
38
55
|
def empty?
|
39
56
|
@functions.empty? && @variables.empty?
|
40
57
|
end
|
41
58
|
|
42
|
-
#
|
59
|
+
# @return [String]
|
43
60
|
def to_s
|
44
61
|
"References(functions: #{@functions}, variables: #{@variables})"
|
45
62
|
end
|
46
63
|
|
47
|
-
#
|
64
|
+
# @return [boolean]
|
48
65
|
def ==(other)
|
49
66
|
@functions == other.functions && @variables == other.variables
|
50
67
|
end
|