csv_plus_plus 0.1.3 → 0.2.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 (82) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +8 -3
  3. data/docs/CHANGELOG.md +16 -0
  4. data/lib/csv_plus_plus/a1_reference.rb +202 -0
  5. data/lib/csv_plus_plus/benchmarked_compiler.rb +3 -3
  6. data/lib/csv_plus_plus/cell.rb +1 -35
  7. data/lib/csv_plus_plus/cli.rb +43 -80
  8. data/lib/csv_plus_plus/cli_flag.rb +71 -70
  9. data/lib/csv_plus_plus/color.rb +1 -1
  10. data/lib/csv_plus_plus/compiler.rb +31 -21
  11. data/lib/csv_plus_plus/entities/ast_builder.rb +11 -4
  12. data/lib/csv_plus_plus/entities/boolean.rb +16 -9
  13. data/lib/csv_plus_plus/entities/builtins.rb +68 -40
  14. data/lib/csv_plus_plus/entities/date.rb +14 -11
  15. data/lib/csv_plus_plus/entities/entity.rb +11 -29
  16. data/lib/csv_plus_plus/entities/entity_with_arguments.rb +18 -31
  17. data/lib/csv_plus_plus/entities/function.rb +22 -11
  18. data/lib/csv_plus_plus/entities/function_call.rb +35 -11
  19. data/lib/csv_plus_plus/entities/has_identifier.rb +19 -0
  20. data/lib/csv_plus_plus/entities/number.rb +15 -10
  21. data/lib/csv_plus_plus/entities/reference.rb +77 -0
  22. data/lib/csv_plus_plus/entities/runtime_value.rb +36 -23
  23. data/lib/csv_plus_plus/entities/string.rb +13 -10
  24. data/lib/csv_plus_plus/entities.rb +2 -18
  25. data/lib/csv_plus_plus/error/cli_error.rb +17 -0
  26. data/lib/csv_plus_plus/error/compiler_error.rb +17 -0
  27. data/lib/csv_plus_plus/error/error.rb +18 -5
  28. data/lib/csv_plus_plus/error/formula_syntax_error.rb +12 -13
  29. data/lib/csv_plus_plus/error/modifier_syntax_error.rb +10 -36
  30. data/lib/csv_plus_plus/error/modifier_validation_error.rb +6 -32
  31. data/lib/csv_plus_plus/error/positional_error.rb +15 -0
  32. data/lib/csv_plus_plus/error/writer_error.rb +1 -1
  33. data/lib/csv_plus_plus/error.rb +4 -1
  34. data/lib/csv_plus_plus/error_formatter.rb +111 -0
  35. data/lib/csv_plus_plus/google_api_client.rb +18 -8
  36. data/lib/csv_plus_plus/lexer/racc_lexer.rb +144 -0
  37. data/lib/csv_plus_plus/lexer/tokenizer.rb +53 -17
  38. data/lib/csv_plus_plus/lexer.rb +40 -1
  39. data/lib/csv_plus_plus/modifier/data_validation.rb +1 -1
  40. data/lib/csv_plus_plus/modifier/expand.rb +17 -0
  41. data/lib/csv_plus_plus/modifier.rb +6 -1
  42. data/lib/csv_plus_plus/options/file_options.rb +49 -0
  43. data/lib/csv_plus_plus/options/google_sheets_options.rb +42 -0
  44. data/lib/csv_plus_plus/options/options.rb +97 -0
  45. data/lib/csv_plus_plus/options.rb +22 -110
  46. data/lib/csv_plus_plus/parser/cell_value.tab.rb +65 -66
  47. data/lib/csv_plus_plus/parser/code_section.tab.rb +92 -84
  48. data/lib/csv_plus_plus/parser/modifier.tab.rb +40 -30
  49. data/lib/csv_plus_plus/reader/csv.rb +50 -0
  50. data/lib/csv_plus_plus/reader/google_sheets.rb +129 -0
  51. data/lib/csv_plus_plus/reader/reader.rb +27 -0
  52. data/lib/csv_plus_plus/reader/rubyxl.rb +37 -0
  53. data/lib/csv_plus_plus/reader.rb +14 -0
  54. data/lib/csv_plus_plus/runtime/graph.rb +6 -6
  55. data/lib/csv_plus_plus/runtime/{position_tracker.rb → position.rb} +16 -5
  56. data/lib/csv_plus_plus/runtime/references.rb +32 -27
  57. data/lib/csv_plus_plus/runtime/runtime.rb +73 -67
  58. data/lib/csv_plus_plus/runtime/scope.rb +280 -0
  59. data/lib/csv_plus_plus/runtime.rb +9 -9
  60. data/lib/csv_plus_plus/source_code.rb +14 -9
  61. data/lib/csv_plus_plus/template.rb +17 -12
  62. data/lib/csv_plus_plus/version.rb +1 -1
  63. data/lib/csv_plus_plus/writer/csv.rb +32 -5
  64. data/lib/csv_plus_plus/writer/excel.rb +19 -6
  65. data/lib/csv_plus_plus/writer/file_backer_upper.rb +27 -14
  66. data/lib/csv_plus_plus/writer/google_sheets.rb +23 -129
  67. data/lib/csv_plus_plus/writer/{google_sheet_builder.rb → google_sheets_builder.rb} +39 -55
  68. data/lib/csv_plus_plus/writer/merger.rb +31 -0
  69. data/lib/csv_plus_plus/writer/open_document.rb +16 -2
  70. data/lib/csv_plus_plus/writer/rubyxl_builder.rb +68 -43
  71. data/lib/csv_plus_plus/writer/writer.rb +42 -0
  72. data/lib/csv_plus_plus/writer.rb +58 -19
  73. data/lib/csv_plus_plus.rb +26 -14
  74. metadata +37 -12
  75. data/lib/csv_plus_plus/entities/cell_reference.rb +0 -231
  76. data/lib/csv_plus_plus/entities/variable.rb +0 -37
  77. data/lib/csv_plus_plus/error/syntax_error.rb +0 -71
  78. data/lib/csv_plus_plus/google_options.rb +0 -32
  79. data/lib/csv_plus_plus/lexer/lexer.rb +0 -89
  80. data/lib/csv_plus_plus/runtime/can_define_references.rb +0 -87
  81. data/lib/csv_plus_plus/runtime/can_resolve_references.rb +0 -209
  82. data/lib/csv_plus_plus/writer/base_writer.rb +0 -45
