csv_plus_plus 0.1.3 → 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/README.md +8 -3
- data/docs/CHANGELOG.md +16 -0
- data/lib/csv_plus_plus/a1_reference.rb +202 -0
- data/lib/csv_plus_plus/benchmarked_compiler.rb +3 -3
- data/lib/csv_plus_plus/cell.rb +1 -35
- data/lib/csv_plus_plus/cli.rb +43 -80
- data/lib/csv_plus_plus/cli_flag.rb +71 -70
- data/lib/csv_plus_plus/color.rb +1 -1
- data/lib/csv_plus_plus/compiler.rb +31 -21
- data/lib/csv_plus_plus/entities/ast_builder.rb +11 -4
- data/lib/csv_plus_plus/entities/boolean.rb +16 -9
- data/lib/csv_plus_plus/entities/builtins.rb +68 -40
- data/lib/csv_plus_plus/entities/date.rb +14 -11
- data/lib/csv_plus_plus/entities/entity.rb +11 -29
- data/lib/csv_plus_plus/entities/entity_with_arguments.rb +18 -31
- data/lib/csv_plus_plus/entities/function.rb +22 -11
- data/lib/csv_plus_plus/entities/function_call.rb +35 -11
- data/lib/csv_plus_plus/entities/has_identifier.rb +19 -0
- data/lib/csv_plus_plus/entities/number.rb +15 -10
- data/lib/csv_plus_plus/entities/reference.rb +77 -0
- data/lib/csv_plus_plus/entities/runtime_value.rb +36 -23
- data/lib/csv_plus_plus/entities/string.rb +13 -10
- data/lib/csv_plus_plus/entities.rb +2 -18
- data/lib/csv_plus_plus/error/cli_error.rb +17 -0
- data/lib/csv_plus_plus/error/compiler_error.rb +17 -0
- data/lib/csv_plus_plus/error/error.rb +18 -5
- data/lib/csv_plus_plus/error/formula_syntax_error.rb +12 -13
- data/lib/csv_plus_plus/error/modifier_syntax_error.rb +10 -36
- data/lib/csv_plus_plus/error/modifier_validation_error.rb +6 -32
- data/lib/csv_plus_plus/error/positional_error.rb +15 -0
- data/lib/csv_plus_plus/error/writer_error.rb +1 -1
- data/lib/csv_plus_plus/error.rb +4 -1
- data/lib/csv_plus_plus/error_formatter.rb +111 -0
- data/lib/csv_plus_plus/google_api_client.rb +18 -8
- data/lib/csv_plus_plus/lexer/racc_lexer.rb +144 -0
- data/lib/csv_plus_plus/lexer/tokenizer.rb +53 -17
- data/lib/csv_plus_plus/lexer.rb +40 -1
- data/lib/csv_plus_plus/modifier/data_validation.rb +1 -1
- data/lib/csv_plus_plus/modifier/expand.rb +17 -0
- data/lib/csv_plus_plus/modifier.rb +6 -1
- data/lib/csv_plus_plus/options/file_options.rb +49 -0
- data/lib/csv_plus_plus/options/google_sheets_options.rb +42 -0
- data/lib/csv_plus_plus/options/options.rb +97 -0
- data/lib/csv_plus_plus/options.rb +22 -110
- data/lib/csv_plus_plus/parser/cell_value.tab.rb +65 -66
- data/lib/csv_plus_plus/parser/code_section.tab.rb +92 -84
- data/lib/csv_plus_plus/parser/modifier.tab.rb +40 -30
- data/lib/csv_plus_plus/reader/csv.rb +50 -0
- data/lib/csv_plus_plus/reader/google_sheets.rb +129 -0
- data/lib/csv_plus_plus/reader/reader.rb +27 -0
- data/lib/csv_plus_plus/reader/rubyxl.rb +37 -0
- data/lib/csv_plus_plus/reader.rb +14 -0
- data/lib/csv_plus_plus/runtime/graph.rb +6 -6
- data/lib/csv_plus_plus/runtime/{position_tracker.rb → position.rb} +16 -5
- data/lib/csv_plus_plus/runtime/references.rb +32 -27
- data/lib/csv_plus_plus/runtime/runtime.rb +73 -67
- data/lib/csv_plus_plus/runtime/scope.rb +280 -0
- data/lib/csv_plus_plus/runtime.rb +9 -9
- data/lib/csv_plus_plus/source_code.rb +14 -9
- data/lib/csv_plus_plus/template.rb +17 -12
- data/lib/csv_plus_plus/version.rb +1 -1
- data/lib/csv_plus_plus/writer/csv.rb +32 -5
- data/lib/csv_plus_plus/writer/excel.rb +19 -6
- data/lib/csv_plus_plus/writer/file_backer_upper.rb +27 -14
- data/lib/csv_plus_plus/writer/google_sheets.rb +23 -129
- data/lib/csv_plus_plus/writer/{google_sheet_builder.rb → google_sheets_builder.rb} +39 -55
- data/lib/csv_plus_plus/writer/merger.rb +31 -0
- data/lib/csv_plus_plus/writer/open_document.rb +16 -2
- data/lib/csv_plus_plus/writer/rubyxl_builder.rb +68 -43
- data/lib/csv_plus_plus/writer/writer.rb +42 -0
- data/lib/csv_plus_plus/writer.rb +58 -19
- data/lib/csv_plus_plus.rb +26 -14
- metadata +37 -12
- data/lib/csv_plus_plus/entities/cell_reference.rb +0 -231
- data/lib/csv_plus_plus/entities/variable.rb +0 -37
- data/lib/csv_plus_plus/error/syntax_error.rb +0 -71
- data/lib/csv_plus_plus/google_options.rb +0 -32
- data/lib/csv_plus_plus/lexer/lexer.rb +0 -89
- data/lib/csv_plus_plus/runtime/can_define_references.rb +0 -87
- data/lib/csv_plus_plus/runtime/can_resolve_references.rb +0 -209
- data/lib/csv_plus_plus/writer/base_writer.rb +0 -45
@@ -0,0 +1,37 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module CSVPlusPlus
|
5
|
+
module Reader
|
6
|
+
# Reads an Excel file
|
7
|
+
class RubyXL < ::CSVPlusPlus::Reader::Reader
|
8
|
+
extend ::T::Sig
|
9
|
+
extend ::T::Generic
|
10
|
+
|
11
|
+
CellValue = type_member { { fixed: ::RubyXL::Cell } }
|
12
|
+
public_constant :CellValue
|
13
|
+
|
14
|
+
sig { params(options: ::CSVPlusPlus::Options::FileOptions, worksheet: ::RubyXL::Worksheet).void }
|
15
|
+
# Open an excel outputter to the +output_filename+ specified by the +Options+
|
16
|
+
#
|
17
|
+
# @param options [Options] The supplied options.
|
18
|
+
# @param worksheet [RubyXL::Worksheet] The already-opened RubyXL worksheet
|
19
|
+
def initialize(options, worksheet)
|
20
|
+
super()
|
21
|
+
|
22
|
+
@options = options
|
23
|
+
@worksheet = worksheet
|
24
|
+
end
|
25
|
+
|
26
|
+
sig { override.params(cell: ::CSVPlusPlus::Cell).returns(::T.nilable(::CSVPlusPlus::Reader::RubyXL::CellValue)) }
|
27
|
+
# Get the current value at the +cell+'s position
|
28
|
+
#
|
29
|
+
# @param cell [Cell]
|
30
|
+
#
|
31
|
+
# @return [RubyXL::Cell, nil]
|
32
|
+
def value_at(cell)
|
33
|
+
@worksheet.sheet_data[cell.row_index]&.[](cell.index)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module CSVPlusPlus
|
5
|
+
# Classes which can read spreadsheets in our various formats.
|
6
|
+
module Reader
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
require_relative './reader/reader'
|
11
|
+
|
12
|
+
require_relative './reader/csv'
|
13
|
+
require_relative './reader/google_sheets'
|
14
|
+
require_relative './reader/rubyxl'
|
@@ -9,18 +9,18 @@ module CSVPlusPlus
|
|
9
9
|
module Graph
|
10
10
|
# Get a list of all variables references in a given +ast+
|
11
11
|
# TODO: this is only used in one place - refactor it
|
12
|
-
def self.variable_references(ast,
|
12
|
+
def self.variable_references(ast, include_runtime_variables: false)
|
13
13
|
depth_first_search(ast) do |node|
|
14
|
-
next unless node.
|
14
|
+
next unless node.is_a?(::CSVPlusPlus::Entities::Reference)
|
15
15
|
|
16
|
-
node.id if
|
16
|
+
node.id if !::CSVPlusPlus::Entities::Builtins.builtin_variable?(node.id) || include_runtime_variables
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
20
20
|
# Create a dependency graph of +variables+
|
21
|
-
def self.dependency_graph(variables
|
21
|
+
def self.dependency_graph(variables)
|
22
22
|
::CSVPlusPlus::Runtime::Graph::DependencyGraph[
|
23
|
-
variables.map { |var_id, ast| [var_id, variable_references(ast
|
23
|
+
variables.map { |var_id, ast| [var_id, variable_references(ast)] }
|
24
24
|
]
|
25
25
|
end
|
26
26
|
|
@@ -47,7 +47,7 @@ module CSVPlusPlus
|
|
47
47
|
ret = yield(node)
|
48
48
|
accum << ret unless ret.nil?
|
49
49
|
|
50
|
-
return accum unless node.
|
50
|
+
return accum unless node.is_a?(::CSVPlusPlus::Entities::FunctionCall)
|
51
51
|
|
52
52
|
node.arguments.each { |n| depth_first_search(n, accum, &) }
|
53
53
|
accum
|
@@ -3,9 +3,15 @@
|
|
3
3
|
|
4
4
|
module CSVPlusPlus
|
5
5
|
module Runtime
|
6
|
-
#
|
7
|
-
#
|
8
|
-
|
6
|
+
# Keeps track of the position in a file where the parser is. The parser makes various passes over the input but
|
7
|
+
# it always needs to track the same things (line number, cell/row index, current cell)
|
8
|
+
#
|
9
|
+
# @attr cell [Cell] The current cell being processed
|
10
|
+
# @attr cell_index [Integer] The index of the current cell being processed (starts at 0)
|
11
|
+
# @attr row_index [Integer] The index of the current row being processed (starts at 0)
|
12
|
+
# @attr line_number [Integer] The line number of the original csvpp template (starts at 1)
|
13
|
+
# rubocop:disable Metrics/ClassLength
|
14
|
+
class Position
|
9
15
|
extend ::T::Sig
|
10
16
|
|
11
17
|
sig { params(cell: ::T.nilable(::CSVPlusPlus::Cell)).returns(::T.nilable(::CSVPlusPlus::Cell)) }
|
@@ -20,6 +26,12 @@ module CSVPlusPlus
|
|
20
26
|
sig { params(row_index: ::T.nilable(::Integer)).returns(::T.nilable(::Integer)) }
|
21
27
|
attr_writer :row_index
|
22
28
|
|
29
|
+
sig { params(input: ::String).void }
|
30
|
+
# @param input [String]
|
31
|
+
def initialize(input)
|
32
|
+
rewrite_input!(::CSVPlusPlus::Lexer.preprocess(input))
|
33
|
+
end
|
34
|
+
|
23
35
|
sig { returns(::CSVPlusPlus::Cell) }
|
24
36
|
# The current cell index. This will only be set when processing the CSV section
|
25
37
|
#
|
@@ -179,7 +191,6 @@ module CSVPlusPlus
|
|
179
191
|
# Each time we run a parse on the input, reset the runtime state starting at the beginning of the file
|
180
192
|
def start!(&block)
|
181
193
|
@row_index = @cell_index = 0
|
182
|
-
self.line_number = 1
|
183
194
|
|
184
195
|
ret = block.call
|
185
196
|
finish!
|
@@ -226,6 +237,6 @@ module CSVPlusPlus
|
|
226
237
|
self.line_number += 1
|
227
238
|
end
|
228
239
|
end
|
229
|
-
# rubocop:enable Metrics/
|
240
|
+
# rubocop:enable Metrics/ClassLength
|
230
241
|
end
|
231
242
|
end
|
@@ -7,79 +7,84 @@ module CSVPlusPlus
|
|
7
7
|
#
|
8
8
|
# @attr functions [Array<Entities::Function>] Functions references
|
9
9
|
# @attr variables [Array<Entities::Variable>] Variable references
|
10
|
-
# TODO: turn this into a CanExtractReferences?
|
11
10
|
class References
|
12
11
|
extend ::T::Sig
|
13
12
|
|
14
13
|
sig { returns(::T::Array[::CSVPlusPlus::Entities::FunctionCall]) }
|
15
14
|
attr_accessor :functions
|
16
15
|
|
17
|
-
sig { returns(::T::Array[::CSVPlusPlus::Entities::
|
16
|
+
sig { returns(::T::Array[::CSVPlusPlus::Entities::Reference]) }
|
18
17
|
attr_accessor :variables
|
19
18
|
|
20
19
|
sig do
|
21
20
|
params(
|
22
21
|
ast: ::CSVPlusPlus::Entities::Entity,
|
23
|
-
|
22
|
+
position: ::CSVPlusPlus::Runtime::Position,
|
23
|
+
scope: ::CSVPlusPlus::Runtime::Scope
|
24
24
|
).returns(::CSVPlusPlus::Runtime::References)
|
25
25
|
end
|
26
26
|
# Extract references from an AST and return them in a new +References+ object
|
27
27
|
#
|
28
28
|
# @param ast [Entity] An +Entity+ to do a depth first search on for references. Entities can be
|
29
29
|
# infinitely deep because they can contain other function calls as params to a function call
|
30
|
-
# @param
|
30
|
+
# @param scope [Scope] The current scope
|
31
31
|
#
|
32
32
|
# @return [References]
|
33
|
-
def self.extract(ast,
|
33
|
+
def self.extract(ast, position, scope)
|
34
34
|
new.tap do |refs|
|
35
35
|
::CSVPlusPlus::Runtime::Graph.depth_first_search(ast) do |node|
|
36
|
-
unless node.
|
37
|
-
|| node.type == ::CSVPlusPlus::Entities::Type::Variable
|
38
|
-
|
36
|
+
unless node.is_a?(::CSVPlusPlus::Entities::FunctionCall) || node.is_a?(::CSVPlusPlus::Entities::Reference)
|
39
37
|
next
|
40
38
|
end
|
41
39
|
|
42
|
-
refs.functions << node if function_reference?(node,
|
43
|
-
refs.variables << node if variable_reference?(node,
|
40
|
+
refs.functions << node if function_reference?(node, scope)
|
41
|
+
refs.variables << node if variable_reference?(node, position, scope)
|
44
42
|
end
|
45
43
|
end
|
46
44
|
end
|
47
45
|
|
48
46
|
sig do
|
49
|
-
params(
|
47
|
+
params(
|
48
|
+
node: ::CSVPlusPlus::Entities::Entity,
|
49
|
+
position: ::CSVPlusPlus::Runtime::Position,
|
50
|
+
scope: ::CSVPlusPlus::Runtime::Scope
|
51
|
+
).returns(::T::Boolean)
|
50
52
|
end
|
51
53
|
# Is the node a resolvable variable reference?
|
52
54
|
#
|
53
55
|
# @param node [Entity] The node to check if it's resolvable
|
54
|
-
# @param
|
56
|
+
# @param scope [Scope] The current scope
|
55
57
|
#
|
56
58
|
# @return [boolean]
|
57
|
-
def self.variable_reference?(node,
|
58
|
-
return false unless node.
|
59
|
+
def self.variable_reference?(node, position, scope)
|
60
|
+
return false unless node.is_a?(::CSVPlusPlus::Entities::Reference)
|
61
|
+
|
62
|
+
id = node.id
|
63
|
+
return false unless id && scope.variables.key?(id)
|
59
64
|
|
60
|
-
if
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
"#{node.
|
65
|
-
node.
|
65
|
+
return true if scope.in_scope?(id, position)
|
66
|
+
|
67
|
+
raise(
|
68
|
+
::CSVPlusPlus::Error::ModifierSyntaxError.new(
|
69
|
+
"Reference #{node.ref} can only be referenced within the ![[expand]] where it was defined.",
|
70
|
+
bad_input: node.ref.to_s
|
66
71
|
)
|
67
|
-
|
72
|
+
)
|
68
73
|
end
|
69
74
|
private_class_method :variable_reference?
|
70
75
|
|
71
76
|
sig do
|
72
|
-
params(node: ::CSVPlusPlus::Entities::Entity,
|
77
|
+
params(node: ::CSVPlusPlus::Entities::Entity, scope: ::CSVPlusPlus::Runtime::Scope).returns(::T::Boolean)
|
73
78
|
end
|
74
79
|
# Is the node a resolvable function reference?
|
75
80
|
#
|
76
81
|
# @param node [Entity] The node to check if it's resolvable
|
77
|
-
# @param
|
82
|
+
# @param scope [Scope] The current scope
|
78
83
|
#
|
79
84
|
# @return [boolean]
|
80
|
-
def self.function_reference?(node,
|
81
|
-
node.
|
82
|
-
&& (
|
85
|
+
def self.function_reference?(node, scope)
|
86
|
+
node.is_a?(::CSVPlusPlus::Entities::FunctionCall) \
|
87
|
+
&& (scope.functions.key?(node.id) || ::CSVPlusPlus::Entities::Builtins.builtin_function?(node.id))
|
83
88
|
end
|
84
89
|
private_class_method :function_reference?
|
85
90
|
|
@@ -87,7 +92,7 @@ module CSVPlusPlus
|
|
87
92
|
# Create an object with empty references. The caller will build them up as it depth-first-searches
|
88
93
|
def initialize
|
89
94
|
@functions = ::T.let([], ::T::Array[::CSVPlusPlus::Entities::FunctionCall])
|
90
|
-
@variables = ::T.let([], ::T::Array[::CSVPlusPlus::Entities::
|
95
|
+
@variables = ::T.let([], ::T::Array[::CSVPlusPlus::Entities::Reference])
|
91
96
|
end
|
92
97
|
|
93
98
|
sig { params(other: ::CSVPlusPlus::Runtime::References).returns(::T::Boolean) }
|
@@ -7,25 +7,17 @@ module CSVPlusPlus
|
|
7
7
|
# a given file. We take multiple runs through the input file for parsing so it's really convenient to have a
|
8
8
|
# central place for these things to be managed.
|
9
9
|
#
|
10
|
-
# @attr_reader
|
11
|
-
#
|
12
|
-
#
|
13
|
-
# @attr cell [Cell] The current cell being processed
|
14
|
-
# @attr cell_index [Integer] The index of the current cell being processed (starts at 0)
|
15
|
-
# @attr row_index [Integer] The index of the current row being processed (starts at 0)
|
16
|
-
# @attr line_number [Integer] The line number of the original csvpp template (starts at 1)
|
10
|
+
# @attr_reader position [Runtime::Position]
|
11
|
+
# @attr_reader scope [Runtime::Scope]
|
12
|
+
# @attr_reader source_code [SourceCode]
|
17
13
|
class Runtime
|
18
14
|
extend ::T::Sig
|
19
15
|
|
20
|
-
|
21
|
-
|
22
|
-
include ::CSVPlusPlus::Runtime::PositionTracker
|
23
|
-
|
24
|
-
sig { returns(::T::Hash[::Symbol, ::CSVPlusPlus::Entities::Function]) }
|
25
|
-
attr_reader :functions
|
16
|
+
sig { returns(::CSVPlusPlus::Runtime::Position) }
|
17
|
+
attr_reader :position
|
26
18
|
|
27
|
-
sig { returns(::
|
28
|
-
attr_reader :
|
19
|
+
sig { returns(::CSVPlusPlus::Runtime::Scope) }
|
20
|
+
attr_reader :scope
|
29
21
|
|
30
22
|
sig { returns(::CSVPlusPlus::SourceCode) }
|
31
23
|
attr_reader :source_code
|
@@ -33,47 +25,52 @@ module CSVPlusPlus
|
|
33
25
|
sig do
|
34
26
|
params(
|
35
27
|
source_code: ::CSVPlusPlus::SourceCode,
|
36
|
-
|
37
|
-
|
28
|
+
position: ::T.nilable(::CSVPlusPlus::Runtime::Position),
|
29
|
+
scope: ::T.nilable(::CSVPlusPlus::Runtime::Scope)
|
38
30
|
).void
|
39
31
|
end
|
32
|
+
# @param position [Position, nil] The (optional) position to start at
|
40
33
|
# @param source_code [SourceCode] The source code being compiled
|
41
|
-
# @param
|
42
|
-
|
43
|
-
def initialize(source_code:, functions: {}, variables: {})
|
44
|
-
@functions = functions
|
45
|
-
@variables = variables
|
34
|
+
# @param scope [Runtime::Scope, nil] The (optional) scope if it already exists
|
35
|
+
def initialize(source_code:, position: nil, scope: nil)
|
46
36
|
@source_code = source_code
|
47
|
-
|
48
|
-
|
37
|
+
@scope = ::T.let(scope || ::CSVPlusPlus::Runtime::Scope.new, ::CSVPlusPlus::Runtime::Scope)
|
38
|
+
@position = ::T.let(
|
39
|
+
position || ::CSVPlusPlus::Runtime::Position.new(source_code.input),
|
40
|
+
::CSVPlusPlus::Runtime::Position
|
41
|
+
)
|
49
42
|
end
|
50
43
|
|
51
|
-
sig { params(
|
52
|
-
#
|
44
|
+
sig { params(var_id: ::Symbol).returns(::CSVPlusPlus::Entities::Reference) }
|
45
|
+
# Bind +var_id+ to the current cell
|
53
46
|
#
|
54
|
-
# @param
|
47
|
+
# @param var_id [Symbol] The name of the variable to bind the cell reference to
|
55
48
|
#
|
56
|
-
# @return [
|
57
|
-
def
|
58
|
-
::CSVPlusPlus::Entities::
|
49
|
+
# @return [Entities::Reference]
|
50
|
+
def bind_variable_to_cell(var_id)
|
51
|
+
::CSVPlusPlus::Entities::Reference.new(
|
52
|
+
a1_ref: ::CSVPlusPlus::A1Reference.new(cell_index: position.cell_index, row_index: position.row_index)
|
53
|
+
).tap do |var|
|
54
|
+
scope.def_variable(var_id, var)
|
55
|
+
end
|
59
56
|
end
|
60
57
|
|
61
|
-
sig
|
62
|
-
|
63
|
-
#
|
64
|
-
# @param var_id [Symbol] The Variable#id to check if it's a runtime variable
|
65
|
-
#
|
66
|
-
# @return [T::Boolean]
|
67
|
-
def builtin_variable?(var_id)
|
68
|
-
::CSVPlusPlus::Entities::Builtins::VARIABLES.key?(var_id)
|
58
|
+
sig do
|
59
|
+
params(var_id: ::Symbol, expand: ::CSVPlusPlus::Modifier::Expand).returns(::CSVPlusPlus::Entities::Reference)
|
69
60
|
end
|
70
|
-
|
71
|
-
|
72
|
-
# Is the parser currently inside of the code section? (includes the `---`)
|
61
|
+
# Bind +var_id+ relative to a cell relative to an ![[expand]] modifier. The variable can only be referenced
|
62
|
+
# inside rows of that expand.
|
73
63
|
#
|
74
|
-
# @
|
75
|
-
|
76
|
-
|
64
|
+
# @param var_id [Symbol] The name of the variable to bind the cell reference to
|
65
|
+
# @param expand [Expand] The expand where the variable is accessible (where it will be bound relative to)
|
66
|
+
#
|
67
|
+
# @return [Entities::Reference]
|
68
|
+
def bind_variable_in_expand(var_id, expand)
|
69
|
+
::CSVPlusPlus::Entities::Reference.new(
|
70
|
+
a1_ref: ::CSVPlusPlus::A1Reference.new(scoped_to_expand: expand, cell_index: position.cell_index)
|
71
|
+
).tap do |var|
|
72
|
+
scope.def_variable(var_id, var)
|
73
|
+
end
|
77
74
|
end
|
78
75
|
|
79
76
|
sig { returns(::T::Boolean) }
|
@@ -81,35 +78,44 @@ module CSVPlusPlus
|
|
81
78
|
#
|
82
79
|
# @return [T::Boolean]
|
83
80
|
def parsing_csv_section?
|
84
|
-
source_code.in_csv_section?(line_number)
|
81
|
+
source_code.in_csv_section?(position.line_number)
|
85
82
|
end
|
86
83
|
|
87
|
-
sig
|
88
|
-
|
89
|
-
.returns(::T.noreturn)
|
90
|
-
end
|
91
|
-
# Called when an error is encoutered during parsing formulas (whether in the code section or a cell). It will
|
92
|
-
# construct a useful error with the current +@row/@cell_index+, +@line_number+ and +@filename+
|
84
|
+
sig { params(ast: ::CSVPlusPlus::Entities::Entity).returns(::CSVPlusPlus::Entities::Entity) }
|
85
|
+
# Resolve all values in the ast of the current cell being processed
|
93
86
|
#
|
94
|
-
# @param
|
95
|
-
#
|
96
|
-
# @
|
97
|
-
|
98
|
-
|
87
|
+
# @param ast [Entities::Entity] The AST to replace references within
|
88
|
+
#
|
89
|
+
# @return [Entity] The AST with all references replaced
|
90
|
+
# rubocop:disable Metrics/MethodLength
|
91
|
+
def resolve_cell_value(ast)
|
92
|
+
last_round = nil
|
93
|
+
::Kernel.loop do
|
94
|
+
refs = ::CSVPlusPlus::Runtime::References.extract(ast, position, scope)
|
95
|
+
return ast if refs.empty?
|
96
|
+
|
97
|
+
# TODO: throw a +CompilerError+ here instead I think - basically we did a round and didn't make progress
|
98
|
+
return ast if last_round == refs
|
99
|
+
|
100
|
+
ast = scope.resolve_functions(
|
101
|
+
position,
|
102
|
+
scope.resolve_variables(position, ast, refs.variables),
|
103
|
+
refs.functions
|
104
|
+
)
|
105
|
+
end
|
99
106
|
end
|
107
|
+
# rubocop:enable Metrics/MethodLength
|
100
108
|
|
101
109
|
sig do
|
102
|
-
params(
|
103
|
-
.returns(::T.noreturn)
|
110
|
+
type_parameters(:R).params(block: ::T.proc.returns(::T.type_parameter(:R))).returns(::T.type_parameter(:R))
|
104
111
|
end
|
105
|
-
#
|
106
|
-
#
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
def raise_modifier_syntax_error(message, bad_input, wrapped_error: nil)
|
111
|
-
raise(::CSVPlusPlus::Error::ModifierSyntaxError.new(self, bad_input:, message:, wrapped_error:))
|
112
|
+
# Each time we run a parse on the input, reset the runtime state starting at the beginning of the file
|
113
|
+
# rubocop:disable Naming/BlockForwarding
|
114
|
+
def start!(&block)
|
115
|
+
position.line_number = 1
|
116
|
+
position.start!(&block)
|
112
117
|
end
|
118
|
+
# rubocop:enable Naming/BlockForwarding
|
113
119
|
|
114
120
|
sig do
|
115
121
|
type_parameters(:R).params(block: ::T.proc.returns(::T.type_parameter(:R))).returns(::T.type_parameter(:R))
|
@@ -117,8 +123,8 @@ module CSVPlusPlus
|
|
117
123
|
# Reset the runtime state starting at the CSV section
|
118
124
|
# rubocop:disable Naming/BlockForwarding
|
119
125
|
def start_at_csv!(&block)
|
120
|
-
|
121
|
-
start!(&block)
|
126
|
+
position.line_number = source_code.length_of_code_section + 1
|
127
|
+
position.start!(&block)
|
122
128
|
end
|
123
129
|
# rubocop:enable Naming/BlockForwarding
|
124
130
|
end
|