csv_plus_plus 0.1.2 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|