@@ -42,7 +42,7 @@ module CSVPlusPlus
42
42
  red_hex, green_hex, blue_hex = hex_string.strip.match(::CSVPlusPlus::Color::HEX_STRING_REGEXP)
43
43
  &.captures
44
44
  &.map { |s| s.length == 1 ? s + s : s }
45
- raise(::CSVPlusPlus::Error::Error, "Invalid color: #{hex_string}") unless red_hex && green_hex && blue_hex
45
+ raise(::CSVPlusPlus::Error::CompilerError, "Invalid color: #{hex_string}") unless red_hex && green_hex && blue_hex
46
46
 
47
47
  @red_hex = ::T.let(red_hex, ::String)
48
48
  @green_hex = ::T.let(green_hex, ::String)
@@ -11,7 +11,7 @@ module CSVPlusPlus
11
11
  class Compiler
12
12
  extend ::T::Sig
13
13
 
14
- sig { returns(::CSVPlusPlus::Options) }
14
+ sig { returns(::CSVPlusPlus::Options::Options) }
15
15
  attr_reader :options
16
16
 
17
17
  sig { returns(::CSVPlusPlus::Runtime::Runtime) }
@@ -19,7 +19,7 @@ module CSVPlusPlus
19
19
 
20
20
  sig do
21
21
  params(
22
- options: ::CSVPlusPlus::Options,
22
+ options: ::CSVPlusPlus::Options::Options,
23
23
  runtime: ::CSVPlusPlus::Runtime::Runtime,
24
24
  block: ::T.proc.params(arg0: ::CSVPlusPlus::Compiler).void
25
25
  ).void
@@ -37,10 +37,10 @@ module CSVPlusPlus
37
37
  block.call(new(options:, runtime:))
38
38
  end
39
39
  ensure
40
- runtime.cleanup!
40
+ runtime.position.cleanup!
41
41
  end
42
42
 
43
- sig { params(options: ::CSVPlusPlus::Options, runtime: ::CSVPlusPlus::Runtime::Runtime).void }
43
+ sig { params(options: ::CSVPlusPlus::Options::Options, runtime: ::CSVPlusPlus::Runtime::Runtime).void }
44
44
  # @param options [Options]
