csv_plus_plus 0.1.1 → 0.1.3
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/README.md +18 -63
- data/{CHANGELOG.md → docs/CHANGELOG.md} +17 -0
- data/lib/csv_plus_plus/benchmarked_compiler.rb +112 -0
- data/lib/csv_plus_plus/cell.rb +46 -24
- data/lib/csv_plus_plus/cli.rb +44 -17
- data/lib/csv_plus_plus/cli_flag.rb +1 -2
- data/lib/csv_plus_plus/color.rb +42 -11
- data/lib/csv_plus_plus/compiler.rb +178 -0
- data/lib/csv_plus_plus/entities/ast_builder.rb +50 -0
- data/lib/csv_plus_plus/entities/boolean.rb +40 -0
- data/lib/csv_plus_plus/entities/builtins.rb +58 -0
- data/lib/csv_plus_plus/entities/cell_reference.rb +231 -0
- data/lib/csv_plus_plus/entities/date.rb +63 -0
- data/lib/csv_plus_plus/entities/entity.rb +50 -0
- data/lib/csv_plus_plus/entities/entity_with_arguments.rb +57 -0
- data/lib/csv_plus_plus/entities/function.rb +45 -0
- data/lib/csv_plus_plus/entities/function_call.rb +50 -0
- data/lib/csv_plus_plus/entities/number.rb +48 -0
- data/lib/csv_plus_plus/entities/runtime_value.rb +43 -0
- data/lib/csv_plus_plus/entities/string.rb +42 -0
- data/lib/csv_plus_plus/entities/variable.rb +37 -0
- data/lib/csv_plus_plus/entities.rb +40 -0
- data/lib/csv_plus_plus/error/error.rb +20 -0
- data/lib/csv_plus_plus/error/formula_syntax_error.rb +37 -0
- data/lib/csv_plus_plus/error/modifier_syntax_error.rb +75 -0
- data/lib/csv_plus_plus/error/modifier_validation_error.rb +69 -0
- data/lib/csv_plus_plus/error/syntax_error.rb +71 -0
- data/lib/csv_plus_plus/error/writer_error.rb +17 -0
- data/lib/csv_plus_plus/error.rb +10 -2
- data/lib/csv_plus_plus/google_api_client.rb +11 -2
- data/lib/csv_plus_plus/google_options.rb +23 -18
- data/lib/csv_plus_plus/lexer/lexer.rb +17 -6
- data/lib/csv_plus_plus/lexer/tokenizer.rb +6 -1
- data/lib/csv_plus_plus/lexer.rb +24 -0
- data/lib/csv_plus_plus/modifier/conditional_formatting.rb +18 -0
- data/lib/csv_plus_plus/modifier/data_validation.rb +138 -0
- data/lib/csv_plus_plus/modifier/expand.rb +61 -0
- data/lib/csv_plus_plus/modifier/google_sheet_modifier.rb +133 -0
- data/lib/csv_plus_plus/modifier/modifier.rb +222 -0
- data/lib/csv_plus_plus/modifier/modifier_validator.rb +243 -0
- data/lib/csv_plus_plus/modifier/rubyxl_modifier.rb +84 -0
- data/lib/csv_plus_plus/modifier.rb +82 -150
- data/lib/csv_plus_plus/options.rb +64 -19
- data/lib/csv_plus_plus/{language → parser}/cell_value.tab.rb +25 -25
- data/lib/csv_plus_plus/{language → parser}/code_section.tab.rb +86 -95
- data/lib/csv_plus_plus/parser/modifier.tab.rb +478 -0
- data/lib/csv_plus_plus/row.rb +53 -15
- data/lib/csv_plus_plus/runtime/can_define_references.rb +87 -0
- data/lib/csv_plus_plus/runtime/can_resolve_references.rb +209 -0
- data/lib/csv_plus_plus/runtime/graph.rb +68 -0
- data/lib/csv_plus_plus/runtime/position_tracker.rb +231 -0
- data/lib/csv_plus_plus/runtime/references.rb +110 -0
- data/lib/csv_plus_plus/runtime/runtime.rb +126 -0
- data/lib/csv_plus_plus/runtime.rb +42 -0
- data/lib/csv_plus_plus/source_code.rb +66 -0
- data/lib/csv_plus_plus/template.rb +63 -36
- data/lib/csv_plus_plus/version.rb +2 -1
- data/lib/csv_plus_plus/writer/base_writer.rb +30 -5
- data/lib/csv_plus_plus/writer/csv.rb +11 -9
- data/lib/csv_plus_plus/writer/excel.rb +9 -2
- data/lib/csv_plus_plus/writer/file_backer_upper.rb +7 -4
- data/lib/csv_plus_plus/writer/google_sheet_builder.rb +88 -45
- data/lib/csv_plus_plus/writer/google_sheets.rb +79 -29
- data/lib/csv_plus_plus/writer/open_document.rb +6 -1
- data/lib/csv_plus_plus/writer/rubyxl_builder.rb +103 -33
- data/lib/csv_plus_plus/writer.rb +39 -9
- data/lib/csv_plus_plus.rb +41 -15
- metadata +44 -30
- data/lib/csv_plus_plus/code_section.rb +0 -101
- data/lib/csv_plus_plus/expand.rb +0 -18
- data/lib/csv_plus_plus/graph.rb +0 -62
- data/lib/csv_plus_plus/language/ast_builder.rb +0 -68
- data/lib/csv_plus_plus/language/benchmarked_compiler.rb +0 -65
- data/lib/csv_plus_plus/language/builtins.rb +0 -46
- data/lib/csv_plus_plus/language/compiler.rb +0 -152
- data/lib/csv_plus_plus/language/entities/boolean.rb +0 -33
- data/lib/csv_plus_plus/language/entities/cell_reference.rb +0 -33
- data/lib/csv_plus_plus/language/entities/entity.rb +0 -86
- data/lib/csv_plus_plus/language/entities/function.rb +0 -35
- data/lib/csv_plus_plus/language/entities/function_call.rb +0 -37
- data/lib/csv_plus_plus/language/entities/number.rb +0 -36
- data/lib/csv_plus_plus/language/entities/runtime_value.rb +0 -28
- data/lib/csv_plus_plus/language/entities/string.rb +0 -31
- data/lib/csv_plus_plus/language/entities/variable.rb +0 -25
- data/lib/csv_plus_plus/language/entities.rb +0 -28
- data/lib/csv_plus_plus/language/references.rb +0 -70
- data/lib/csv_plus_plus/language/runtime.rb +0 -205
- data/lib/csv_plus_plus/language/scope.rb +0 -192
- data/lib/csv_plus_plus/language/syntax_error.rb +0 -66
- data/lib/csv_plus_plus/modifier.tab.rb +0 -907
- data/lib/csv_plus_plus/writer/google_sheet_modifier.rb +0 -56
- data/lib/csv_plus_plus/writer/rubyxl_modifier.rb +0 -59
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'csv'
|
|
4
|
-
|
|
5
|
-
require_relative 'benchmarked_compiler'
|
|
6
|
-
require_relative 'code_section.tab'
|
|
7
|
-
require_relative 'entities'
|
|
8
|
-
require_relative 'runtime'
|
|
9
|
-
require_relative 'scope'
|
|
10
|
-
|
|
11
|
-
module CSVPlusPlus
|
|
12
|
-
module Language
|
|
13
|
-
# Encapsulates the parsing and building of objects (+Template+ -> +Row+ -> +Cell+). Variable resolution is delegated
|
|
14
|
-
# to the +Scope+
|
|
15
|
-
#
|
|
16
|
-
# @attr_reader options [Options] The +Options+ to compile with
|
|
17
|
-
# @attr_reader runtime [Runtime] The runtime execution
|
|
18
|
-
# @attr_reader scope [Scope] +Scope+ for variable resolution
|
|
19
|
-
class Compiler
|
|
20
|
-
attr_reader :timings, :benchmark, :options, :runtime, :scope
|
|
21
|
-
|
|
22
|
-
# Create a compiler and make sure it gets cleaned up
|
|
23
|
-
#
|
|
24
|
-
# @param runtime [Runtime] The initial +Runtime+ for the compiler
|
|
25
|
-
# @param options [Options]
|
|
26
|
-
def self.with_compiler(runtime:, options:, &block)
|
|
27
|
-
compiler = new(options:, runtime:)
|
|
28
|
-
if options.verbose
|
|
29
|
-
::CSVPlusPlus::Language::BenchmarkedCompiler.with_benchmarks(compiler) do |c|
|
|
30
|
-
block.call(c)
|
|
31
|
-
end
|
|
32
|
-
else
|
|
33
|
-
yield(compiler)
|
|
34
|
-
end
|
|
35
|
-
ensure
|
|
36
|
-
runtime.cleanup!
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
# @param runtime [Runtime]
|
|
40
|
-
# @param options [Options]
|
|
41
|
-
# @param scope [Scope, nil]
|
|
42
|
-
def initialize(runtime:, options:, scope: nil)
|
|
43
|
-
@options = options
|
|
44
|
-
@runtime = runtime
|
|
45
|
-
@scope = scope || ::CSVPlusPlus::Language::Scope.new(runtime:)
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
# Write the compiled results
|
|
49
|
-
def outputting!
|
|
50
|
-
@runtime.start_at_csv!
|
|
51
|
-
yield
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
# Compile a template and return a +::CSVPlusPlus::Template+ instance ready to be written with a +Writer+
|
|
55
|
-
#
|
|
56
|
-
# @return [Template]
|
|
57
|
-
def compile_template
|
|
58
|
-
parse_code_section!
|
|
59
|
-
rows = parse_csv_section!
|
|
60
|
-
|
|
61
|
-
::CSVPlusPlus::Template.new(rows:, code_section: scope.code_section).tap do |t|
|
|
62
|
-
t.validate_infinite_expands(@runtime)
|
|
63
|
-
expanding { t.expand_rows! }
|
|
64
|
-
resolve_all_cells!(t)
|
|
65
|
-
end
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
# @return [String]
|
|
69
|
-
def to_s
|
|
70
|
-
"Compiler(options: #{@options}, runtime: #{@runtime}, scope: #{@scope})"
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
protected
|
|
74
|
-
|
|
75
|
-
# Parses the input file and returns a +CodeSection+
|
|
76
|
-
#
|
|
77
|
-
# @return [CodeSection]
|
|
78
|
-
def parse_code_section!
|
|
79
|
-
@runtime.start!
|
|
80
|
-
parsing_code_section do |input|
|
|
81
|
-
code_section, csv_section = ::CSVPlusPlus::Language::CodeSectionParser.new.parse(input, @runtime)
|
|
82
|
-
# TODO: infer a type
|
|
83
|
-
# allow user-supplied key/values to override anything global or from the code section
|
|
84
|
-
code_section.def_variables(
|
|
85
|
-
options.key_values.transform_values { |v| ::CSVPlusPlus::Language::Entities::String.new(v.to_s) }
|
|
86
|
-
)
|
|
87
|
-
@scope.code_section = code_section
|
|
88
|
-
|
|
89
|
-
# return the csv_section to the caller because they're gonna re-write input with it
|
|
90
|
-
next csv_section
|
|
91
|
-
end
|
|
92
|
-
@scope.code_section
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
# Parse the CSV section and return an array of +Row+s
|
|
96
|
-
#
|
|
97
|
-
# @return [Array<Row>]
|
|
98
|
-
def parse_csv_section!
|
|
99
|
-
@runtime.start_at_csv!
|
|
100
|
-
@runtime.map_rows(::CSV.new(runtime.input)) do |csv_row|
|
|
101
|
-
parse_row(csv_row)
|
|
102
|
-
end
|
|
103
|
-
ensure
|
|
104
|
-
# we're done with the file and everything is in memory
|
|
105
|
-
@runtime.cleanup!
|
|
106
|
-
end
|
|
107
|
-
|
|
108
|
-
# Iterates through each cell of each row and resolves it's variable and function references.
|
|
109
|
-
#
|
|
110
|
-
# @param template [Template]
|
|
111
|
-
# @return [Array<Entity>]
|
|
112
|
-
def resolve_all_cells!(template)
|
|
113
|
-
@runtime.start_at_csv!
|
|
114
|
-
@runtime.map_rows(template.rows, cells_too: true) do |cell|
|
|
115
|
-
cell.ast = @scope.resolve_cell_value if cell.ast
|
|
116
|
-
end
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
# Expanding rows
|
|
120
|
-
def expanding
|
|
121
|
-
@runtime.start_at_csv!
|
|
122
|
-
yield
|
|
123
|
-
end
|
|
124
|
-
|
|
125
|
-
private
|
|
126
|
-
|
|
127
|
-
def parsing_code_section
|
|
128
|
-
csv_section = yield(@runtime.input.read)
|
|
129
|
-
@runtime.rewrite_input!(csv_section)
|
|
130
|
-
end
|
|
131
|
-
|
|
132
|
-
# Using the current +@runtime+ and the given +csv_row+ parse it into a +Row+ of +Cell+s
|
|
133
|
-
# +csv_row+ should have already been run through a CSV parser and is an array of strings
|
|
134
|
-
#
|
|
135
|
-
# @param csv_row [Array<Array<String>>]
|
|
136
|
-
# @return [Row]
|
|
137
|
-
def parse_row(csv_row)
|
|
138
|
-
row_modifier = ::CSVPlusPlus::Modifier.new(row_level: true)
|
|
139
|
-
|
|
140
|
-
cells =
|
|
141
|
-
@runtime.map_row(csv_row) do |value, _cell_index|
|
|
142
|
-
cell_modifier = ::CSVPlusPlus::Modifier.new
|
|
143
|
-
parsed_value = ::CSVPlusPlus::ModifierParser.new(row_modifier:, cell_modifier:).parse(value, @runtime)
|
|
144
|
-
|
|
145
|
-
::CSVPlusPlus::Cell.parse(parsed_value, runtime:, modifier: cell_modifier)
|
|
146
|
-
end
|
|
147
|
-
|
|
148
|
-
::CSVPlusPlus::Row.new(@runtime.row_index, cells, row_modifier)
|
|
149
|
-
end
|
|
150
|
-
end
|
|
151
|
-
end
|
|
152
|
-
end
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative './entity'
|
|
4
|
-
|
|
5
|
-
module CSVPlusPlus
|
|
6
|
-
module Language
|
|
7
|
-
module Entities
|
|
8
|
-
# A boolean value
|
|
9
|
-
#
|
|
10
|
-
# @attr_reader value [true, false]
|
|
11
|
-
class Boolean < Entity
|
|
12
|
-
attr_reader :value
|
|
13
|
-
|
|
14
|
-
# @param value [String, Boolean]
|
|
15
|
-
def initialize(value)
|
|
16
|
-
super(:boolean)
|
|
17
|
-
# TODO: probably can do a lot better in general on type validation
|
|
18
|
-
@value = value.is_a?(::String) ? (value.downcase == 'true') : value
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
# @return [String]
|
|
22
|
-
def to_s
|
|
23
|
-
@value.to_s.upcase
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
# @return [boolean]
|
|
27
|
-
def ==(other)
|
|
28
|
-
super && value == other.value
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
end
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative './entity'
|
|
4
|
-
|
|
5
|
-
module CSVPlusPlus
|
|
6
|
-
module Language
|
|
7
|
-
module Entities
|
|
8
|
-
# A reference to a cell
|
|
9
|
-
#
|
|
10
|
-
# @attr_reader cell_reference [String] The cell reference in A1 format
|
|
11
|
-
class CellReference < Entity
|
|
12
|
-
attr_reader :cell_reference
|
|
13
|
-
|
|
14
|
-
# @param cell_reference [String] The cell reference in A1 format
|
|
15
|
-
def initialize(cell_reference)
|
|
16
|
-
super(:cell_reference)
|
|
17
|
-
|
|
18
|
-
@cell_reference = cell_reference
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
# @return [String]
|
|
22
|
-
def to_s
|
|
23
|
-
@cell_reference
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
# @return [Boolean]
|
|
27
|
-
def ==(other)
|
|
28
|
-
super && @cell_reference == other.cell_reference
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
end
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative '../entities'
|
|
4
|
-
|
|
5
|
-
module CSVPlusPlus
|
|
6
|
-
module Language
|
|
7
|
-
module Entities
|
|
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+
|
|
13
|
-
class Entity
|
|
14
|
-
attr_reader :id, :type
|
|
15
|
-
|
|
16
|
-
# @param type [::String, Symbol]
|
|
17
|
-
# @param id [::String, nil]
|
|
18
|
-
def initialize(type, id: nil)
|
|
19
|
-
@type = type.to_sym
|
|
20
|
-
@id = id.downcase.to_sym if id
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
# @return [boolean]
|
|
24
|
-
def ==(other)
|
|
25
|
-
self.class == other.class && @type == other.type && @id == other.id
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
# Respond to predicates that correspond to types like #boolean?, #string?, etc
|
|
29
|
-
#
|
|
30
|
-
# @param method_name [Symbol] The +method_name+ to respond to
|
|
31
|
-
def method_missing(method_name, *_arguments)
|
|
32
|
-
if method_name =~ /^(\w+)\?$/
|
|
33
|
-
t = ::Regexp.last_match(1)
|
|
34
|
-
a_type?(t) && @type == t.to_sym
|
|
35
|
-
else
|
|
36
|
-
super
|
|
37
|
-
end
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
# Respond to predicates by type (entity.boolean?, entity.string?, etc)
|
|
41
|
-
#
|
|
42
|
-
# @param method_name [Symbol] The +method_name+ to respond to
|
|
43
|
-
#
|
|
44
|
-
# @return [boolean]
|
|
45
|
-
def respond_to_missing?(method_name, *_arguments)
|
|
46
|
-
(method_name =~ /^(\w+)\?$/ && a_type?(::Regexp.last_match(1))) || super
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
private
|
|
50
|
-
|
|
51
|
-
def a_type?(str)
|
|
52
|
-
::CSVPlusPlus::Language::TYPES.include?(str.to_sym)
|
|
53
|
-
end
|
|
54
|
-
end
|
|
55
|
-
|
|
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.
|
|
60
|
-
class EntityWithArguments < Entity
|
|
61
|
-
attr_reader :arguments
|
|
62
|
-
|
|
63
|
-
# @param type [::String, Symbol]
|
|
64
|
-
# @param id [::String]
|
|
65
|
-
# @param arguments [Array<Entity>]
|
|
66
|
-
def initialize(type, id: nil, arguments: [])
|
|
67
|
-
super(type, id:)
|
|
68
|
-
@arguments = arguments
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
# @return [boolean]
|
|
72
|
-
def ==(other)
|
|
73
|
-
super && @arguments == other.arguments
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
protected
|
|
77
|
-
|
|
78
|
-
attr_writer :arguments
|
|
79
|
-
|
|
80
|
-
def arguments_to_s
|
|
81
|
-
@arguments.join(', ')
|
|
82
|
-
end
|
|
83
|
-
end
|
|
84
|
-
end
|
|
85
|
-
end
|
|
86
|
-
end
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative './entity'
|
|
4
|
-
|
|
5
|
-
module CSVPlusPlus
|
|
6
|
-
module Language
|
|
7
|
-
module Entities
|
|
8
|
-
# A function definition
|
|
9
|
-
#
|
|
10
|
-
# @attr_reader body [Entity] The body of the function. +body+ can contain variable references
|
|
11
|
-
# from +@arguments+
|
|
12
|
-
class Function < EntityWithArguments
|
|
13
|
-
attr_reader :body
|
|
14
|
-
|
|
15
|
-
# @param id [Symbool, String] the name of the function - what it will be callable by
|
|
16
|
-
# @param arguments [Array<Symbol>]
|
|
17
|
-
# @param body [Entity]
|
|
18
|
-
def initialize(id, arguments, body)
|
|
19
|
-
super(:function, id:, arguments: arguments.map(&:to_sym))
|
|
20
|
-
@body = body
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
# @return [String]
|
|
24
|
-
def to_s
|
|
25
|
-
"def #{@id.to_s.upcase}(#{arguments_to_s}) #{@body}"
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
# @return [boolean]
|
|
29
|
-
def ==(other)
|
|
30
|
-
super && @body == other.body
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
end
|
|
35
|
-
end
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module CSVPlusPlus
|
|
4
|
-
module Language
|
|
5
|
-
module Entities
|
|
6
|
-
# A function call
|
|
7
|
-
#
|
|
8
|
-
# @attr_reader infix [boolean] Whether or not this function call is infix (X * Y, A + B, etc)
|
|
9
|
-
class FunctionCall < EntityWithArguments
|
|
10
|
-
attr_reader :infix
|
|
11
|
-
|
|
12
|
-
# @param id [String] The name of the function
|
|
13
|
-
# @param arguments [Array<Entity>] The arguments to the function
|
|
14
|
-
# @param infix [boolean] Whether the function is infix
|
|
15
|
-
def initialize(id, arguments, infix: false)
|
|
16
|
-
super(:function_call, id:, arguments:)
|
|
17
|
-
|
|
18
|
-
@infix = infix
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
# @return [String]
|
|
22
|
-
def to_s
|
|
23
|
-
if @infix
|
|
24
|
-
"(#{arguments.join(" #{@id} ")})"
|
|
25
|
-
else
|
|
26
|
-
"#{@id.to_s.upcase}(#{arguments_to_s})"
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
# @return [boolean]
|
|
31
|
-
def ==(other)
|
|
32
|
-
super && @id == other.id
|
|
33
|
-
end
|
|
34
|
-
end
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
|
-
end
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module CSVPlusPlus
|
|
4
|
-
module Language
|
|
5
|
-
module Entities
|
|
6
|
-
# A number value
|
|
7
|
-
#
|
|
8
|
-
# @attr_reader value [Numeric] The parsed number value
|
|
9
|
-
class Number < Entity
|
|
10
|
-
attr_reader :value
|
|
11
|
-
|
|
12
|
-
# @param value [String, Numeric] Either a +String+ that looks like a number, or an already parsed Numeric
|
|
13
|
-
def initialize(value)
|
|
14
|
-
super(:number)
|
|
15
|
-
|
|
16
|
-
@value =
|
|
17
|
-
if value.instance_of?(::String)
|
|
18
|
-
value.include?('.') ? Float(value) : Integer(value, 10)
|
|
19
|
-
else
|
|
20
|
-
value
|
|
21
|
-
end
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
# @return [String]
|
|
25
|
-
def to_s
|
|
26
|
-
@value.to_s
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
# @return [boolean]
|
|
30
|
-
def ==(other)
|
|
31
|
-
super && value == other.value
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
end
|
|
35
|
-
end
|
|
36
|
-
end
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module CSVPlusPlus
|
|
4
|
-
module Language
|
|
5
|
-
module Entities
|
|
6
|
-
# A runtime value. These are values which can be materialized at any point via the +resolve_fn+
|
|
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
|
|
10
|
-
class RuntimeValue < Entity
|
|
11
|
-
attr_reader :arguments, :resolve_fn
|
|
12
|
-
|
|
13
|
-
# @param resolve_fn [lambda] A lambda that is called when the runtime value is resolved
|
|
14
|
-
def initialize(resolve_fn, arguments: nil)
|
|
15
|
-
super(:runtime_value)
|
|
16
|
-
|
|
17
|
-
@arguments = arguments
|
|
18
|
-
@resolve_fn = resolve_fn
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
# @return [String]
|
|
22
|
-
def to_s
|
|
23
|
-
'(runtime_value)'
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
end
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module CSVPlusPlus
|
|
4
|
-
module Language
|
|
5
|
-
module Entities
|
|
6
|
-
# A string value
|
|
7
|
-
#
|
|
8
|
-
# @attr_reader value [String]
|
|
9
|
-
class String < Entity
|
|
10
|
-
attr_reader :value
|
|
11
|
-
|
|
12
|
-
# @param value [String] The string that has been parsed out of the template
|
|
13
|
-
def initialize(value)
|
|
14
|
-
super(:string)
|
|
15
|
-
|
|
16
|
-
@value = value.gsub(/^"|"$/, '')
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
# @return [String]
|
|
20
|
-
def to_s
|
|
21
|
-
"\"#{@value}\""
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
# @return [boolean]
|
|
25
|
-
def ==(other)
|
|
26
|
-
super && value == other.value
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
end
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module CSVPlusPlus
|
|
4
|
-
module Language
|
|
5
|
-
module Entities
|
|
6
|
-
# A reference to a variable
|
|
7
|
-
class Variable < Entity
|
|
8
|
-
# initialize
|
|
9
|
-
def initialize(id)
|
|
10
|
-
super(:variable, id:)
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
# to_s
|
|
14
|
-
def to_s
|
|
15
|
-
"$$#{@id}"
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
# ==
|
|
19
|
-
def ==(other)
|
|
20
|
-
super && id == other.id
|
|
21
|
-
end
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
end
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative 'entities/boolean'
|
|
4
|
-
require_relative 'entities/cell_reference'
|
|
5
|
-
require_relative 'entities/entity'
|
|
6
|
-
require_relative 'entities/function'
|
|
7
|
-
require_relative 'entities/function_call'
|
|
8
|
-
require_relative 'entities/number'
|
|
9
|
-
require_relative 'entities/runtime_value'
|
|
10
|
-
require_relative 'entities/string'
|
|
11
|
-
require_relative 'entities/variable'
|
|
12
|
-
|
|
13
|
-
module CSVPlusPlus
|
|
14
|
-
module Language
|
|
15
|
-
TYPES = {
|
|
16
|
-
boolean: ::CSVPlusPlus::Language::Entities::Boolean,
|
|
17
|
-
cell_reference: ::CSVPlusPlus::Language::Entities::CellReference,
|
|
18
|
-
function: ::CSVPlusPlus::Language::Entities::Function,
|
|
19
|
-
function_call: ::CSVPlusPlus::Language::Entities::FunctionCall,
|
|
20
|
-
number: ::CSVPlusPlus::Language::Entities::Number,
|
|
21
|
-
runtime_value: ::CSVPlusPlus::Language::Entities::RuntimeValue,
|
|
22
|
-
string: ::CSVPlusPlus::Language::Entities::String,
|
|
23
|
-
variable: ::CSVPlusPlus::Language::Entities::Variable
|
|
24
|
-
}.freeze
|
|
25
|
-
|
|
26
|
-
public_constant :TYPES
|
|
27
|
-
end
|
|
28
|
-
end
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative '../graph'
|
|
4
|
-
require_relative './scope'
|
|
5
|
-
|
|
6
|
-
module CSVPlusPlus
|
|
7
|
-
module Language
|
|
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
|
|
12
|
-
class References
|
|
13
|
-
attr_accessor :functions, :variables
|
|
14
|
-
|
|
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]
|
|
22
|
-
def self.extract(ast, code_section)
|
|
23
|
-
new.tap do |refs|
|
|
24
|
-
::CSVPlusPlus::Graph.depth_first_search(ast) do |node|
|
|
25
|
-
next unless node.function_call? || node.variable?
|
|
26
|
-
|
|
27
|
-
refs.functions << node if function_reference?(node, code_section)
|
|
28
|
-
refs.variables << node if node.variable?
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
|
|
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
|
|
39
|
-
def self.function_reference?(node, code_section)
|
|
40
|
-
node.function_call? && (code_section.defined_function?(node.id) \
|
|
41
|
-
|| ::CSVPlusPlus::Language::Builtins::FUNCTIONS.key?(node.id))
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
private_class_method :function_reference?
|
|
45
|
-
|
|
46
|
-
# Create an object with empty references. The caller will build them up as it depth-first-searches
|
|
47
|
-
def initialize
|
|
48
|
-
@functions = []
|
|
49
|
-
@variables = []
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
# Are there any references to be resolved?
|
|
53
|
-
#
|
|
54
|
-
# @return [boolean]
|
|
55
|
-
def empty?
|
|
56
|
-
@functions.empty? && @variables.empty?
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
# @return [String]
|
|
60
|
-
def to_s
|
|
61
|
-
"References(functions: #{@functions}, variables: #{@variables})"
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
# @return [boolean]
|
|
65
|
-
def ==(other)
|
|
66
|
-
@functions == other.functions && @variables == other.variables
|
|
67
|
-
end
|
|
68
|
-
end
|
|
69
|
-
end
|
|
70
|
-
end
|