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.
- 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
|