45
45
  # @param runtime [Runtime]
46
46
  def initialize(options:, runtime:)
@@ -49,7 +49,7 @@ module CSVPlusPlus
49
49
 
50
50
  # TODO: infer a type
51
51
  # allow user-supplied key/values to override anything global or from the code section
52
- @runtime.def_variables(
52
+ @runtime.scope.def_variables(
53
53
  options.key_values.transform_values { |v| ::CSVPlusPlus::Entities::String.new(v.to_s) }
54
54
  )
55
55
  end
@@ -72,17 +72,17 @@ module CSVPlusPlus
72
72
  rows = parse_csv_section!
73
73
 
74
74
  ::CSVPlusPlus::Template.new(rows:, runtime: @runtime).tap do |t|
75
- t.validate_infinite_expands(@runtime)
75
+ t.validate_infinite_expands
76
76
  expanding! { t.expand_rows! }
77
77
  bind_all_vars! { t.bind_all_vars!(@runtime) }
78
78
  resolve_all_cells!(t)
79
79
  end
80
80
  end
81
81
 
82
- sig { params(block: ::T.proc.params(runtime: ::CSVPlusPlus::Runtime::Runtime).void).void }
82
+ sig { params(block: ::T.proc.params(position: ::CSVPlusPlus::Runtime::Position).void).void }
83
83
  # Write the compiled results
84
84
  def outputting!(&block)
85
- @runtime.start_at_csv! { block.call(@runtime) }
85
+ @runtime.start_at_csv! { block.call(@runtime.position) }
86
86
  end
87
87
 
88
88
  protected
@@ -94,7 +94,7 @@ module CSVPlusPlus
94
94
  # TODO: this flow can probably be refactored, it used to have more needs back when we had to
95
95
  # parse and save the code_section
96
96
  parsing_code_section do |input|
97
- csv_section = ::CSVPlusPlus::Parser::CodeSection.new.parse(input, @runtime)
97
+ csv_section = ::CSVPlusPlus::Parser::CodeSection.new(@runtime.scope).parse(input)
98
98
 
99
99
  # return the csv_section to the caller because they're gonna re-write input with it
100
100
  next csv_section
@@ -108,16 +108,19 @@ module CSVPlusPlus
108
108
  # @return [Array<Row>]
109
109
  def parse_csv_section!
110
110
  @runtime.start_at_csv! do
111
- @runtime.map_lines(::CSV.new(::T.unsafe(@runtime.input))) do |csv_row|
111
+ @runtime.position.map_lines(::CSV.new(::T.unsafe(@runtime.position.input))) do |csv_row|
112
112
  parse_row(::T.cast(csv_row, ::T::Array[::String]))
113
113
  end
114
114
  end
115
115
  ensure
116
116
  # we're done with the file and everything is in memory
117
- @runtime.cleanup!
117
+ @runtime.position.cleanup!
118
118
  end
119
119
 
120
- sig { params(template: ::CSVPlusPlus::Template).returns(::T::Array[::T::Array[::CSVPlusPlus::Entities::Entity]]) }
120
+ sig do
121
+ params(template: ::CSVPlusPlus::Template)
122
+ .returns(::T::Array[::T::Array[::T.nilable(::CSVPlusPlus::Entities::Entity)]])
123
+ end
121
124
  # Iterates through each cell of each row and resolves it's variable and function references.
122
125
  #
123
126
  # @param template [Template]
@@ -125,8 +128,8 @@ module CSVPlusPlus
125
128
  # @return [Array<Entity>]
126
129
  def resolve_all_cells!(template)
127
130
  @runtime.start_at_csv! do
128
- @runtime.map_all_cells(template.rows) do |cell|
129
- cell.ast = @runtime.resolve_cell_value if cell.ast
131
+ @runtime.position.map_all_cells(template.rows) do |cell|
132
+ cell.ast = @runtime.resolve_cell_value(::T.must(cell.ast)) if cell.ast
130
133
  end
131
134
  end
132
135
  end
@@ -147,8 +150,8 @@ module CSVPlusPlus
147
150
 
148
151
  sig { params(block: ::T.proc.params(arg0: ::String).returns(::String)).void }
149
152
  def parsing_code_section(&block)
150
- csv_section = block.call(::T.must(::T.must(@runtime.input).read))
151
- @runtime.rewrite_input!(csv_section)
153
+ csv_section = block.call(::T.must(::T.must(@runtime.position.input).read))
154
+ @runtime.position.rewrite_input!(csv_section)
152
155
  end
153
156
 
154
157
  sig { params(csv_row: ::T::Array[::T.nilable(::String)]).returns(::CSVPlusPlus::Row) }
@@ -161,17 +164,24 @@ module CSVPlusPlus
161
164
  def parse_row(csv_row)
162
165
  row_modifier = ::CSVPlusPlus::Modifier.new(@options, row_level: true)
163
166
 
164
- cells = @runtime.map_row(csv_row) { |value, _cell_index| parse_cell(value || '', row_modifier) }
167
+ cells = @runtime.position.map_row(csv_row) { |value, _cell_index| parse_cell(value || '', row_modifier) }
165
168
 
166
- ::CSVPlusPlus::Row.new(cells:, index: @runtime.row_index, modifier: row_modifier)
169
+ ::CSVPlusPlus::Row.new(cells:, index: @runtime.position.row_index, modifier: row_modifier)
167
170
  end
168
171
 
169
172
  sig { params(value: ::String, row_modifier: ::CSVPlusPlus::Modifier::Modifier).returns(::CSVPlusPlus::Cell) }
170
173
  def parse_cell(value, row_modifier)
171
174
  cell_modifier = ::CSVPlusPlus::Modifier.new(@options)
172
- parsed_value = ::CSVPlusPlus::Parser::Modifier.new(cell_modifier:, row_modifier:).parse(value, @runtime)
173
-
174
- ::CSVPlusPlus::Cell.parse(parsed_value, runtime:, modifier: cell_modifier)
175
+ parsed_value = ::CSVPlusPlus::Parser::Modifier.new(cell_modifier:, row_modifier:).parse(value)
176
+
177
+ ::CSVPlusPlus::Cell.new(
178
+ value: parsed_value,
179
+ row_index: @runtime.position.row_index,
180
+ index: @runtime.position.cell_index,
181
+ modifier: cell_modifier
182
+ ).tap do |c|
183
+ c.ast = ::CSVPlusPlus::Parser::CellValue.new.parse(parsed_value) unless parsed_value.nil?
184
+ end
175
185
  end
176
186
  end
177
187
  # rubocop:enable Metrics/ClassLength
@@ -25,8 +25,7 @@ module CSVPlusPlus
25
25
  # @return [Entity, #super]
26
26
  # rubocop:disable Naming/BlockForwarding
27
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)
28
+ ::CSVPlusPlus::Entities.const_get(snake_case_to_class_name(method_name)).new(*args, **kwargs, &block)
30
29
  rescue ::NameError
