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.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +7 -0
  3. data/README.md +1 -0
  4. data/lib/csv_plus_plus/cell.rb +24 -8
  5. data/lib/csv_plus_plus/cli.rb +29 -16
  6. data/lib/csv_plus_plus/cli_flag.rb +10 -2
  7. data/lib/csv_plus_plus/code_section.rb +22 -3
  8. data/lib/csv_plus_plus/color.rb +19 -5
  9. data/lib/csv_plus_plus/google_options.rb +6 -2
  10. data/lib/csv_plus_plus/graph.rb +0 -1
  11. data/lib/csv_plus_plus/language/ast_builder.rb +68 -0
  12. data/lib/csv_plus_plus/language/benchmarked_compiler.rb +65 -0
  13. data/lib/csv_plus_plus/language/builtins.rb +46 -0
  14. data/lib/csv_plus_plus/language/cell_value.tab.rb +1 -2
  15. data/lib/csv_plus_plus/language/code_section.tab.rb +1 -2
  16. data/lib/csv_plus_plus/language/compiler.rb +74 -86
  17. data/lib/csv_plus_plus/language/entities/boolean.rb +3 -2
  18. data/lib/csv_plus_plus/language/entities/cell_reference.rb +10 -3
  19. data/lib/csv_plus_plus/language/entities/entity.rb +20 -8
  20. data/lib/csv_plus_plus/language/entities/function.rb +6 -4
  21. data/lib/csv_plus_plus/language/entities/function_call.rb +4 -3
  22. data/lib/csv_plus_plus/language/entities/number.rb +6 -4
  23. data/lib/csv_plus_plus/language/entities/runtime_value.rb +9 -8
  24. data/lib/csv_plus_plus/language/entities/string.rb +6 -4
  25. data/lib/csv_plus_plus/language/references.rb +22 -5
  26. data/lib/csv_plus_plus/language/runtime.rb +80 -22
  27. data/lib/csv_plus_plus/language/scope.rb +29 -38
  28. data/lib/csv_plus_plus/language/syntax_error.rb +10 -5
  29. data/lib/csv_plus_plus/lexer/lexer.rb +25 -12
  30. data/lib/csv_plus_plus/lexer/tokenizer.rb +35 -11
  31. data/lib/csv_plus_plus/modifier.rb +38 -13
  32. data/lib/csv_plus_plus/modifier.tab.rb +2 -2
  33. data/lib/csv_plus_plus/options.rb +17 -2
  34. data/lib/csv_plus_plus/row.rb +15 -4
  35. data/lib/csv_plus_plus/template.rb +10 -6
  36. data/lib/csv_plus_plus/version.rb +1 -1
  37. data/lib/csv_plus_plus/writer/excel.rb +2 -9
  38. data/lib/csv_plus_plus/writer/file_backer_upper.rb +22 -20
  39. data/lib/csv_plus_plus/writer/google_sheet_builder.rb +8 -10
  40. data/lib/csv_plus_plus/writer/google_sheets.rb +4 -10
  41. data/lib/csv_plus_plus/writer/rubyxl_builder.rb +23 -15
  42. data/lib/csv_plus_plus/writer/rubyxl_modifier.rb +15 -8
  43. data/lib/csv_plus_plus.rb +21 -3
  44. metadata +5 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c4f5c9b5a342102fa8e1c31b319fb05f19ad55278bc633435129254b5b2511a8
4
- data.tar.gz: 0c2656d4d7b22d0b3b3311745ed5a70b789277c622f0f2d09d2b8a144de71a8d
3
+ metadata.gz: 0a93c3ee93f2320dc717ee330c55cd3f300cfa4d155a4502b9a433cdafdcc389
4
+ data.tar.gz: aa95dca43a374d57f2217030e0aefef680341549613b0388cb5df1122cb5f815
5
5
  SHA512:
6
- metadata.gz: 90965e2275cc7988f4054b59e30523cd645b21c1adb89797828e86a767217b09ac174bbc17094604dd608b93defd79f4c9a74ccf7e2f20dd17995f8c1b5dadac
7
- data.tar.gz: 291011896232e88a7bd0e252887459986812d2877c1d0383458250e1c30cf735d8b2bdaf5dd759f44fa5b8b33b08f74d780df7960622ab774e9f6d856628508f
6
+ metadata.gz: 19a827c9a5fc8f0f9fe9d58c4e6b85de821c9e09627465673c2813c2690fadc886c7d8f24e3da5402783b27347a79f7832b13c82abe4e204026d12839d6f4691
7
+ data.tar.gz: b157b531d9ae9e2fda9c93ed466976deacfe639b1fbc1527b60c706be9d97792946341a767f00c32b6d35cbc6e388711980767d9ef6b5c20b4a2f44ebe7fb121
data/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+
2
+ ## v0.1.0
3
+
4
+ - revamp of builtin functions
5
+
6
+ - docs & tests
7
+
1
8
  ## v0.0.5
