csv_plus_plus 0.1.1 → 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.
Files changed (93) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +18 -63
  3. data/{CHANGELOG.md → docs/CHANGELOG.md} +17 -0
  4. data/lib/csv_plus_plus/benchmarked_compiler.rb +112 -0
  5. data/lib/csv_plus_plus/cell.rb +46 -24
  6. data/lib/csv_plus_plus/cli.rb +44 -17
  7. data/lib/csv_plus_plus/cli_flag.rb +1 -2
  8. data/lib/csv_plus_plus/color.rb +42 -11
  9. data/lib/csv_plus_plus/compiler.rb +178 -0
  10. data/lib/csv_plus_plus/entities/ast_builder.rb +50 -0
  11. data/lib/csv_plus_plus/entities/boolean.rb +40 -0
  12. data/lib/csv_plus_plus/entities/builtins.rb +58 -0
  13. data/lib/csv_plus_plus/entities/cell_reference.rb +231 -0
  14. data/lib/csv_plus_plus/entities/date.rb +63 -0
  15. data/lib/csv_plus_plus/entities/entity.rb +50 -0
  16. data/lib/csv_plus_plus/entities/entity_with_arguments.rb +57 -0
  17. data/lib/csv_plus_plus/entities/function.rb +45 -0
  18. data/lib/csv_plus_plus/entities/function_call.rb +50 -0
  19. data/lib/csv_plus_plus/entities/number.rb +48 -0
  20. data/lib/csv_plus_plus/entities/runtime_value.rb +43 -0
  21. data/lib/csv_plus_plus/entities/string.rb +42 -0
  22. data/lib/csv_plus_plus/entities/variable.rb +37 -0
  23. data/lib/csv_plus_plus/entities.rb +40 -0
  24. data/lib/csv_plus_plus/error/error.rb +20 -0
  25. data/lib/csv_plus_plus/error/formula_syntax_error.rb +37 -0
  26. data/lib/csv_plus_plus/error/modifier_syntax_error.rb +75 -0
  27. data/lib/csv_plus_plus/error/modifier_validation_error.rb +69 -0
  28. data/lib/csv_plus_plus/error/syntax_error.rb +71 -0
  29. data/lib/csv_plus_plus/error/writer_error.rb +17 -0
  30. data/lib/csv_plus_plus/error.rb +10 -2
  31. data/lib/csv_plus_plus/google_api_client.rb +11 -2
  32. data/lib/csv_plus_plus/google_options.rb +23 -18
  33. data/lib/csv_plus_plus/lexer/lexer.rb +17 -6
  34. data/lib/csv_plus_plus/lexer/tokenizer.rb +6 -1
  35. data/lib/csv_plus_plus/lexer.rb +24 -0
  36. data/lib/csv_plus_plus/modifier/conditional_formatting.rb +18 -0
  37. data/lib/csv_plus_plus/modifier/data_validation.rb +138 -0
  38. data/lib/csv_plus_plus/modifier/expand.rb +61 -0
  39. data/lib/csv_plus_plus/modifier/google_sheet_modifier.rb +133 -0
  40. data/lib/csv_plus_plus/modifier/modifier.rb +222 -0
  41. data/lib/csv_plus_plus/modifier/modifier_validator.rb +243 -0
  42. data/lib/csv_plus_plus/modifier/rubyxl_modifier.rb +84 -0
  43. data/lib/csv_plus_plus/modifier.rb +82 -150
  44. data/lib/csv_plus_plus/options.rb +64 -19
  45. data/lib/csv_plus_plus/{language → parser}/cell_value.tab.rb +25 -25
  46. data/lib/csv_plus_plus/{language → parser}/code_section.tab.rb +86 -95
  47. data/lib/csv_plus_plus/parser/modifier.tab.rb +478 -0
  48. data/lib/csv_plus_plus/row.rb +53 -15
  49. data/lib/csv_plus_plus/runtime/can_define_references.rb +87 -0
  50. data/lib/csv_plus_plus/runtime/can_resolve_references.rb +209 -0
  51. data/lib/csv_plus_plus/runtime/graph.rb +68 -0
  52. data/lib/csv_plus_plus/runtime/position_tracker.rb +231 -0
  53. data/lib/csv_plus_plus/runtime/references.rb +110 -0
  54. data/lib/csv_plus_plus/runtime/runtime.rb +126 -0
  55. data/lib/csv_plus_plus/runtime.rb +42 -0
  56. data/lib/csv_plus_plus/source_code.rb +66 -0
  57. data/lib/csv_plus_plus/template.rb +63 -36
  58. data/lib/csv_plus_plus/version.rb +2 -1
  59. data/lib/csv_plus_plus/writer/base_writer.rb +30 -5
  60. data/lib/csv_plus_plus/writer/csv.rb +11 -9
  61. data/lib/csv_plus_plus/writer/excel.rb +9 -2
  62. data/lib/csv_plus_plus/writer/file_backer_upper.rb +7 -4
  63. data/lib/csv_plus_plus/writer/google_sheet_builder.rb +88 -45
  64. data/lib/csv_plus_plus/writer/google_sheets.rb +79 -29
  65. data/lib/csv_plus_plus/writer/open_document.rb +6 -1
  66. data/lib/csv_plus_plus/writer/rubyxl_builder.rb +103 -33
  67. data/lib/csv_plus_plus/writer.rb +39 -9
  68. data/lib/csv_plus_plus.rb +41 -15
  69. metadata +44 -30
  70. data/lib/csv_plus_plus/code_section.rb +0 -101
  71. data/lib/csv_plus_plus/expand.rb +0 -18
  72. data/lib/csv_plus_plus/graph.rb +0 -62
  73. data/lib/csv_plus_plus/language/ast_builder.rb +0 -68
  74. data/lib/csv_plus_plus/language/benchmarked_compiler.rb +0 -65
  75. data/lib/csv_plus_plus/language/builtins.rb +0 -46
  76. data/lib/csv_plus_plus/language/compiler.rb +0 -152
  77. data/lib/csv_plus_plus/language/entities/boolean.rb +0 -33
  78. data/lib/csv_plus_plus/language/entities/cell_reference.rb +0 -33
  79. data/lib/csv_plus_plus/language/entities/entity.rb +0 -86
  80. data/lib/csv_plus_plus/language/entities/function.rb +0 -35
  81. data/lib/csv_plus_plus/language/entities/function_call.rb +0 -37
  82. data/lib/csv_plus_plus/language/entities/number.rb +0 -36
  83. data/lib/csv_plus_plus/language/entities/runtime_value.rb +0 -28
  84. data/lib/csv_plus_plus/language/entities/string.rb +0 -31
  85. data/lib/csv_plus_plus/language/entities/variable.rb +0 -25
  86. data/lib/csv_plus_plus/language/entities.rb +0 -28
  87. data/lib/csv_plus_plus/language/references.rb +0 -70
  88. data/lib/csv_plus_plus/language/runtime.rb +0 -205
  89. data/lib/csv_plus_plus/language/scope.rb +0 -192
  90. data/lib/csv_plus_plus/language/syntax_error.rb +0 -66
  91. data/lib/csv_plus_plus/modifier.tab.rb +0 -907
  92. data/lib/csv_plus_plus/writer/google_sheet_modifier.rb +0 -56
  93. data/lib/csv_plus_plus/writer/rubyxl_modifier.rb +0 -59
