csv_plus_plus 0.1.1 → 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 +18 -63
- data/{CHANGELOG.md → docs/CHANGELOG.md} +17 -0
- data/lib/csv_plus_plus/benchmarked_compiler.rb +112 -0
- data/lib/csv_plus_plus/cell.rb +46 -24
- data/lib/csv_plus_plus/cli.rb +44 -17
- data/lib/csv_plus_plus/cli_flag.rb +1 -2
- data/lib/csv_plus_plus/color.rb +42 -11
- data/lib/csv_plus_plus/compiler.rb +178 -0
- data/lib/csv_plus_plus/entities/ast_builder.rb +50 -0
- data/lib/csv_plus_plus/entities/boolean.rb +40 -0
- data/lib/csv_plus_plus/entities/builtins.rb +58 -0
- data/lib/csv_plus_plus/entities/cell_reference.rb +231 -0
- data/lib/csv_plus_plus/entities/date.rb +63 -0
- data/lib/csv_plus_plus/entities/entity.rb +50 -0
- data/lib/csv_plus_plus/entities/entity_with_arguments.rb +57 -0
- data/lib/csv_plus_plus/entities/function.rb +45 -0
- data/lib/csv_plus_plus/entities/function_call.rb +50 -0
- data/lib/csv_plus_plus/entities/number.rb +48 -0
- data/lib/csv_plus_plus/entities/runtime_value.rb +43 -0
- data/lib/csv_plus_plus/entities/string.rb +42 -0
- data/lib/csv_plus_plus/entities/variable.rb +37 -0
- data/lib/csv_plus_plus/entities.rb +40 -0
- data/lib/csv_plus_plus/error/error.rb +20 -0
- data/lib/csv_plus_plus/error/formula_syntax_error.rb +37 -0
- data/lib/csv_plus_plus/error/modifier_syntax_error.rb +75 -0
- data/lib/csv_plus_plus/error/modifier_validation_error.rb +69 -0
- data/lib/csv_plus_plus/error/syntax_error.rb +71 -0
- data/lib/csv_plus_plus/error/writer_error.rb +17 -0
- data/lib/csv_plus_plus/error.rb +10 -2
- data/lib/csv_plus_plus/google_api_client.rb +11 -2
- data/lib/csv_plus_plus/google_options.rb +23 -18
- data/lib/csv_plus_plus/lexer/lexer.rb +17 -6
- 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 +18 -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 -150
- data/lib/csv_plus_plus/options.rb +64 -19
- data/lib/csv_plus_plus/{language → parser}/cell_value.tab.rb +25 -25
- data/lib/csv_plus_plus/{language → parser}/code_section.tab.rb +86 -95
- data/lib/csv_plus_plus/parser/modifier.tab.rb +478 -0
- data/lib/csv_plus_plus/row.rb +53 -15
- 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 +42 -0
- data/lib/csv_plus_plus/source_code.rb +66 -0
- data/lib/csv_plus_plus/template.rb +63 -36
- 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 +7 -4
- data/lib/csv_plus_plus/writer/google_sheet_builder.rb +88 -45
- 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 -33
- data/lib/csv_plus_plus/writer.rb +39 -9
- data/lib/csv_plus_plus.rb +41 -15
- metadata +44 -30
- data/lib/csv_plus_plus/code_section.rb +0 -101
- data/lib/csv_plus_plus/expand.rb +0 -18
- data/lib/csv_plus_plus/graph.rb +0 -62
- data/lib/csv_plus_plus/language/ast_builder.rb +0 -68
- data/lib/csv_plus_plus/language/benchmarked_compiler.rb +0 -65
- data/lib/csv_plus_plus/language/builtins.rb +0 -46
- data/lib/csv_plus_plus/language/compiler.rb +0 -152
- data/lib/csv_plus_plus/language/entities/boolean.rb +0 -33
- data/lib/csv_plus_plus/language/entities/cell_reference.rb +0 -33
- data/lib/csv_plus_plus/language/entities/entity.rb +0 -86
- data/lib/csv_plus_plus/language/entities/function.rb +0 -35
- data/lib/csv_plus_plus/language/entities/function_call.rb +0 -37
- data/lib/csv_plus_plus/language/entities/number.rb +0 -36
- data/lib/csv_plus_plus/language/entities/runtime_value.rb +0 -28
- data/lib/csv_plus_plus/language/entities/string.rb +0 -31
- data/lib/csv_plus_plus/language/entities/variable.rb +0 -25
- data/lib/csv_plus_plus/language/entities.rb +0 -28
- data/lib/csv_plus_plus/language/references.rb +0 -70
- data/lib/csv_plus_plus/language/runtime.rb +0 -205
- data/lib/csv_plus_plus/language/scope.rb +0 -192
- data/lib/csv_plus_plus/language/syntax_error.rb +0 -66
- data/lib/csv_plus_plus/modifier.tab.rb +0 -907
- data/lib/csv_plus_plus/writer/google_sheet_modifier.rb +0 -56
- 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
|
data/lib/csv_plus_plus/error.rb
CHANGED
@@ -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
|
-
#
|
5
|
-
|
12
|
+
# A module containing errors to be raised
|
13
|
+
module Error
|
6
14
|
end
|
7
15
|
end
|