31
30
  super
32
31
  end
@@ -41,10 +40,18 @@ module CSVPlusPlus
41
40
  #
42
41
  # @return [::T::Boolean, #super]
43
42
  def respond_to_missing?(method_name, *_args)
44
- !::CSVPlusPlus::Entities::Type.deserialize(method_name.to_s.gsub('_', '')).nil?
45
- rescue ::KeyError
43
+ ::CSVPlusPlus::Entities.const_get(snake_case_to_class_name(method_name))
44
+ true
45
+ rescue ::NameError
46
46
  super
47
47
  end
48
+
49
+ private
50
+
51
+ sig { params(method_name: ::Symbol).returns(::Symbol) }
52
+ def snake_case_to_class_name(method_name)
53
+ method_name.to_s.split('_').map(&:capitalize).join.to_sym
54
+ end
48
55
  end
49
56
  end
50
57
  end
@@ -6,34 +6,41 @@ module CSVPlusPlus
6
6
  # A boolean value
7
7
  #
8
8
  # @attr_reader value [true, false]
9
- class Boolean < Entity
9
+ class Boolean < ::CSVPlusPlus::Entities::Entity
10
+ extend ::T::Sig
11
+
10
12
  sig { returns(::T::Boolean) }
11
13
  attr_reader :value
12
14
 
13
15
  sig { params(value: ::T.any(::String, ::T::Boolean)).void }
14
16
  # @param value [::String, boolean]