@@ -0,0 +1,57 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module CSVPlusPlus
5
+ module Entities
6
+ # An entity that can take other entities as arguments. Current use cases for this
7
+ # are function calls and function definitions
8
+ #
9
+ # @attr_reader arguments [Array<Entity>] The arguments supplied to this entity.
10
+ class EntityWithArguments < Entity
11
+ extend ::T::Sig
12
+
13
+ abstract!
14
+
15
+ sig { returns(::T::Array[::CSVPlusPlus::Entities::Entity]) }
16
+ attr_reader :arguments
17
+
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:)
30
+ @arguments = arguments
31
+ end
32
+
33
+ sig { override.params(other: ::CSVPlusPlus::Entities::Entity).returns(::T::Boolean) }
34
+ # @param other [Entity]
35
+ #
36
+ # @return [boolean]
37
+ 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) }
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,45 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module CSVPlusPlus
5
+ module Entities
6
+ # A function definition
7
+ #
8
+ # @attr_reader body [Entity] The body of the function. +body+ can contain variable references
9
+ # from +@arguments+
10
+ class Function < EntityWithArguments
11
+ extend ::T::Sig
12
+
13
+ sig { returns(::CSVPlusPlus::Entities::Entity) }
14
+ attr_reader :body
15
+
16
+ sig { params(id: ::Symbol, arguments: ::T::Array[::Symbol], body: ::CSVPlusPlus::Entities::Entity).void }
17
+ # @param id [Symbol] the name of the function - what it will be callable by
18
+ # @param arguments [Array<Symbol>]
19
+ # @param body [Entity]
20
+ def initialize(id, arguments, body)
21
+ super(::CSVPlusPlus::Entities::Type::Function, id:, arguments: arguments.map(&:to_sym))
22
+
23
+ @body = ::T.let(body, ::CSVPlusPlus::Entities::Entity)
24
+ end
25
+
26
+ sig { override.params(runtime: ::CSVPlusPlus::Runtime::Runtime).returns(::String) }
27
+ # @param runtime [Runtime]
28
+ #
29
+ # @return [::String]
30
+ def evaluate(runtime)
31
+ "def #{@id.to_s.upcase}(#{arguments.map(&:to_s).join(', ')}) #{@body.evaluate(runtime)}"
32
+ end
33
+
34
+ sig { override.params(other: ::CSVPlusPlus::Entities::Entity).returns(::T::Boolean) }
35
+ # @param other [Entity]
36
+ #
37
+ # @return [::T::Boolean]
38
+ def ==(other)
39
+ return false unless super
40
+
41
+ other.is_a?(self.class) && @body == other.body
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,50 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module CSVPlusPlus
5
+ module Entities
6
+ # A function call that can be either infix (A + B) or prefix (ADD(A, B))
7
+ #
8
+ # @attr_reader infix [boolean] Whether or not this function call is infix (X * Y, A + B, etc)
9
+ class FunctionCall < EntityWithArguments
10
+ extend ::T::Sig
11
+
12
+ sig { returns(::T::Boolean) }
13
+ attr_reader :infix
14
+
15
+ sig { params(id: ::Symbol, arguments: ::T::Array[::CSVPlusPlus::Entities::Entity], infix: ::T::Boolean).void }
16
+ # @param id [::String] The name of the function
17
+ # @param arguments [Array<Entity>] The arguments to the function
18
+ # @param infix [T::Boolean] Whether the function is infix
19
+ def initialize(id, arguments, infix: false)
20
+ super(::CSVPlusPlus::Entities::Type::FunctionCall, id:, arguments:)
21
+
22
+ @infix = infix
23
+ end
24
+
25
+ sig { override.params(runtime: ::CSVPlusPlus::Runtime::Runtime).returns(::String) }
26
+ # @param runtime [Runtime]
27
+ #
28
+ # @return [::String]
29
+ def evaluate(runtime)
30
+ evaluated_arguments = evaluate_arguments(runtime)
31
+
32
+ if @infix
33
+ "(#{evaluated_arguments.join(" #{@id} ")})"
34
+ else
35
+ "#{@id.to_s.upcase}(#{evaluated_arguments.join(', ')})"
36
+ end
37
+ end
38
+
39
+ sig { override.params(other: ::CSVPlusPlus::Entities::Entity).returns(::T::Boolean) }
40
+ # @param other [Entity]
41
+ #
42
+ # @return [boolean]
43
+ def ==(other)
44
+ return false unless super
45
+
46
+ other.is_a?(self.class) && @id == other.id && @infix == other.infix
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,48 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module CSVPlusPlus
5
+ module Entities
6
+ # A number value
7
+ #
8
+ # @attr_reader value [Numeric] The parsed number value
9
+ class Number < Entity
10
+ sig { returns(::Numeric) }
11
+ attr_reader :value
12
+
13
+ sig { params(value: ::T.any(::String, ::Numeric)).void }
14
+ # @param value [String, Numeric] Either a +String+ that looks like a number, or an already parsed Numeric
15
+ def initialize(value)
16
+ super(::CSVPlusPlus::Entities::Type::Number)
17
+
18
+ @value =
19
+ ::T.let(
20
+ (if value.is_a?(::String)
21
+ value.include?('.') ? Float(value) : Integer(value, 10)
22
+ else
23
+ value
24
+ end),
25
+ ::Numeric
26
+ )
27
+ end
28
+
29
+ sig { override.params(_runtime: ::CSVPlusPlus::Runtime::Runtime).returns(::String) }
30
+ # @param _runtime [Runtime]
31
+ #
32
+ # @return [::String]
33
+ def evaluate(_runtime)
34
+ @value.to_s
35
+ end
36
+
37
+ sig { override.params(other: ::CSVPlusPlus::Entities::Entity).returns(::T::Boolean) }
38
+ # @param other [Entity]
39
+ #
40
+ # @return [::T::Boolean]
41
+ def ==(other)
42
+ return false unless super
43
+
44
+ other.is_a?(self.class) && @value == other.value
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,43 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module CSVPlusPlus
5
+ module Entities
6
+ # A runtime value. These are values which can be materialized at any point via the +resolve_fn+
7
+ # which takes an ExecutionContext as a param
8
+ #
9
+ # @attr_reader resolve_fn [lambda] A lambda that is called when the runtime value is resolved
10
+ class RuntimeValue < Entity
11
+ extend ::T::Sig
12
+
13
+ sig { returns(::T::Array[::T.untyped]) }
14
+ attr_reader :arguments
15
+
16
+ sig { returns(::T.proc.params(arg0: ::CSVPlusPlus::Runtime::Runtime).returns(::CSVPlusPlus::Entities::Entity)) }
17
+ attr_reader :resolve_fn
18
+
19
+ sig do
20
+ params(
21
+ resolve_fn: ::T.proc.params(arg0: ::CSVPlusPlus::Runtime::Runtime).returns(::CSVPlusPlus::Entities::Entity),
22
+ arguments: ::T::Array[::T.untyped]
23
+ ).void
24
+ end
25
+ # @param resolve_fn [lambda] A lambda that is called when the runtime value is resolved
26
+ # @param arguments [Any] Arguments to the runtime value call
27
+ def initialize(resolve_fn, arguments: [])
28
+ super(::CSVPlusPlus::Entities::Type::RuntimeValue)
29
+
30
+ @arguments = arguments
31
+ @resolve_fn = resolve_fn
32
+ end
33
+
34
+ sig { override.params(_runtime: ::CSVPlusPlus::Runtime::Runtime).returns(::String) }
35
+ # @param _runtime [Runtime]
36
+ #
37
+ # @return [String]
38
+ def evaluate(_runtime)
39
+ '(runtime value)'
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,42 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module CSVPlusPlus
5
+ module Entities
6
+ # A string value
7
+ #
8
+ # @attr_reader value [String]
9
+ class String < Entity
10
+ extend ::T::Sig
11
+
12
+ sig { returns(::String) }
13
+ attr_reader :value
14
+
15
+ sig { params(value: ::String).void }
16
+ # @param value [String] The string that has been parsed out of the template
17
+ def initialize(value)
18
+ super(::CSVPlusPlus::Entities::Type::String)
19
+
20
+ @value = ::T.let(value.gsub(/^"|"$/, ''), ::String)
21
+ end
22
+
23
+ sig { override.params(_runtime: ::CSVPlusPlus::Runtime::Runtime).returns(::String) }
24
+ # @param _runtime [Runtime]
25
+ #
26
+ # @return [::String]
27
+ def evaluate(_runtime)
28
+ "\"#{@value}\""
29
+ end
30
+
31
+ sig { override.params(other: ::CSVPlusPlus::Entities::Entity).returns(::T::Boolean) }
32
+ # @param other [Entity]
33
+ #
34
+ # @return [T::Boolean]
35
+ def ==(other)
36
+ return false unless super
37
+
38
+ other.is_a?(self.class) && @value == other.value
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,37 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module CSVPlusPlus
5
+ module Entities
6
+ # TODO: get rid of this I think - everything will just be References
7
+ #
8
+ # A reference to a variable
9
+ class Variable < Entity
10
+ extend ::T::Sig
11
+
12
+ sig { params(id: ::Symbol).void }
13
+ # @param id [Symbol] The identifier of the variable
14
+ def initialize(id)
15
+ super(::CSVPlusPlus::Entities::Type::Variable, id:)
16
+ end
17
+
18
+ sig { override.params(_runtime: ::CSVPlusPlus::Runtime::Runtime).returns(::String) }
19
+ # @param _runtime [Runtime]
20
+ #
21
+ # @return [::String]
22
+ def evaluate(_runtime)
23
+ "$$#{@id}"
24
+ end
25
+
26
+ sig { override.params(other: ::CSVPlusPlus::Entities::Entity).returns(::T::Boolean) }
27
+ # @param other [Entity]
28
+ #
29
+ # @return [boolean]
30
+ def ==(other)
31
+ return false unless super
32
+
33
+ other.is_a?(self.class) && @id == other.id
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,40 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require_relative 'entities/entity'
5
+ require_relative 'entities/entity_with_arguments'
6
+
7
+ require_relative 'entities/boolean'
8
+ require_relative 'entities/cell_reference'
9
+ require_relative 'entities/date'
10
+ require_relative 'entities/function'
11
+ require_relative 'entities/function_call'
12
+ require_relative 'entities/number'
13
+ require_relative 'entities/runtime_value'
14
+ require_relative 'entities/string'
15
+ require_relative 'entities/variable'
16
+
17
+ module CSVPlusPlus
18
+ # The entities that form abstract syntax trees which make up the language
19
+ module Entities
20
+ extend ::T::Sig
21
+
22
+ # A primitive type. These all correspond to an implementation of the same name in the CSVPlusPlus::Entities module.
23
+ class Type < ::T::Enum
24
+ enums do
25
+ Boolean = new
26
+ CellReference = new
27
+ Date = new
28
+ Function = new
29
+ FunctionCall = new
30
+ Number = new
31
+ RuntimeValue = new
32
+ String = new
33
+ Variable = new
34
+ end
35
+ end
36
+ end
37
+ end
38
+
39
+ require_relative 'entities/ast_builder'
40
+ require_relative 'entities/builtins'
@@ -0,0 +1,20 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module CSVPlusPlus
5
+ module Error
6
+ # An error thrown by our code (generally to be handled at the top level bin/ command)
7
+ class Error < StandardError
8
+ extend ::T::Sig
9
+ extend ::T::Helpers
10
+
11
+ sig { returns(::String) }
12
+ # Return an error message for display to a command-line user.
13
+ #
14
+ # @return [::String]
15
+ def error_message
16
+ message
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,37 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ require_relative './syntax_error'
5
+
6
+ module CSVPlusPlus
7
+ module Error
8
+ # An error that can be thrown when there is an error parsing a modifier
9
+ #
10
+ # @attr_reader message [::String] A helpful error message
11
+ # @attr_reader bad_input [String] The offending input that caused the error to be thrown
12
+ class FormulaSyntaxError < ::CSVPlusPlus::Error::SyntaxError
13
+ attr_reader :message, :bad_input
14
+
15
+ # You must supply either a +choices+ or +message+
16
+ #
17
+ # @param message [String] A relevant message to show
18
+ # @param bad_input [String] The offending input that caused the error to be thrown
19
+ # @param runtime [Runtime] The current runtime
20
+ # @param wrapped_error [StandardError] The underlying error that caused the syntax error. For example a
21
+ # Racc::ParseError that was thrown
22
+ def initialize(message, bad_input, runtime, wrapped_error: nil)
23
+ @bad_input = bad_input
24
+ @message = message
25
+
26
+ super(runtime, wrapped_error:)
27
+ end
28
+
29
+ # Create a relevant error message given +@bad_input+ and +@message+.
30
+ #
31
+ # @return [::String]
32
+ def error_message
33
+ "#{@message}: \"#{@bad_input}\""
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,75 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require_relative './syntax_error'
5
+
6
+ module CSVPlusPlus
7
+ module Error
8
+ # An Error that wraps a +ModifierValidationError+ with a +Runtime+.
9
+ class ModifierSyntaxError < ::CSVPlusPlus::Error::SyntaxError
10
+ extend ::T::Sig
11
+
12
+ sig { returns(::String) }
13
+ attr_reader :bad_input
14
+
15
+ sig { returns(::String) }
16
+ attr_reader :message
17
+
18
+ sig { returns(::T.nilable(::Symbol)) }
19
+ attr_reader :modifier
20
+
21
+ sig do
22
+ params(
23
+ runtime: ::CSVPlusPlus::Runtime::Runtime,
24
+ modifier_validation_error: ::CSVPlusPlus::Error::ModifierValidationError
25
+ ).returns(::CSVPlusPlus::Error::ModifierSyntaxError)
26
+ end
27
+ # Create a +ModifierSyntaxError+ given a +runtime+ and +ModifierValidationError+.
28
+ #
29
+ # @param runtime [Runtime]
30
+ # @param modifier_validation_error [ModifierValidationError]
31
+ #
32
+ # @return [ModifierSyntaxError]
33
+ def self.from_validation_error(runtime, modifier_validation_error)
34
+ new(
35
+ runtime,
36
+ modifier: modifier_validation_error.modifier,
37
+ bad_input: modifier_validation_error.bad_input,
38
+ message: modifier_validation_error.message,
39
+ wrapped_error: modifier_validation_error
40
+ )
41
+ end
42
+
43
+ sig do
44
+ params(
45
+ runtime: ::CSVPlusPlus::Runtime::Runtime,
46
+ bad_input: ::String,
47
+ message: ::String,
48
+ modifier: ::T.nilable(::Symbol),
49
+ wrapped_error: ::T.nilable(::StandardError)
50
+ ).void
51
+ end
52
+ # @param runtime [Runtime] The current runtime
53
+ # @param wrapped_error [ModifierValidationError] The validtion error that this is wrapping
54
+ def initialize(runtime, bad_input:, message:, modifier: nil, wrapped_error: nil)
55
+ @bad_input = bad_input
56
+ @modifier = modifier
57
+ @message = message
58
+
59
+ super(runtime, wrapped_error:)
60
+ end
61
+
62
+ sig { override.returns(::String) }
63
+ # Create a relevant error message given +@choices+ or +@message+ (one of them must be supplied).
64
+ #
65
+ # @return [::String]
66
+ def error_message
67
+ <<~ERROR_MESSAGE
68
+ Error parsing modifier: [[#{@modifier}=...]]
69
+ Bad input: #{@bad_input}
70
+ Reason: #{@message}
71
+ ERROR_MESSAGE
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,69 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module CSVPlusPlus
5
+ module Error
6
+ # An error that can be thrown when a modifier doesn't pass our validation.
7
+ #
8
+ # @attr_reader modifier [Symbol] The modifier being parsed when the bad input was encountered
9
+ # @attr_reader bad_input [String] The offending input that caused the error to be thrown
10
+ # @attr_reader choices [Array<Symbol>, nil] The choices that +value+ must be one of (but violated)
11
+ # @attr_reader message [String, nil] A relevant message to show
12
+ class ModifierValidationError < ::CSVPlusPlus::Error::Error
13
+ extend ::T::Sig
14
+
15
+ sig { returns(::String) }
16
+ attr_reader :bad_input
17
+
18
+ sig { returns(::T.nilable(::T.class_of(::T::Enum))) }
19
+ attr_reader :choices
20
+
21
+ sig { returns(::String) }
22
+ attr_reader :message
23
+
24
+ sig { returns(::Symbol) }
25
+ attr_reader :modifier
26
+
27
+ sig do
28
+ params(
29
+ modifier: ::Symbol,
30
+ bad_input: ::String,
31
+ choices: ::T.nilable(::T.class_of(::T::Enum)),
32
+ message: ::T.nilable(::String)
33
+ ).void
34
+ end
35
+ # You must supply either a +choices+ or +message+
36
+ #
37
+ # @param modifier [Symbol] The modifier being parsed when the bad input was encountered
38
+ # @param bad_input [String] The offending input that caused the error to be thrown
39
+ # @param choices [Array<Symbol>, nil] The choices that +value+ must be one of (but violated)
40
+ # @param message [String, nil] A relevant message to show
41
+ # rubocop:disable Metrics/MethodLength
42
+ def initialize(modifier, bad_input:, choices: nil, message: nil)
43
+ @bad_input = bad_input
44
+ @choices = choices
45
+ @modifier = modifier
46
+
47
+ @message = ::T.let(
48
+ if @choices
49
+ "must be one of (#{@choices.values.map(&:serialize).join(', ')})"
50
+ else
51
+ ::T.must(message)
52
+ end,
53
+ ::String
54
+ )
55
+
56
+ super(@message)
57
+ end
58
+ # rubocop:enable Metrics/MethodLength
59
+
60
+ sig { returns(::String) }
61
+ # A user-facing error message
62
+ #
63
+ # @return [::String]
64
+ def error_message
65
+ @message
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,71 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module CSVPlusPlus
5
+ module Error
6
+ # An error that can be thrown for various syntax errors
7
+ class SyntaxError < ::CSVPlusPlus::Error::Error
8
+ extend ::T::Sig
9
+
10
+ sig { params(runtime: ::CSVPlusPlus::Runtime::Runtime, wrapped_error: ::T.nilable(::StandardError)).void }
11
+ # @param runtime [Runtime] The current runtime
12
+ # @param wrapped_error [StandardError] The underlying error that caused the syntax error. For example a
13
+ # Racc::ParseError that was thrown
14
+ def initialize(runtime, wrapped_error: nil)
15
+ @runtime = runtime
16
+ @wrapped_error = wrapped_error
17
+
18
+ super()
19
+ end
20
+
21
+ sig { returns(::String) }
22
+ # @return [::String]
23
+ def to_s
24
+ to_trace
25
+ end
26
+
27
+ # TODO: clean up all these different string-formatting error classes
28
+ sig { override.returns(::String) }
29
+ # @return [::String]
30
+ def error_message
31
+ ''
32
+ end
33
+
34
+ sig { returns(::String) }
35
+ # Output a verbose user-helpful string that references the current runtime
36
+ def to_verbose_trace
37
+ warn(@wrapped_error.full_message) if @wrapped_error
38
+ warn((@wrapped_error.backtrace || []).join("\n")) if @wrapped_error&.backtrace
39
+ to_trace
40
+ end
41
+
42
+ sig { returns(::String) }
43
+ # Output a user-helpful string that references the runtime state
44
+ #
45
+ # @return [String]
46
+ def to_trace
47
+ "#{message_prefix}#{cell_index} #{error_message}"
48
+ end
49
+
50
+ private
51
+
52
+ sig { returns(::String) }
53
+ def cell_index
54
+ if @runtime.parsing_csv_section?
55
+ "[#{@runtime.row_index},#{@runtime.cell_index}]"
56
+ else
57
+ ''
58
+ end
59
+ end
60
+
61
+ sig { returns(::String) }
62
+ def message_prefix
63
+ line_number = @runtime.line_number
64
+ filename = @runtime.source_code.filename
65
+
66
+ line_str = ":#{line_number}"
67
+ "#{filename}#{line_str}"
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,17 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module CSVPlusPlus
5
+ module Error
6
+ # An error that can be thrown when writing a spreadsheet
7
+ class WriterError < ::CSVPlusPlus::Error::Error
8
+ extend ::T::Sig
9
+
10
+ sig { override.returns(::String) }
11
+ # @return [::String]
12
+ def error_message
13
+ "Error writing template: #{message}"
14
+ end
15
+ end
16
+ end
17
+ end
@@ -1,7 +1,15 @@
1
+ # typed: strict
1
2
  # frozen_string_literal: true
2
3
 
4
+ require_relative './error/error'
5
+ require_relative './error/formula_syntax_error'
6
+ require_relative './error/modifier_syntax_error'
7
+ require_relative './error/modifier_validation_error'
8
+ require_relative './error/syntax_error'
9
+ require_relative './error/writer_error'
10
+
3
11
  module CSVPlusPlus
4
- # An error thrown by our code (generally to be handled at the top level bin/ command)
5
- class Error < StandardError
12
+ # A module containing errors to be raised
13
+ module Error
6
14
  end
7
15
  end