2
9
 
3
10
  - Support the --backup/-b option
data/README.md CHANGED
@@ -1,4 +1,5 @@
1
1
  [![Ruby Style Guide](https://img.shields.io/badge/code_style-community-brightgreen.svg)](https://rubystyle.guide)
2
+ [![Gem Version](https://badge.fury.io/rb/csv_plus_plus.svg)](https://badge.fury.io/rb/csv_plus_plus)
2
3
 
3
4
  # csv++
4
5
 
@@ -4,21 +4,33 @@ require_relative './language/cell_value.tab'
4
4
  require_relative './modifier'
5
5
 
6
6
  module CSVPlusPlus
7
- ##
8
7
  # A cell of a template
8
+ #
9
+ # @attr ast [Entity]
10
+ # @attr row_index [Integer] The cell's row index (starts at 0)
11
+ # @attr_reader index [Integer] The cell's index (starts at 0)
12
+ # @attr_reader modifier [Modifier] The modifier for this cell
9
13
  class Cell
10
14
  attr_accessor :ast, :row_index
11
15
  attr_reader :index, :modifier
12
16
 
13
- # Parse a +value+ into a Cell object. The +value+ should already have been through
14
- # a CSV parser
17
+ # Parse a +value+ into a Cell object.
18
+ #
19
+ # @param value [String] A string value which should already have been processed through a CSV parser
20
+ # @param runtime [Runtime]
21
+ # @param modifier [Modifier]
22
+ #
23
+ # @return [Cell]
15
24
  def self.parse(value, runtime:, modifier:)
16
25
  new(value:, row_index: runtime.row_index, index: runtime.cell_index, modifier:).tap do |c|
17
26
  c.ast = ::CSVPlusPlus::Language::CellValueParser.new.parse(value, runtime)
18
27
  end
19
28
  end
20
29
 
21
- # initialize
30
+ # @param row_index [Integer] The cell's row index (starts at 0)
31
+ # @param index [Integer] The cell's index (starts at 0)
32
+ # @param value [String] A string value which should already have been processed through a CSV parser
33
+ # @param modifier [Modifier] A modifier to apply to this cell
22
34
  def initialize(row_index:, index:, value:, modifier:)
23
35
  @value = value
24
36
  @modifier = modifier
@@ -26,25 +38,29 @@ module CSVPlusPlus
26
38
  @row_index = row_index
27
39
  end
28
40
 
29
- # The value (cleaned up some)
41
+ # The +@value+ (cleaned up some)
42
+ #
43
+ # @return [String]
30
44
  def value
31
45
  return if @value.nil? || @value.strip.empty?
32
46
 
33
47
  @value.strip
34
48
  end
35
49
 
36
- # to_s
50
+ # @return [String]
37
51
  def to_s
38
52
  "Cell(index: #{@index}, row_index: #{@row_index}, value: #{@value}, modifier: #{@modifier})"
39
53
  end
40
54
 
41
55
  # A compiled final representation of the cell. This can only happen after all cell have had
42
56
  # variables and functions resolved.
57
+ #
58
+ # @return [String]
43
59
  def to_csv
44
60
  return value unless @ast
45
61
 
46
- # This looks really simple but we're relying on each node of the AST to define #to_s and calling
47
- # this at the top will recursively print the tree
62
+ # This looks really simple but we're relying on each node of the AST to define #to_s such that calling
63
+ # this at the top will recursively print the tree (as a well-formatted spreadsheet formula)
48
64
  "=#{@ast}"
49
65
  end
50
66
  end
@@ -4,27 +4,32 @@ require 'optparse'
4
4
 
5
5
  module CSVPlusPlus
6
6
  # Handle running the application with the given CLI flags
7
+ #
8
+ # @attr options [Options, nil] The parsed CLI options
7
9
  class CLI
8
- # handle any CLI flags and launch the compiler
10
+ attr_accessor :options
11
+
12
+ # Handle CLI flags and launch the compiler
13
+ #
14
+ # @return [CLI]
9
15
  def self.launch_compiler!
10
16
  cli = new
11
- cli.compile!
17
+ cli.parse_options!
18
+ cli.main
12
19
  rescue ::StandardError => e
13
20
  cli.handle_error(e)
14
21
  exit(1)
15
22
  end
16
23
 
17
- # initialize
18
- def initialize
19
- parse_options!
20
- end
21
-
22
- # compile the given template, using the given CLI flags
23
- def compile!
24
+ # Compile the given template using the given CLI flags
25
+ def main
26
+ parse_options! unless @options
24
27
  ::CSVPlusPlus.apply_template_to_sheet!(::ARGF.read, ::ARGF.filename, @options)
25
28
  end
26
29
 
27
- # (nicely) handle a given error. how it's handled depends on if it's our error and if @options.verbose
30
+ # Nicely handle a given error. How it's handled depends on if it's our error and if @options.verbose
31
+ #
32
+ # @param error [CSVPlusPlus::Error, Google::Apis::ClientError, StandardError]
28
33
  def handle_error(error)
29
34
  case error
30
35
  when ::CSVPlusPlus::Error
@@ -37,6 +42,20 @@ module CSVPlusPlus
37
42
  end
38
43
  end
39
44
 
45
+ # Handle the supplied command line options, setting +@options+ or throw an error if anything is invalid
46
+ def parse_options!
47
+ @options = ::CSVPlusPlus::Options.new
48
+ option_parser.parse!
49
+ validate_options
50
+ rescue ::OptionParser::InvalidOption => e
51
+ raise(::CSVPlusPlus::Error, e.message)
52
+ end
53
+
54
+ # @return [String]
55
+ def to_s
56
+ "CLI(options: #{options})"
57
+ end
58
+
40
59
  private
41
60
 
42
61
  def handle_internal_error(error)
@@ -54,12 +73,6 @@ module CSVPlusPlus
54
73
  warn("#{error.status_code} Error making Google API request [#{error.message}]: #{error.body}")
55
74
  end
56
75
 
57
- def parse_options!
58
- @options = ::CSVPlusPlus::Options.new
59
- option_parser.parse!
60
- validate_options
61
- end
62
-
63
76
  def validate_options
64
77
  error_message = @options.validate
65
78
  return if error_message.nil?
@@ -4,10 +4,18 @@ require_relative './google_options'
4
4
 
5
5
  module CSVPlusPlus
6
6
  # Individual CLI flags that a user can supply
7
+ #
8
+ # @attr_reader short_flag [String] A definition of the short/single-character flag
9
+ # @attr_reader long_flag [String] A definition of the long/word-based flag
10
+ # @attr_reader description [String] A description of what the flag does
11
+ # @attr_reader handler [Proc(Options, String)] A proc which is called to handle when this flag is seen
7
12
  class CliFlag
8
13
  attr_reader :short_flag, :long_flag, :description, :handler
9
14
 
10
- # initialize
15
+ # @param short_flag [String] A definition of the short/single-character flag
16
+ # @param long_flag [String] A definition of the long/word-based flag
17
+ # @param description [String] A description of what the flag does
18
+ # @param handler [Proc(Options, String)] A proc which is called to handle when this flag is seen
11
19
  def initialize(short_flag, long_flag, description, handler)
12
20
  @short_flag = short_flag
13
21
  @long_flag = long_flag
@@ -15,7 +23,7 @@ module CSVPlusPlus
15
23
  @handler = handler
16
24
  end
17
25
 
18
- # to_s
26
+ # @return [String]
19
27
  def to_s
20
28
  "#{@short_flag}, #{@long_flag} #{@description}"
21
29
  end
@@ -4,44 +4,63 @@ require_relative './language/code_section.tab'
4
4
  require_relative './language/entities'
5
5
 
6
6
  module CSVPlusPlus
7
- ##
8
7
  # A representation of the code section part of a template (the variable and function definitions)
8
+ #
9
+ # @attr variables [Hash<Symbol, Variable>] All defined variables
10
+ # @attr_reader functions [Hash<Symbol, Function>] All defined functions
9
11
  class CodeSection
10
12
  attr_reader :functions
11
13
  attr_accessor :variables
12
14
 
13
- # initialize
15
+ # @param variables [Hash<Symbol, Variable>] Initial variables
16
+ # @param functions [Hash<Symbol, Variable>] Initial functions
14
17
  def initialize(variables: {}, functions: {})
15
18
  @variables = variables
16
19
  @functions = functions
17
20
  end
18
21
 
19
22
  # Define a (or re-define an existing) variable
23
+ #
24
+ # @param id [String, Symbol] The identifier for the variable
25
+ # @param entity [Entity] The value (entity) the variable holds
20
26
  def def_variable(id, entity)
21
27
  @variables[id.to_sym] = entity
22
28
  end
23
29
 
24
30
  # Define (or re-define existing) variables
31
+ #
32
+ # @param variables [Hash<Symbol, Variable>] Variables to define
25
33
  def def_variables(variables)
26
34
  variables.each { |id, entity| def_variable(id, entity) }
27
35
  end
28
36
 
29
37
  # Define a (or re-define an existing) function
38
+ #
39
+ # @param id [String, Symbol] The identifier for the function
40
+ # @param entity [Entities::Function] The defined function
30
41
  def def_function(id, entity)
31
42
  @functions[id.to_sym] = entity
32
43
  end
33
44
 
34
45
  # Is the variable defined?
46
+ #
47
+ # @param var_id [Symbol, String] The identifier of the variable
48
+ #
49
+ # @return [boolean]
35
50
  def defined_variable?(var_id)
36
51
  @variables.key?(var_id.to_sym)
37
52
  end
38
53
 
39
54
  # Is the function defined?
55
+ #
56
+ # @param fn_id [Symbol, String] The identifier of the function
57
+ #
58
+ # @return [boolean]
40
59
  def defined_function?(fn_id)
41
60
  @functions.key?(fn_id.to_sym)
42
61
  end
43
62
 
44
- # to_s
63
+ # @return [String]
45
64
  def to_s
46
65
  "CodeSection(functions: #{@functions}, variables: #{@variables})"
47
66
  end
@@ -1,11 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CSVPlusPlus
4
- # A color value
4
+ # A color value parsed into it's respective components
5
+ #
6
+ # attr_reader blue_hex [String] The blue value in hex ("FF", "00", "AF", etc)
7
+ # attr_reader green_hex [String] The green value in hex ("FF", "00", "AF", etc)
8
+ # attr_reader red_hex [String] The red value in hex ("FF", "00", "AF", etc)
5
9
  class Color
6
10
  attr_reader :red_hex, :green_hex, :blue_hex
7
11
 
8
12
  # create an instance from a string like "#FFF" or "#FFFFFF"
13
+ #
14
+ # @param hex_string [String] The hex string input to parse
9
15
  def initialize(hex_string)
10
16
  @red_hex, @green_hex, @blue_hex = hex_string
11
17
  .gsub(/^#?/, '')
@@ -15,31 +21,39 @@ module CSVPlusPlus
15
21
  end
16
22
 
17
23
  # The percent (decimal between 0-1) of red
24
+ #
25
+ # @return [Numeric]
18
26
  def red_percent
19
27
  hex_to_percent(@red_hex)
20
28
  end
21
29
 
22
30
  # The percent (decimal between 0-1) of green
31
+ #
32
+ # @return [Numeric]
23
33
  def green_percent
24
34
  hex_to_percent(@green_hex)
25
35
  end
26
36
 
27
37
  # The percent (decimal between 0-1) of blue
38
+ #
39
+ # @return [Numeric]
28
40
  def blue_percent
29
41
  hex_to_percent(@blue_hex)
30
42
  end
31
43
 
32
- # to_hex
44
+ # Create a hex representation of the color (without a '#')
45
+ #
46
+ # @return [String]
33
47
  def to_hex
34
48
  [@red_hex, @green_hex, @blue_hex].join
35
49
  end
36
50
 
37
- # to_s
51
+ # @return [String]
38
52
  def to_s
39
53
  "Color(r: #{@red_hex}, g: #{@green_hex}, b: #{@blue_hex})"
40
54
  end
41
55
 
42
- # ==
56
+ # @return [boolean]
43
57
  def ==(other)
44
58
  other.is_a?(self.class) &&
45
59
  other.red_hex == @red_hex &&
@@ -50,7 +64,7 @@ module CSVPlusPlus
50
64
  private
51
65
 
52
66
  def hex_to_percent(hex)
53
- hex.to_i(16) / 255
67
+ hex.to_i(16) / 255.0
54
68
  end
55
69
  end
56
70
  end
@@ -2,9 +2,13 @@
2
2
 
3
3
  module CSVPlusPlus
4
4
  # The Google-specific options a user can supply
5
+ #
6
+ # attr sheet_id [String] The ID of the Google Sheet to write to
5
7
  GoogleOptions =
6
8
  ::Struct.new(:sheet_id) do
7
- # Return a string with a verbose description of what we're doing with the options
9
+ # Format a string with a verbose description of what we're doing with the options
10
+ #
11
+ # @return [String]
8
12
  def verbose_summary
9
13
  <<~SUMMARY
10
14
  ## Google Sheets Options
@@ -13,7 +17,7 @@ module CSVPlusPlus
13
17
  SUMMARY
14
18
  end
15
19
 
16
- # to_s
20
+ # @return [String]
17
21
  def to_s
18
22
  "GoogleOptions(sheet_id: #{sheet_id})"
19
23
  end
@@ -3,7 +3,6 @@
3
3
  require 'tsort'
4
4
 
5
5
  module CSVPlusPlus
6
- ##
7
6
  # Graph ordering and searching functions
8
7
  module Graph
9
8
  # Get a list of all variables references in a given +ast+
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './entities'
4
+
5
+ module CSVPlusPlus
6
+ module Language
7
+ # Some helpful functions that can be mixed into a class to help building ASTs
8
+ module ASTBuilder
9
+ # Let the current class have functions which can build a given entity by calling it's type. For example
10
+ # +number(1)+, +variable(:foo)+
11
+ #
12
+ # @param method_name [Symbol] The +method_name+ to respond to
13
+ # @param arguments [] The arguments to create the entity with
14
+ #
15
+ # @return [Entity, #super]
16
+ def method_missing(method_name, *arguments)
17
+ entity_class = ::CSVPlusPlus::Language::TYPES[method_name.to_sym]
18
+ return super unless entity_class
19
+
20
+ entity_class.new(*arguments)
21
+ end
22
+
23
+ # Let the current class have functions which can build a given entity by calling it's type. For example
24
+ # +number(1)+, +variable(:foo)+
25
+ #
26
+ # @param method_name [Symbol] The +method_name+ to respond to
27
+ # @param arguments [] The arguments to create the entity with
28
+ #
29
+ # @return [Boolean, #super]
30
+ def respond_to_missing?(method_name, *_arguments)
31
+ ::CSVPlusPlus::Language::TYPES.include?(method_name.to_sym) || super
32
+ end
33
+
34
+ # Turns index-based/X,Y coordinates into a A1 format
35
+ #
36
+ # @param row_index [Integer]
37
+ # @param cell_index [Integer]
38
+ #
39
+ # @return [String]
40
+ def ref(row_index: nil, cell_index: nil)
41
+ return unless row_index || cell_index
42
+
43
+ rowref = row_index ? (row_index + 1).to_s : ''
44
+ cellref = cell_index ? cell_ref(cell_index) : ''
45
+ cell_reference([cellref, rowref].join)
46
+ end
47
+
48
+ private
49
+
50
+ ALPHA = ('A'..'Z').to_a.freeze
51
+ private_constant :ALPHA
52
+
53
+ def cell_ref(cell_index)
54
+ c = cell_index.dup
55
+ ref = ''
56
+
57
+ while c >= 0
58
+ # rubocop:disable Lint/ConstantResolution
59
+ ref += ALPHA[c % 26]
60
+ # rubocop:enable Lint/ConstantResolution
61
+ c = (c / 26).floor - 1
62
+ end
63
+
64
+ ref.reverse
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'benchmark'
4
+
5
+ module CSVPlusPlus
6
+ module Language
7
+ # Extend a +Compiler+ class and add benchmark timings
8
+ # @attr_reader timings [Array<Benchmark::Tms>] +Benchmark+ timings that have been accumulated by each step of
9
+ # compilation
10
+ # @attr_reader benchmark [Benchmark] A +Benchmark+ instance
11
+ module BenchmarkedCompiler
12
+ attr_reader :benchmark, :timings
13
+
14
+ # Wrap a +Compiler+ with our instance methods that add benchmarks
15
+ def self.with_benchmarks(compiler, &block)
16
+ ::Benchmark.benchmark(::Benchmark::CAPTION, 25, ::Benchmark::FORMAT, '> Total') do |x|
17
+ # compiler = new(options:, runtime:, benchmark: x)
18
+ compiler.extend(self)
19
+ compiler.benchmark = x
20
+
21
+ block.call(compiler)
22
+
23
+ [compiler.timings.reduce(:+)]
24
+ end
25
+ end
26
+
27
+ # @param benchmark [Benchmark] A +Benchmark+ instance
28
+ def benchmark=(benchmark)
29
+ @benchmark = benchmark
30
+ @timings = []
31
+ end
32
+
33
+ # Time the Compiler#outputting! stage
34
+ def outputting!
35
+ time_stage('Writing the spreadsheet') { super }
36
+ end
37
+
38
+ protected
39
+
40
+ def parse_code_section!
41
+ time_stage('Parsing code section') { super }
42
+ end
43
+
44
+ def parse_csv_section!
45
+ time_stage('Parsing CSV section') { super }
46
+ end
47
+
48
+ def expanding
49
+ time_stage('Expanding rows') { super }
50
+ end
51
+
52
+ def resolve_all_cells!(template)
53
+ time_stage('Resolving each cell') { super(template) }
54
+ end
55
+
56
+ private
57
+
58
+ def time_stage(stage, &block)
59
+ ret = nil
60
+ @timings << @benchmark.report(stage) { ret = block.call }
61
+ ret
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './ast_builder'
4
+
5
+ module CSVPlusPlus
6
+ module Language
7
+ # Provides ASTs for builtin functions and variables
8
+ module Builtins
9
+ extend ::CSVPlusPlus::Language::ASTBuilder
10
+
11
+ VARIABLES = {
12
+ # The number (integer) of the current cell. Starts at 1
13
+ cellnum: runtime_value(->(runtime) { number(runtime.cell_index + 1) }),
14
+
15
+ # A reference to the current cell
16
+ cellref: runtime_value(->(runtime) { ref(row_index: runtime.row_index, cell_index: runtime.cell_index) }),
17
+
18
+ # A reference to the row above
19
+ rowabove: runtime_value(->(runtime) { ref(row_index: [0, (runtime.row_index - 1)].max) }),
20
+
21
+ # A reference to the row below
22
+ rowbelow: runtime_value(->(runtime) { ref(row_index: runtime.row_index + 1) }),
23
+
24
+ # The number (integer) of the current row. Starts at 1
25
+ rownum: runtime_value(->(runtime) { number(runtime.rownum) }),
26
+
27
+ # A reference to the current row
28
+ rowref: runtime_value(->(runtime) { ref(row_index: runtime.row_index) })
29
+ }.freeze
30
+ public_constant :VARIABLES
31
+
32
+ FUNCTIONS = {
33
+ # TODO: A reference to a cell in a given row?
34
+ # A reference to a cell above the current row
35
+ cellabove: runtime_value(->(runtime, args) { cell_reference([args[0], [1, (runtime.rownum - 1)].max].join) }),
36
+
37
+ # A reference to a cell in the current row
38
+ celladjacent: runtime_value(->(runtime, args) { cell_reference([args[0], runtime.rownum].join) }),
39
+
40
+ # A reference to a cell below the current row
41
+ cellbelow: runtime_value(->(runtime, args) { cell_reference([args[0], runtime.rownum + 1].join) })
42
+ }.freeze
43
+ public_constant :FUNCTIONS
44
+ end
45
+ end
46
+ end
@@ -29,11 +29,10 @@ module_eval(<<'...end cell_value.y/module_eval...', 'cell_value.y', 48)
29
29
  @ast
30
30
  end
31
31
 
32
- def tokenizer(input)
32
+ def tokenizer
33
33
  ::CSVPlusPlus::Lexer::Tokenizer.new(
34
34
  catchall: /[\(\)\/\*\+\-,=&]/,
35
35
  ignore: /\s+/,
36
- input:,
37
36
  tokens: [
38
37
  [/true/i, :TRUE],
39
38
  [/false/i, :FALSE],
@@ -33,11 +33,10 @@ module_eval(<<'...end code_section.y/module_eval...', 'code_section.y', 67)
33
33
  'code section'
34
34
  end
35
35
 
36
- def tokenizer(input)
36
+ def tokenizer
37
37
  ::CSVPlusPlus::Lexer::Tokenizer.new(
38
38
  catchall: /[\(\)\{\}\/\*\+\-,=&]/,
39
39
  ignore: /\s+|\#[^\n]+\n/,
40
- input:,
41
40
  stop_fn: lambda do |scanner|
42
41
  return false unless scanner.scan(/#{::CSVPlusPlus::Lexer::END_OF_CODE_SECTION}/)
43
42