15
17
  def initialize(value)
16
- super(::CSVPlusPlus::Entities::Type::Boolean)
18
+ super()
17
19
  # TODO: probably can do a lot better in general on type validation
18
20
  @value = ::T.let(value.is_a?(::String) ? (value.downcase == 'true') : value, ::T::Boolean)
19
21
  end
20
22
 
21
- sig { override.params(_runtime: ::CSVPlusPlus::Runtime::Runtime).returns(::String) }
22
- # @param _runtime [Runtime]
23
+ sig do
24
+ override.params(_position: ::CSVPlusPlus::Runtime::Position).returns(::String)
25
+ end
26
+ # @param _position [Position]
23
27
  #
24
28
  # @return [::String]
25
- def evaluate(_runtime)
29
+ def evaluate(_position)
26
30
  @value.to_s.upcase
27
31
  end
28
32
 
29
- sig { override.params(other: ::CSVPlusPlus::Entities::Entity).returns(::T::Boolean) }
33
+ sig { override.params(other: ::BasicObject).returns(::T::Boolean) }
30
34
  # @param other [Entity]
31
35
  #
32
36
  # @return [::T::Boolean]
33
37
  def ==(other)
34
- return false unless super
35
-
36
- other.is_a?(self.class) && value == other.value
38
+ case other
39
+ when self.class
40
+ value == other.value
41
+ else
42
+ false
43
+ end
37
44
  end
38
45
  end
39
46
  end
@@ -1,58 +1,86 @@
1
- # typed: false
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module CSVPlusPlus
5
5
  module Entities
6
- # Provides ASTs for builtin functions and variables
6
+ # Provides +RuntimeValue+s for builtin functions and variables
7
7
  module Builtins
8
+ extend ::T::Sig
9
+
8
10
  extend ::CSVPlusPlus::Entities::ASTBuilder
9
11
 
10
- VARIABLES = {
11
- # The number (integer) of the current cell. Starts at 1
12
- cellnum: runtime_value(->(r) { number(r.cell_index + 1) }),
12
+ VARIABLES = ::T.let(
13
+ {
14
+ # The number (integer) of the current cell. Starts at 1
15
+ cellnum: runtime_value(->(p, _args) { number(p.cell_index + 1) }),
13
16
 
14
- # A reference to the current cell
15
- cellref: runtime_value(->(r) { cell_reference(row_index: r.row_index, cell_index: r.cell_index) }),
17
+ # A reference to the current cell
18
+ cellref: runtime_value(->(p, _args) { cell_ref(p.row_index, p.cell_index) }),
16
19
 
17
- # A reference to the row above
18
- rowabove: runtime_value(->(r) { cell_reference(row_index: [0, (r.row_index - 1)].max) }),
20
+ # A reference to the row above
21
+ rowabove: runtime_value(->(p, _args) { cell_ref([0, (p.row_index - 1)].max) }),
19
22
 
20
- # A reference to the row below
21
- rowbelow: runtime_value(->(r) { cell_reference(row_index: r.row_index + 1) }),
23
+ # A reference to the row below
24
+ rowbelow: runtime_value(->(p, _args) { cell_ref(p.row_index + 1) }),
22
25
 
23
- # The number (integer) of the current row. Starts at 1
24
- rownum: runtime_value(->(r) { number(r.rownum) }),
26
+ # The number (integer) of the current row. Starts at 1
27
+ rownum: runtime_value(->(p, _args) { number(p.rownum) }),
25
28
 
26
- # A reference to the current row
27
- rowref: runtime_value(->(r) { cell_reference(row_index: r.row_index) })
28
- }.freeze
29
+ # A reference to the current row
30
+ rowref: runtime_value(->(p, _args) { cell_ref(p.row_index) })
31
+ }.freeze,
32
+ ::T::Hash[::Symbol, ::CSVPlusPlus::Entities::RuntimeValue]
33
+ )
29
34
  public_constant :VARIABLES
30
35
 
