csv_plus_plus 0.1.2 → 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 +1 -2
- data/{CHANGELOG.md → docs/CHANGELOG.md} +9 -0
- data/lib/csv_plus_plus/benchmarked_compiler.rb +70 -20
- data/lib/csv_plus_plus/cell.rb +46 -24
- data/lib/csv_plus_plus/cli.rb +23 -13
- data/lib/csv_plus_plus/cli_flag.rb +1 -2
- data/lib/csv_plus_plus/color.rb +32 -7
- data/lib/csv_plus_plus/compiler.rb +82 -60
- data/lib/csv_plus_plus/entities/ast_builder.rb +27 -43
- data/lib/csv_plus_plus/entities/boolean.rb +18 -9
- data/lib/csv_plus_plus/entities/builtins.rb +23 -9
- data/lib/csv_plus_plus/entities/cell_reference.rb +200 -29
- data/lib/csv_plus_plus/entities/date.rb +38 -5
- data/lib/csv_plus_plus/entities/entity.rb +27 -61
- data/lib/csv_plus_plus/entities/entity_with_arguments.rb +57 -0
- data/lib/csv_plus_plus/entities/function.rb +23 -11
- data/lib/csv_plus_plus/entities/function_call.rb +24 -9
- data/lib/csv_plus_plus/entities/number.rb +24 -10
- data/lib/csv_plus_plus/entities/runtime_value.rb +22 -5
- data/lib/csv_plus_plus/entities/string.rb +19 -6
- data/lib/csv_plus_plus/entities/variable.rb +16 -4
- data/lib/csv_plus_plus/entities.rb +20 -13
- data/lib/csv_plus_plus/error/error.rb +11 -1
- data/lib/csv_plus_plus/error/formula_syntax_error.rb +1 -0
- data/lib/csv_plus_plus/error/modifier_syntax_error.rb +53 -5
- data/lib/csv_plus_plus/error/modifier_validation_error.rb +34 -14
- data/lib/csv_plus_plus/error/syntax_error.rb +22 -9
- data/lib/csv_plus_plus/error/writer_error.rb +8 -0
- data/lib/csv_plus_plus/error.rb +1 -0
- data/lib/csv_plus_plus/google_api_client.rb +7 -2
- data/lib/csv_plus_plus/google_options.rb +23 -18
- data/lib/csv_plus_plus/lexer/lexer.rb +8 -4
- 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 +1 -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 -158
- data/lib/csv_plus_plus/options.rb +64 -19
- data/lib/csv_plus_plus/parser/cell_value.tab.rb +5 -5
- data/lib/csv_plus_plus/parser/code_section.tab.rb +8 -13
- data/lib/csv_plus_plus/parser/modifier.tab.rb +17 -23
- data/lib/csv_plus_plus/row.rb +53 -12
- 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 +34 -191
- data/lib/csv_plus_plus/source_code.rb +66 -0
- data/lib/csv_plus_plus/template.rb +62 -35
- 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 +1 -0
- data/lib/csv_plus_plus/writer/google_sheet_builder.rb +71 -23
- 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 -30
- data/lib/csv_plus_plus/writer.rb +39 -9
- data/lib/csv_plus_plus.rb +29 -12
- metadata +18 -14
- data/lib/csv_plus_plus/can_define_references.rb +0 -88
- data/lib/csv_plus_plus/can_resolve_references.rb +0 -8
- data/lib/csv_plus_plus/data_validation.rb +0 -138
- data/lib/csv_plus_plus/expand.rb +0 -20
- data/lib/csv_plus_plus/graph.rb +0 -62
- data/lib/csv_plus_plus/references.rb +0 -68
- data/lib/csv_plus_plus/scope.rb +0 -196
- data/lib/csv_plus_plus/validated_modifier.rb +0 -164
- data/lib/csv_plus_plus/writer/google_sheet_modifier.rb +0 -77
- data/lib/csv_plus_plus/writer/rubyxl_modifier.rb +0 -59
@@ -1,59 +1,69 @@
|
|
1
|
+
# typed: strict
|
1
2
|
# frozen_string_literal: true
|
2
3
|
|
3
|
-
require_relative 'benchmarked_compiler'
|
4
|
-
require_relative 'entities'
|
5
|
-
require_relative 'parser/code_section.tab'
|
6
|
-
require_relative 'runtime'
|
7
|
-
require_relative 'scope'
|
8
|
-
|
9
4
|
module CSVPlusPlus
|
10
5
|
# Encapsulates the parsing and building of objects (+Template+ -> +Row+ -> +Cell+). Variable resolution is delegated
|
11
6
|
# to the +Scope+
|
12
7
|
#
|
13
8
|
# @attr_reader options [Options] The +Options+ to compile with
|
14
9
|
# @attr_reader runtime [Runtime] The runtime execution
|
15
|
-
#
|
10
|
+
# rubocop:disable Metrics/ClassLength
|
16
11
|
class Compiler
|
17
|
-
|
12
|
+
extend ::T::Sig
|
13
|
+
|
14
|
+
sig { returns(::CSVPlusPlus::Options) }
|
15
|
+
attr_reader :options
|
16
|
+
|
17
|
+
sig { returns(::CSVPlusPlus::Runtime::Runtime) }
|
18
|
+
attr_reader :runtime
|
18
19
|
|
20
|
+
sig do
|
21
|
+
params(
|
22
|
+
options: ::CSVPlusPlus::Options,
|
23
|
+
runtime: ::CSVPlusPlus::Runtime::Runtime,
|
24
|
+
block: ::T.proc.params(arg0: ::CSVPlusPlus::Compiler).void
|
25
|
+
).void
|
26
|
+
end
|
19
27
|
# Create a compiler and make sure it gets cleaned up
|
20
28
|
#
|
21
|
-
# @param runtime [Runtime] The initial +Runtime+ for the compiler
|
22
29
|
# @param options [Options]
|
23
|
-
|
24
|
-
|
30
|
+
# @param runtime [Runtime] The initial +Runtime+ for the compiler
|
31
|
+
def self.with_compiler(options:, runtime:, &block)
|
25
32
|
if options.verbose
|
26
|
-
::CSVPlusPlus::BenchmarkedCompiler.with_benchmarks(
|
33
|
+
::CSVPlusPlus::BenchmarkedCompiler.with_benchmarks(options:, runtime:) do |c|
|
27
34
|
block.call(c)
|
28
35
|
end
|
29
36
|
else
|
30
|
-
|
37
|
+
block.call(new(options:, runtime:))
|
31
38
|
end
|
32
39
|
ensure
|
33
40
|
runtime.cleanup!
|
34
41
|
end
|
35
42
|
|
36
|
-
|
43
|
+
sig { params(options: ::CSVPlusPlus::Options, runtime: ::CSVPlusPlus::Runtime::Runtime).void }
|
37
44
|
# @param options [Options]
|
38
|
-
# @param
|
39
|
-
def initialize(
|
45
|
+
# @param runtime [Runtime]
|
46
|
+
def initialize(options:, runtime:)
|
40
47
|
@options = options
|
41
48
|
@runtime = runtime
|
42
|
-
@scope = scope || ::CSVPlusPlus::Scope.new(runtime:)
|
43
49
|
|
44
50
|
# TODO: infer a type
|
45
51
|
# allow user-supplied key/values to override anything global or from the code section
|
46
|
-
@
|
52
|
+
@runtime.def_variables(
|
47
53
|
options.key_values.transform_values { |v| ::CSVPlusPlus::Entities::String.new(v.to_s) }
|
48
54
|
)
|
49
55
|
end
|
50
56
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
57
|
+
sig { params(benchmark: ::Benchmark::Report).void }
|
58
|
+
# Attach a +Benchmark+ and a place to store timings to the compiler class.
|
59
|
+
#
|
60
|
+
# @param benchmark [Benchmark] A +Benchmark+ instance
|
61
|
+
def benchmark=(benchmark)
|
62
|
+
@benchmark = ::T.let(benchmark, ::T.nilable(::Benchmark::Report))
|
63
|
+
@timings = ::T.let([], ::T.nilable(::T::Array[::Benchmark::Tms]))
|
55
64
|
end
|
56
65
|
|
66
|
+
sig { returns(::CSVPlusPlus::Template) }
|
57
67
|
# Compile a template and return a +::CSVPlusPlus::Template+ instance ready to be written with a +Writer+
|
58
68
|
#
|
59
69
|
# @return [Template]
|
@@ -61,96 +71,108 @@ module CSVPlusPlus
|
|
61
71
|
parse_code_section!
|
62
72
|
rows = parse_csv_section!
|
63
73
|
|
64
|
-
::CSVPlusPlus::Template.new(rows:,
|
74
|
+
::CSVPlusPlus::Template.new(rows:, runtime: @runtime).tap do |t|
|
65
75
|
t.validate_infinite_expands(@runtime)
|
66
|
-
expanding { t.expand_rows! }
|
76
|
+
expanding! { t.expand_rows! }
|
77
|
+
bind_all_vars! { t.bind_all_vars!(@runtime) }
|
67
78
|
resolve_all_cells!(t)
|
68
79
|
end
|
69
80
|
end
|
70
81
|
|
71
|
-
|
72
|
-
|
73
|
-
|
82
|
+
sig { params(block: ::T.proc.params(runtime: ::CSVPlusPlus::Runtime::Runtime).void).void }
|
83
|
+
# Write the compiled results
|
84
|
+
def outputting!(&block)
|
85
|
+
@runtime.start_at_csv! { block.call(@runtime) }
|
74
86
|
end
|
75
87
|
|
76
88
|
protected
|
77
89
|
|
78
|
-
|
79
|
-
#
|
80
|
-
# @return [CodeSection]
|
90
|
+
sig { void }
|
91
|
+
# Parses the input file and sets variables on +@runtime+ as necessary
|
81
92
|
def parse_code_section!
|
82
|
-
@runtime.start!
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
next csv_section
|
93
|
+
@runtime.start! do
|
94
|
+
# TODO: this flow can probably be refactored, it used to have more needs back when we had to
|
95
|
+
# parse and save the code_section
|
96
|
+
parsing_code_section do |input|
|
97
|
+
csv_section = ::CSVPlusPlus::Parser::CodeSection.new.parse(input, @runtime)
|
98
|
+
|
99
|
+
# return the csv_section to the caller because they're gonna re-write input with it
|
100
|
+
next csv_section
|
101
|
+
end
|
92
102
|
end
|
93
|
-
# @scope.code_section
|
94
103
|
end
|
95
104
|
|
105
|
+
sig { returns(::T::Array[::CSVPlusPlus::Row]) }
|
96
106
|
# Parse the CSV section and return an array of +Row+s
|
97
107
|
#
|
98
108
|
# @return [Array<Row>]
|
99
109
|
def parse_csv_section!
|
100
|
-
@runtime.start_at_csv!
|
101
|
-
|
102
|
-
|
110
|
+
@runtime.start_at_csv! do
|
111
|
+
@runtime.map_lines(::CSV.new(::T.unsafe(@runtime.input))) do |csv_row|
|
112
|
+
parse_row(::T.cast(csv_row, ::T::Array[::String]))
|
113
|
+
end
|
103
114
|
end
|
104
115
|
ensure
|
105
116
|
# we're done with the file and everything is in memory
|
106
117
|
@runtime.cleanup!
|
107
118
|
end
|
108
119
|
|
120
|
+
sig { params(template: ::CSVPlusPlus::Template).returns(::T::Array[::T::Array[::CSVPlusPlus::Entities::Entity]]) }
|
109
121
|
# Iterates through each cell of each row and resolves it's variable and function references.
|
110
122
|
#
|
111
123
|
# @param template [Template]
|
124
|
+
#
|
112
125
|
# @return [Array<Entity>]
|
113
126
|
def resolve_all_cells!(template)
|
114
|
-
@runtime.start_at_csv!
|
115
|
-
|
116
|
-
|
127
|
+
@runtime.start_at_csv! do
|
128
|
+
@runtime.map_all_cells(template.rows) do |cell|
|
129
|
+
cell.ast = @runtime.resolve_cell_value if cell.ast
|
130
|
+
end
|
117
131
|
end
|
118
132
|
end
|
119
133
|
|
134
|
+
sig { params(block: ::T.proc.void).void }
|
120
135
|
# Expanding rows
|
121
|
-
def expanding
|
122
|
-
@runtime.start_at_csv!
|
123
|
-
|
136
|
+
def expanding!(&block)
|
137
|
+
@runtime.start_at_csv! { block.call }
|
138
|
+
end
|
139
|
+
|
140
|
+
sig { params(block: ::T.proc.void).void }
|
141
|
+
# Binding all [[var=]] directives
|
142
|
+
def bind_all_vars!(&block)
|
143
|
+
@runtime.start_at_csv! { block.call }
|
124
144
|
end
|
125
145
|
|
126
146
|
private
|
127
147
|
|
128
|
-
|
129
|
-
|
148
|
+
sig { params(block: ::T.proc.params(arg0: ::String).returns(::String)).void }
|
149
|
+
def parsing_code_section(&block)
|
150
|
+
csv_section = block.call(::T.must(::T.must(@runtime.input).read))
|
130
151
|
@runtime.rewrite_input!(csv_section)
|
131
152
|
end
|
132
153
|
|
154
|
+
sig { params(csv_row: ::T::Array[::T.nilable(::String)]).returns(::CSVPlusPlus::Row) }
|
133
155
|
# Using the current +@runtime+ and the given +csv_row+ parse it into a +Row+ of +Cell+s
|
134
156
|
# +csv_row+ should have already been run through a CSV parser and is an array of strings
|
135
157
|
#
|
136
158
|
# @param csv_row [Array<Array<String>>]
|
159
|
+
#
|
137
160
|
# @return [Row]
|
138
161
|
def parse_row(csv_row)
|
139
|
-
row_modifier = ::CSVPlusPlus::
|
162
|
+
row_modifier = ::CSVPlusPlus::Modifier.new(@options, row_level: true)
|
140
163
|
|
141
|
-
cells = @runtime.map_row(csv_row) { |value, _cell_index| parse_cell(value, row_modifier) }
|
164
|
+
cells = @runtime.map_row(csv_row) { |value, _cell_index| parse_cell(value || '', row_modifier) }
|
142
165
|
|
143
|
-
::CSVPlusPlus::Row.new(@runtime.row_index,
|
166
|
+
::CSVPlusPlus::Row.new(cells:, index: @runtime.row_index, modifier: row_modifier)
|
144
167
|
end
|
145
168
|
|
169
|
+
sig { params(value: ::String, row_modifier: ::CSVPlusPlus::Modifier::Modifier).returns(::CSVPlusPlus::Cell) }
|
146
170
|
def parse_cell(value, row_modifier)
|
147
|
-
cell_modifier = ::CSVPlusPlus::
|
148
|
-
parsed_value = ::CSVPlusPlus::Parser::Modifier.new(cell_modifier:, row_modifier
|
149
|
-
value,
|
150
|
-
@runtime
|
151
|
-
)
|
171
|
+
cell_modifier = ::CSVPlusPlus::Modifier.new(@options)
|
172
|
+
parsed_value = ::CSVPlusPlus::Parser::Modifier.new(cell_modifier:, row_modifier:).parse(value, @runtime)
|
152
173
|
|
153
174
|
::CSVPlusPlus::Cell.parse(parsed_value, runtime:, modifier: cell_modifier)
|
154
175
|
end
|
155
176
|
end
|
177
|
+
# rubocop:enable Metrics/ClassLength
|
156
178
|
end
|
@@ -1,65 +1,49 @@
|
|
1
|
+
# typed: strict
|
1
2
|
# frozen_string_literal: true
|
2
3
|
|
3
4
|
module CSVPlusPlus
|
4
5
|
module Entities
|
5
6
|
# Some helpful functions that can be mixed into a class to help building ASTs
|
6
7
|
module ASTBuilder
|
8
|
+
extend ::T::Sig
|
9
|
+
|
10
|
+
sig do
|
11
|
+
params(
|
12
|
+
method_name: ::Symbol,
|
13
|
+
args: ::T.untyped,
|
14
|
+
kwargs: ::T.untyped,
|
15
|
+
block: ::T.untyped
|
16
|
+
).returns(::CSVPlusPlus::Entities::Entity)
|
17
|
+
end
|
7
18
|
# Let the current class have functions which can build a given entity by calling it's type. For example
|
8
19
|
# +number(1)+, +variable(:foo)+
|
9
20
|
#
|
10
21
|
# @param method_name [Symbol] The +method_name+ to respond to
|
11
|
-
# @param
|
22
|
+
# @param args [Array] The arguments to create the entity with
|
23
|
+
# @param kwargs [Hash] The arguments to create the entity with
|
12
24
|
#
|
13
25
|
# @return [Entity, #super]
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
26
|
+
# rubocop:disable Naming/BlockForwarding
|
27
|
+
def method_missing(method_name, *args, **kwargs, &block)
|
28
|
+
entity_class_name = method_name.to_s.split('_').map(&:capitalize).join.to_sym
|
29
|
+
::CSVPlusPlus::Entities.const_get(entity_class_name).new(*args, **kwargs, &block)
|
30
|
+
rescue ::NameError
|
31
|
+
super
|
19
32
|
end
|
33
|
+
# rubocop:enable Naming/BlockForwarding
|
20
34
|
|
35
|
+
sig { params(method_name: ::Symbol, _args: ::T.untyped).returns(::T::Boolean) }
|
21
36
|
# Let the current class have functions which can build a given entity by calling it's type. For example
|
22
37
|
# +number(1)+, +variable(:foo)+
|
23
38
|
#
|
24
39
|
# @param method_name [Symbol] The +method_name+ to respond to
|
25
|
-
# @param
|
40
|
+
# @param _args [::T.Untyped] The arguments to create the entity with
|
26
41
|
#
|
27
|
-
# @return [Boolean, #super]
|
28
|
-
def respond_to_missing?(method_name, *
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
# Turns index-based/X,Y coordinates into a A1 format
|
33
|
-
#
|
34
|
-
# @param row_index [Integer]
|
35
|
-
# @param cell_index [Integer]
|
36
|
-
#
|
37
|
-
# @return [String]
|
38
|
-
def ref(row_index: nil, cell_index: nil)
|
39
|
-
return unless row_index || cell_index
|
40
|
-
|
41
|
-
rowref = row_index ? (row_index + 1).to_s : ''
|
42
|
-
cellref = cell_index ? cell_ref(cell_index) : ''
|
43
|
-
cell_reference([cellref, rowref].join)
|
44
|
-
end
|
45
|
-
|
46
|
-
private
|
47
|
-
|
48
|
-
ALPHA = ('A'..'Z').to_a.freeze
|
49
|
-
private_constant :ALPHA
|
50
|
-
|
51
|
-
def cell_ref(cell_index)
|
52
|
-
c = cell_index.dup
|
53
|
-
ref = ''
|
54
|
-
|
55
|
-
while c >= 0
|
56
|
-
# rubocop:disable Lint/ConstantResolution
|
57
|
-
ref += ALPHA[c % 26]
|
58
|
-
# rubocop:enable Lint/ConstantResolution
|
59
|
-
c = (c / 26).floor - 1
|
60
|
-
end
|
61
|
-
|
62
|
-
ref.reverse
|
42
|
+
# @return [::T::Boolean, #super]
|
43
|
+
def respond_to_missing?(method_name, *_args)
|
44
|
+
!::CSVPlusPlus::Entities::Type.deserialize(method_name.to_s.gsub('_', '')).nil?
|
45
|
+
rescue ::KeyError
|
46
|
+
super
|
63
47
|
end
|
64
48
|
end
|
65
49
|
end
|
@@ -1,30 +1,39 @@
|
|
1
|
+
# typed: strict
|
1
2
|
# frozen_string_literal: true
|
2
3
|
|
3
|
-
require_relative './entity'
|
4
|
-
|
5
4
|
module CSVPlusPlus
|
6
5
|
module Entities
|
7
6
|
# A boolean value
|
8
7
|
#
|
9
8
|
# @attr_reader value [true, false]
|
10
9
|
class Boolean < Entity
|
10
|
+
sig { returns(::T::Boolean) }
|
11
11
|
attr_reader :value
|
12
12
|
|
13
|
-
|
13
|
+
sig { params(value: ::T.any(::String, ::T::Boolean)).void }
|
14
|
+
# @param value [::String, boolean]
|
14
15
|
def initialize(value)
|
15
|
-
super(
|
16
|
+
super(::CSVPlusPlus::Entities::Type::Boolean)
|
16
17
|
# TODO: probably can do a lot better in general on type validation
|
17
|
-
@value = value.is_a?(::String) ? (value.downcase == 'true') : value
|
18
|
+
@value = ::T.let(value.is_a?(::String) ? (value.downcase == 'true') : value, ::T::Boolean)
|
18
19
|
end
|
19
20
|
|
20
|
-
|
21
|
-
|
21
|
+
sig { override.params(_runtime: ::CSVPlusPlus::Runtime::Runtime).returns(::String) }
|
22
|
+
# @param _runtime [Runtime]
|
23
|
+
#
|
24
|
+
# @return [::String]
|
25
|
+
def evaluate(_runtime)
|
22
26
|
@value.to_s.upcase
|
23
27
|
end
|
24
28
|
|
25
|
-
|
29
|
+
sig { override.params(other: ::CSVPlusPlus::Entities::Entity).returns(::T::Boolean) }
|
30
|
+
# @param other [Entity]
|
31
|
+
#
|
32
|
+
# @return [::T::Boolean]
|
26
33
|
def ==(other)
|
27
|
-
|
34
|
+
return false unless super
|
35
|
+
|
36
|
+
other.is_a?(self.class) && value == other.value
|
28
37
|
end
|
29
38
|
end
|
30
39
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# typed: false
|
1
2
|
# frozen_string_literal: true
|
2
3
|
|
3
4
|
module CSVPlusPlus
|
@@ -8,35 +9,48 @@ module CSVPlusPlus
|
|
8
9
|
|
9
10
|
VARIABLES = {
|
10
11
|
# The number (integer) of the current cell. Starts at 1
|
11
|
-
cellnum: runtime_value(->(
|
12
|
+
cellnum: runtime_value(->(r) { number(r.cell_index + 1) }),
|
12
13
|
|
13
14
|
# A reference to the current cell
|
14
|
-
cellref: runtime_value(->(
|
15
|
+
cellref: runtime_value(->(r) { cell_reference(row_index: r.row_index, cell_index: r.cell_index) }),
|
15
16
|
|
16
17
|
# A reference to the row above
|
17
|
-
rowabove: runtime_value(->(
|
18
|
+
rowabove: runtime_value(->(r) { cell_reference(row_index: [0, (r.row_index - 1)].max) }),
|
18
19
|
|
19
20
|
# A reference to the row below
|
20
|
-
rowbelow: runtime_value(->(
|
21
|
+
rowbelow: runtime_value(->(r) { cell_reference(row_index: r.row_index + 1) }),
|
21
22
|
|
22
23
|
# The number (integer) of the current row. Starts at 1
|
23
|
-
rownum: runtime_value(->(
|
24
|
+
rownum: runtime_value(->(r) { number(r.rownum) }),
|
24
25
|
|
25
26
|
# A reference to the current row
|
26
|
-
rowref: runtime_value(->(
|
27
|
+
rowref: runtime_value(->(r) { cell_reference(row_index: r.row_index) })
|
27
28
|
}.freeze
|
28
29
|
public_constant :VARIABLES
|
29
30
|
|
30
31
|
FUNCTIONS = {
|
31
32
|
# TODO: A reference to a cell in a given row?
|
32
33
|
# A reference to a cell above the current row
|
33
|
-
cellabove: runtime_value(->(
|
34
|
+
# cellabove: runtime_value(->(r, args) { cell_reference(ref: [args[0], [1, (r.rownum - 1)].max].join) }),
|
35
|
+
cellabove: runtime_value(
|
36
|
+
lambda { |r, args|
|
37
|
+
cell_reference(cell_index: args[0].cell_index, row_index: [0, (r.row_index - 1)].max)
|
38
|
+
}
|
39
|
+
),
|
34
40
|
|
35
41
|
# A reference to a cell in the current row
|
36
|
-
celladjacent: runtime_value(
|
42
|
+
celladjacent: runtime_value(
|
43
|
+
lambda { |r, args|
|
44
|
+
cell_reference(cell_index: args[0].cell_index, row_index: r.row_index)
|
45
|
+
}
|
46
|
+
),
|
37
47
|
|
38
48
|
# A reference to a cell below the current row
|
39
|
-
cellbelow: runtime_value(
|
49
|
+
cellbelow: runtime_value(
|
50
|
+
lambda { |r, args|
|
51
|
+
cell_reference(cell_index: args[0].cell_index, row_index: r.row_index + 1)
|
52
|
+
}
|
53
|
+
)
|
40
54
|
}.freeze
|
41
55
|
public_constant :FUNCTIONS
|
42
56
|
end
|