31
- FUNCTIONS = {
32
- # TODO: A reference to a cell in a given row?
33
- # A reference to a cell above the current row
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
- ),
40
-
41
- # A reference to a cell in the current row
42
- celladjacent: runtime_value(
43
- lambda { |r, args|
44
- cell_reference(cell_index: args[0].cell_index, row_index: r.row_index)
45
- }
46
- ),
47
-
48
- # A reference to a cell below the current row
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
- )
54
- }.freeze
36
+ # TODO: A reference to a cell in a given row?
37
+ # TODO: check types of the args and throw a more friendly error?
38
+ FUNCTIONS = ::T.let(
39
+ {
40
+ # A reference to a cell above the current row
41
+ cellabove: runtime_value(->(p, args) { cell_ref([0, (p.row_index - 1)].max, args[0].a1_ref.cell_index) }),
42
+
43
+ # A reference to a cell in the current row
44
+ celladjacent: runtime_value(->(p, args) { cell_ref(p.row_index, args[0].a1_ref.cell_index) }),
45
+
46
+ # A reference to a cell below the current row
47
+ cellbelow: runtime_value(->(p, args) { cell_ref(p.row_index + 1, args[0].a1_ref.cell_index) })
48
+ }.freeze,
49
+ ::T::Hash[::Symbol, ::CSVPlusPlus::Entities::RuntimeValue]
50
+ )
55
51
  public_constant :FUNCTIONS
52
+
53
+ sig { params(fn_id: ::Symbol).returns(::T::Boolean) }
54
+ # Is +fn_id+ a builtin function?
55
+ #
56
+ # @param fn_id [Symbol] The Function#id to check if it's a runtime variable
57
+ #
58
+ # @return [T::Boolean]
59
+ def self.builtin_function?(fn_id)
60
+ ::CSVPlusPlus::Entities::Builtins::FUNCTIONS.key?(fn_id)
61
+ end
62
+
63
+ sig { params(var_id: ::Symbol).returns(::T::Boolean) }
64
+ # Is +var_id+ a builtin variable?
65
+ #
66
+ # @param var_id [Symbol] The Variable#id to check if it's a runtime variable
67
+ #
68
+ # @return [Boolean]
69
+ def self.builtin_variable?(var_id)
70
+ ::CSVPlusPlus::Entities::Builtins::VARIABLES.key?(var_id)
71
+ end
72
+
73
+ sig do
74
+ params(row_index: ::Integer, cell_index: ::T.nilable(::Integer)).returns(::CSVPlusPlus::Entities::Reference)
75
+ end
76
+ # @param row_index [Integer]
77
+ # @param cell_index [Integer, nil]
78
+ #
79
+ # @return [Runtime::Reference]
80
+ def self.cell_ref(row_index, cell_index = nil)
81
+ ::CSVPlusPlus::Entities::Reference.new(a1_ref: ::CSVPlusPlus::A1Reference.new(row_index:, cell_index:))
82
+ end
83
+ private_class_method :cell_ref
56
84
  end
57
85
  end
58
86
  end
@@ -6,7 +6,7 @@ module CSVPlusPlus
6
6
  # A date value
7
7
  #
8
8
  # @attr_reader value [Date] The parsed date
9
- class Date < Entity
9
+ class Date < ::CSVPlusPlus::Entities::Entity
10
10
  extend ::T::Sig
11
11
 
12
12
  sig { returns(::Date) }
@@ -30,7 +30,7 @@ module CSVPlusPlus
30
30
  sig { params(value: ::String).void }
31
31
  # @param value [::String] The user-inputted date value
32
32
  def initialize(value)
33
- super(::CSVPlusPlus::Entities::Type::Date)
33
+ super()
34
34
 
35
35
  parsed =
36
36
  begin
@@ -41,22 +41,25 @@ module CSVPlusPlus
41
41
  @value = ::T.let(parsed, ::Date)
42
42
  end
43
43
 
44
- sig { override.params(_runtime: ::CSVPlusPlus::Runtime::Runtime).returns(::String) }
45
- # @param _runtime [Runtime]
44
+ sig { override.params(_position: ::CSVPlusPlus::Runtime::Position).returns(::String) }
45
+ # @param _position [Position]
46
46
  #
47
47
  # @return [::String]
48
- def evaluate(_runtime)
48
+ def evaluate(_position)
49
49
  @value.strftime('%m/%d/%y')
50
50
  end
51
51
 
52
- sig { override.params(other: ::CSVPlusPlus::Entities::Entity).returns(::T::Boolean) }
53
- # @param other [Entity]
52
+ sig { override.params(other: ::BasicObject).returns(::T::Boolean) }
53
+ # @param other [BasicObject]
54
54
  #
55
- # @return [T::Boolean]
55
+ # @return [Boolean]
56
56
  def ==(other)
57
- return false unless super
58
-
59
- other.is_a?(self.class) && other.value == @value
57
+ case other
58
+ when self.class
59
+ other.value == @value
60
+ else
61
+ false
62
+ end
60
63
  end
61
64
  end
62
65
  end
@@ -3,48 +3,30 @@
3
3
 
4
4
  module CSVPlusPlus
5
5
  module Entities
6
- # A basic building block of the abstract syntax tree (AST)
7
- #
8
- # @attr_reader id [Symbol] The identifier of the entity. For functions this is the function name,
9
- # for variables it's the variable name
10
- # @attr_reader type [Entities::Type] The type of the entity. Each type should have a corresponding class definition
11
- # in CSVPlusPlus::Entities
6
+ # All classes that are a part of an AST must implement this interface
12
7
  class Entity
13
8
  extend ::T::Sig
14
9
  extend ::T::Helpers
15
10
 
16
11
  abstract!
17
12
 
18
- sig { returns(::T.nilable(::Symbol)) }
19
- attr_reader :id
20
-
21
- sig { returns(::CSVPlusPlus::Entities::Type) }
22
- attr_reader :type
23
-
24
- sig { params(type: ::CSVPlusPlus::Entities::Type, id: ::T.nilable(::Symbol)).void }
25
- # @param type [Entities::Type]
26
- # @param id [Symbol, nil]
27
- def initialize(type, id: nil)
28
- @type = type
29
- @id = ::T.let(id&.downcase&.to_sym || nil, ::T.nilable(::Symbol))
30
- end
31
-
32
- sig { overridable.params(other: ::CSVPlusPlus::Entities::Entity).returns(::T::Boolean) }
33
- # Each class should define it's own version of #==
13
+ sig { abstract.params(other: ::BasicObject).returns(::T::Boolean) }
14
+ # Each node in the AST needs to implement #== so we can compare entities for equality
15
+ #
34
16
  # @param other [Entity]
35
17
  #
36
18
  # @return [boolean]
37
- def ==(other)
38
- self.class == other.class && @type == other.type && @id == other.id
39
- end
19
+ def ==(other); end
40
20
 
41
- sig { abstract.params(_runtime: ::CSVPlusPlus::Runtime::Runtime).returns(::String) }
42
- # Uses the given +runtime+ to evaluate itself in the current context
21
+ sig do
22
+ abstract.params(position: ::CSVPlusPlus::Runtime::Position).returns(::String)
23
+ end
24
+ # Uses the given +position+ to evaluate itself in the current context
43
25
  #
44
- # @param _runtime [Runtime] The current runtime
26
+ # @param position [Position] The current runtime
45
27
  #
46
28
  # @return [::String]
47
- def evaluate(_runtime); end
29
+ def evaluate(position); end
48
30
  end
49
31
  end
50
32
  end
@@ -7,50 +7,37 @@ module CSVPlusPlus
7
7
  # are function calls and function definitions
8
8
  #
9
9
  # @attr_reader arguments [Array<Entity>] The arguments supplied to this entity.
10
- class EntityWithArguments < Entity
10
+ class EntityWithArguments < ::CSVPlusPlus::Entities::Entity
11
11
  extend ::T::Sig
12
+ extend ::T::Helpers
13
+ extend ::T::Generic
12
14
 
13
15
  abstract!
14
16
 
15
- sig { returns(::T::Array[::CSVPlusPlus::Entities::Entity]) }
17
+ ArgumentsType = type_member
18
+ public_constant :ArgumentsType
19
+
20
+ sig { returns(::T::Array[::CSVPlusPlus::Entities::EntityWithArguments::ArgumentsType]) }
16
21
  attr_reader :arguments
17
22
 
18
- sig do
19
- params(
20
- type: ::CSVPlusPlus::Entities::Type,
21
- id: ::T.nilable(::Symbol),
22
- arguments: ::T::Array[::CSVPlusPlus::Entities::Entity]
23
- ).void
24
- end
25
- # @param type [Entities::Type]
26
- # @param id [::String]
27
- # @param arguments [Array<Entity>]
28
- def initialize(type, id: nil, arguments: [])
29
- super(type, id:)
23
+ sig { params(arguments: ::T::Array[::CSVPlusPlus::Entities::EntityWithArguments::ArgumentsType]).void }
24
+ # @param arguments [Array<ArgumentsType>]
25
+ def initialize(arguments: [])
26
+ super()
30
27
  @arguments = arguments
31
28
  end
32
29
 
33
- sig { override.params(other: ::CSVPlusPlus::Entities::Entity).returns(::T::Boolean) }
30
+ sig { override.params(other: ::BasicObject).returns(::T::Boolean) }
34
31
  # @param other [Entity]
35
32
  #
36
33
  # @return [boolean]
37
34
  def ==(other)
38
- return false unless other.is_a?(self.class)
39
-
40
- @arguments == other.arguments && super
41
- end
42
-
43
- protected
44
-
45
- sig do
46
- params(arguments: ::T::Array[::CSVPlusPlus::Entities::Entity])
47
- .returns(::T::Array[::CSVPlusPlus::Entities::Entity])
48
- end
49
- attr_writer :arguments
50
-
51
- sig { params(runtime: ::CSVPlusPlus::Runtime::Runtime).returns(::T::Array[::String]) }
52
- def evaluate_arguments(runtime)
53
- @arguments.map { |arg| arg.evaluate(runtime) }
35
+ case other
36
+ when self.class
37
+ @arguments == other.arguments
38
+ else
39
+ false
40
+ end
54
41
  end
55
42
  end
56
43
  end
@@ -7,8 +7,15 @@ module CSVPlusPlus
7
7
  #
8
8
  # @attr_reader body [Entity] The body of the function. +body+ can contain variable references
9
9
  # from +@arguments+
10
- class Function < EntityWithArguments
10
+ class Function < ::CSVPlusPlus::Entities::EntityWithArguments
11
11
  extend ::T::Sig
12
+ include ::CSVPlusPlus::Entities::HasIdentifier
13
+
14
+ ArgumentsType = type_member { { fixed: ::Symbol } }
15
+ public_constant :ArgumentsType
16
+
17
+ sig { returns(::Symbol) }
18
+ attr_reader :id
12
19
 
13
20
  sig { returns(::CSVPlusPlus::Entities::Entity) }
14
21
  attr_reader :body
@@ -18,27 +25,31 @@ module CSVPlusPlus
18
25
  # @param arguments [Array<Symbol>]
19
26
  # @param body [Entity]
20
27
  def initialize(id, arguments, body)
21
- super(::CSVPlusPlus::Entities::Type::Function, id:, arguments: arguments.map(&:to_sym))
28
+ super(arguments: arguments.map(&:to_sym))
22
29
 
23
30
  @body = ::T.let(body, ::CSVPlusPlus::Entities::Entity)
31
+ @id = ::T.let(identifier(id), ::Symbol)
24
32
  end
25
33
 
26
- sig { override.params(runtime: ::CSVPlusPlus::Runtime::Runtime).returns(::String) }
27
- # @param runtime [Runtime]
34
+ sig { override.params(position: ::CSVPlusPlus::Runtime::Position).returns(::String) }
35
+ # @param position [Position]
28
36
  #
29
- # @return [::String]
30
- def evaluate(runtime)
31
- "def #{@id.to_s.upcase}(#{arguments.map(&:to_s).join(', ')}) #{@body.evaluate(runtime)}"
37
+ # @return [String]
38
+ def evaluate(position)
39
+ "def #{@id.to_s.upcase}(#{arguments.map(&:to_s).join(', ')}) #{@body.evaluate(position)}"
32
40
  end
33
41
 
34
- sig { override.params(other: ::CSVPlusPlus::Entities::Entity).returns(::T::Boolean) }
42
+ sig { override.params(other: ::BasicObject).returns(::T::Boolean) }
35
43
  # @param other [Entity]
36
44
  #
37
45
  # @return [::T::Boolean]
38
46
  def ==(other)
39
- return false unless super
40
-
41
- other.is_a?(self.class) && @body == other.body
47
+ case other
48
+ when self.class
49
+ @body == other.body && super
50
+ else
51
+ false
52
+ end
42
53
  end
43
54
  end
44
55
  end