csv_plus_plus 0.1.3 → 0.2.1
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 +13 -3
- data/docs/CHANGELOG.md +18 -0
- data/lib/csv_plus_plus/a1_reference.rb +202 -0
- data/lib/csv_plus_plus/benchmarked_compiler.rb +3 -3
- data/lib/csv_plus_plus/cell.rb +1 -35
- data/lib/csv_plus_plus/cli.rb +43 -80
- data/lib/csv_plus_plus/cli_flag.rb +77 -70
- data/lib/csv_plus_plus/color.rb +1 -1
- data/lib/csv_plus_plus/compiler.rb +31 -21
- data/lib/csv_plus_plus/entities/ast_builder.rb +11 -4
- data/lib/csv_plus_plus/entities/boolean.rb +16 -9
- data/lib/csv_plus_plus/entities/builtins.rb +68 -40
- data/lib/csv_plus_plus/entities/date.rb +14 -11
- data/lib/csv_plus_plus/entities/entity.rb +11 -29
- data/lib/csv_plus_plus/entities/entity_with_arguments.rb +18 -31
- data/lib/csv_plus_plus/entities/function.rb +22 -11
- data/lib/csv_plus_plus/entities/function_call.rb +35 -11
- data/lib/csv_plus_plus/entities/has_identifier.rb +19 -0
- data/lib/csv_plus_plus/entities/number.rb +15 -10
- data/lib/csv_plus_plus/entities/reference.rb +77 -0
- data/lib/csv_plus_plus/entities/runtime_value.rb +36 -23
- data/lib/csv_plus_plus/entities/string.rb +13 -10
- data/lib/csv_plus_plus/entities.rb +2 -18
- data/lib/csv_plus_plus/error/cli_error.rb +17 -0
- data/lib/csv_plus_plus/error/compiler_error.rb +17 -0
- data/lib/csv_plus_plus/error/error.rb +18 -5
- data/lib/csv_plus_plus/error/formula_syntax_error.rb +12 -13
- data/lib/csv_plus_plus/error/modifier_syntax_error.rb +10 -36
- data/lib/csv_plus_plus/error/modifier_validation_error.rb +6 -32
- data/lib/csv_plus_plus/error/positional_error.rb +15 -0
- data/lib/csv_plus_plus/error/writer_error.rb +1 -1
- data/lib/csv_plus_plus/error.rb +4 -1
- data/lib/csv_plus_plus/error_formatter.rb +111 -0
- data/lib/csv_plus_plus/google_api_client.rb +18 -8
- data/lib/csv_plus_plus/lexer/racc_lexer.rb +144 -0
- data/lib/csv_plus_plus/lexer/tokenizer.rb +53 -17
- data/lib/csv_plus_plus/lexer.rb +40 -1
- data/lib/csv_plus_plus/modifier/data_validation.rb +1 -1
- data/lib/csv_plus_plus/modifier/expand.rb +17 -0
- data/lib/csv_plus_plus/modifier.rb +6 -1
- data/lib/csv_plus_plus/options/file_options.rb +49 -0
- data/lib/csv_plus_plus/options/google_sheets_options.rb +42 -0
- data/lib/csv_plus_plus/options/options.rb +102 -0
- data/lib/csv_plus_plus/options.rb +22 -110
- data/lib/csv_plus_plus/parser/cell_value.tab.rb +65 -66
- data/lib/csv_plus_plus/parser/code_section.tab.rb +92 -84
- data/lib/csv_plus_plus/parser/modifier.tab.rb +40 -30
- data/lib/csv_plus_plus/reader/csv.rb +50 -0
- data/lib/csv_plus_plus/reader/google_sheets.rb +129 -0
- data/lib/csv_plus_plus/reader/reader.rb +27 -0
- data/lib/csv_plus_plus/reader/rubyxl.rb +37 -0
- data/lib/csv_plus_plus/reader.rb +14 -0
- data/lib/csv_plus_plus/runtime/graph.rb +6 -6
- data/lib/csv_plus_plus/runtime/{position_tracker.rb → position.rb} +16 -5
- data/lib/csv_plus_plus/runtime/references.rb +32 -27
- data/lib/csv_plus_plus/runtime/runtime.rb +73 -67
- data/lib/csv_plus_plus/runtime/scope.rb +280 -0
- data/lib/csv_plus_plus/runtime.rb +9 -9
- data/lib/csv_plus_plus/source_code.rb +14 -9
- data/lib/csv_plus_plus/template.rb +17 -12
- data/lib/csv_plus_plus/version.rb +1 -1
- data/lib/csv_plus_plus/writer/csv.rb +32 -5
- data/lib/csv_plus_plus/writer/excel.rb +19 -6
- data/lib/csv_plus_plus/writer/file_backer_upper.rb +27 -14
- data/lib/csv_plus_plus/writer/google_sheets.rb +23 -129
- data/lib/csv_plus_plus/writer/{google_sheet_builder.rb → google_sheets_builder.rb} +39 -55
- data/lib/csv_plus_plus/writer/merger.rb +56 -0
- data/lib/csv_plus_plus/writer/open_document.rb +16 -2
- data/lib/csv_plus_plus/writer/rubyxl_builder.rb +68 -43
- data/lib/csv_plus_plus/writer/writer.rb +42 -0
- data/lib/csv_plus_plus/writer.rb +58 -19
- data/lib/csv_plus_plus.rb +26 -14
- metadata +43 -18
- data/lib/csv_plus_plus/entities/cell_reference.rb +0 -231
- data/lib/csv_plus_plus/entities/variable.rb +0 -37
- data/lib/csv_plus_plus/error/syntax_error.rb +0 -71
- data/lib/csv_plus_plus/google_options.rb +0 -32
- data/lib/csv_plus_plus/lexer/lexer.rb +0 -89
- data/lib/csv_plus_plus/runtime/can_define_references.rb +0 -87
- data/lib/csv_plus_plus/runtime/can_resolve_references.rb +0 -209
- data/lib/csv_plus_plus/writer/base_writer.rb +0 -45
|
@@ -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
|
-
|
|
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
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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: ::
|
|
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
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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(
|
|
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(
|
|
27
|
-
# @param
|
|
34
|
+
sig { override.params(position: ::CSVPlusPlus::Runtime::Position).returns(::String) }
|
|
35
|
+
# @param position [Position]
|
|
28
36
|
#
|
|
29
|
-
# @return [
|
|
30
|
-
def evaluate(
|
|
31
|
-
"def #{@id.to_s.upcase}(#{arguments.map(&:to_s).join(', ')}) #{@body.evaluate(
|
|
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: ::
|
|
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
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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
|
|
@@ -8,26 +8,40 @@ module CSVPlusPlus
|
|
|
8
8
|
# @attr_reader infix [boolean] Whether or not this function call is infix (X * Y, A + B, etc)
|
|
9
9
|
class FunctionCall < EntityWithArguments
|
|
10
10
|
extend ::T::Sig
|
|
11
|
+
include ::CSVPlusPlus::Entities::HasIdentifier
|
|
12
|
+
|
|
13
|
+
ArgumentsType = type_member { { fixed: ::CSVPlusPlus::Entities::Entity } }
|
|
14
|
+
public_constant :ArgumentsType
|
|
11
15
|
|
|
12
16
|
sig { returns(::T::Boolean) }
|
|
13
17
|
attr_reader :infix
|
|
14
18
|
|
|
15
|
-
sig {
|
|
19
|
+
sig { returns(::Symbol) }
|
|
20
|
+
attr_reader :id
|
|
21
|
+
|
|
22
|
+
sig do
|
|
23
|
+
params(
|
|
24
|
+
id: ::Symbol,
|
|
25
|
+
arguments: ::T::Array[::CSVPlusPlus::Entities::FunctionCall::ArgumentsType],
|
|
26
|
+
infix: ::T::Boolean
|
|
27
|
+
).void
|
|
28
|
+
end
|
|
16
29
|
# @param id [::String] The name of the function
|
|
17
30
|
# @param arguments [Array<Entity>] The arguments to the function
|
|
18
31
|
# @param infix [T::Boolean] Whether the function is infix
|
|
19
32
|
def initialize(id, arguments, infix: false)
|
|
20
|
-
super(
|
|
33
|
+
super(arguments:)
|
|
21
34
|
|
|
35
|
+
@id = ::T.let(identifier(id), ::Symbol)
|
|
22
36
|
@infix = infix
|
|
23
37
|
end
|
|
24
38
|
|
|
25
|
-
sig { override.params(
|
|
26
|
-
# @param
|
|
39
|
+
sig { override.params(position: ::CSVPlusPlus::Runtime::Position).returns(::String) }
|
|
40
|
+
# @param position [Position]
|
|
27
41
|
#
|
|
28
42
|
# @return [::String]
|
|
29
|
-
def evaluate(
|
|
30
|
-
evaluated_arguments = evaluate_arguments(
|
|
43
|
+
def evaluate(position)
|
|
44
|
+
evaluated_arguments = evaluate_arguments(position)
|
|
31
45
|
|
|
32
46
|
if @infix
|
|
33
47
|
"(#{evaluated_arguments.join(" #{@id} ")})"
|
|
@@ -36,14 +50,24 @@ module CSVPlusPlus
|
|
|
36
50
|
end
|
|
37
51
|
end
|
|
38
52
|
|
|
39
|
-
sig { override.params(other: ::
|
|
40
|
-
# @param other [
|
|
53
|
+
sig { override.params(other: ::BasicObject).returns(::T::Boolean) }
|
|
54
|
+
# @param other [BasicObject]
|
|
41
55
|
#
|
|
42
|
-
# @return [
|
|
56
|
+
# @return [Boolean]
|
|
43
57
|
def ==(other)
|
|
44
|
-
|
|
58
|
+
case other
|
|
59
|
+
when self.class
|
|
60
|
+
@id == other.id && @infix == other.infix
|
|
61
|
+
else
|
|
62
|
+
false
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
private
|
|
45
67
|
|
|
46
|
-
|
|
68
|
+
sig { params(position: ::CSVPlusPlus::Runtime::Position).returns(::T::Array[::String]) }
|
|
69
|
+
def evaluate_arguments(position)
|
|
70
|
+
@arguments.map { |arg| arg.evaluate(position) }
|
|
47
71
|
end
|
|
48
72
|
end
|
|
49
73
|
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module CSVPlusPlus
|
|
5
|
+
module Entities
|
|
6
|
+
# Can be included on any class that has a comparable id
|
|
7
|
+
module HasIdentifier
|
|
8
|
+
extend ::T::Sig
|
|
9
|
+
|
|
10
|
+
sig { params(symbol: ::Symbol).returns(::Symbol) }
|
|
11
|
+
# Variables and functions are case insensitive. I hate it but it's how excel is
|
|
12
|
+
#
|
|
13
|
+
# @param symbol [Symbol]
|
|
14
|
+
def identifier(symbol)
|
|
15
|
+
symbol.downcase
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -6,14 +6,16 @@ module CSVPlusPlus
|
|
|
6
6
|
# A number value
|
|
7
7
|
#
|
|
8
8
|
# @attr_reader value [Numeric] The parsed number value
|
|
9
|
-
class Number < Entity
|
|
9
|
+
class Number < ::CSVPlusPlus::Entities::Entity
|
|
10
|
+
extend ::T::Sig
|
|
11
|
+
|
|
10
12
|
sig { returns(::Numeric) }
|
|
11
13
|
attr_reader :value
|
|
12
14
|
|
|
13
15
|
sig { params(value: ::T.any(::String, ::Numeric)).void }
|
|
14
16
|
# @param value [String, Numeric] Either a +String+ that looks like a number, or an already parsed Numeric
|
|
15
17
|
def initialize(value)
|
|
16
|
-
super(
|
|
18
|
+
super()
|
|
17
19
|
|
|
18
20
|
@value =
|
|
19
21
|
::T.let(
|
|
@@ -26,22 +28,25 @@ module CSVPlusPlus
|
|
|
26
28
|
)
|
|
27
29
|
end
|
|
28
30
|
|
|
29
|
-
sig { override.params(
|
|
30
|
-
# @param
|
|
31
|
+
sig { override.params(_position: ::CSVPlusPlus::Runtime::Position).returns(::String) }
|
|
32
|
+
# @param _position [Position]
|
|
31
33
|
#
|
|
32
34
|
# @return [::String]
|
|
33
|
-
def evaluate(
|
|
35
|
+
def evaluate(_position)
|
|
34
36
|
@value.to_s
|
|
35
37
|
end
|
|
36
38
|
|
|
37
|
-
sig { override.params(other: ::
|
|
38
|
-
# @param other [
|
|
39
|
+
sig { override.params(other: ::BasicObject).returns(::T::Boolean) }
|
|
40
|
+
# @param other [BasicObject]
|
|
39
41
|
#
|
|
40
42
|
# @return [::T::Boolean]
|
|
41
43
|
def ==(other)
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
44
|
+
case other
|
|
45
|
+
when self.class
|
|
46
|
+
@value == other.value
|
|
47
|
+
else
|
|
48
|
+
false
|
|
49
|
+
end
|
|
45
50
|
end
|
|
46
51
|
end
|
|
47
52
|
end
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module CSVPlusPlus
|
|
5
|
+
module Entities
|
|
6
|
+
# A reference to something - this is typically either a cell reference (handled by +A1Reference+) or a reference
|
|
7
|
+
# to a variable.
|
|
8
|
+
#
|
|
9
|
+
# One sticking point with the design of this class is that we don't know if a reference is a variable reference
|
|
10
|
+
# unless we look at currently defined variables and see if there is one by that name. Since all cell references
|
|
11
|
+
# can be a valid variable name, we can't be sure which is which. So we delegate that decision as late as possible
|
|
12
|
+
# - in #evaluate
|
|
13
|
+
#
|
|
14
|
+
# @attr_reader scoped_to_expand [Expand, nil] If set, the expand in which this variable is scoped to. It cannot be
|
|
15
|
+
# resolved outside of the given expand.
|
|
16
|
+
class Reference < ::CSVPlusPlus::Entities::Entity
|
|
17
|
+
extend ::T::Sig
|
|
18
|
+
include ::CSVPlusPlus::Entities::HasIdentifier
|
|
19
|
+
|
|
20
|
+
sig { returns(::T.nilable(::String)) }
|
|
21
|
+
attr_reader :ref
|
|
22
|
+
|
|
23
|
+
# sig { returns(::T.nilable(::CSVPlusPlus::A1Reference)) }
|
|
24
|
+
# attr_reader :a1_ref
|
|
25
|
+
|
|
26
|
+
sig { params(ref: ::T.nilable(::String), a1_ref: ::T.nilable(::CSVPlusPlus::A1Reference)).void }
|
|
27
|
+
# Either +ref+, +cell_index+ or +row_index+ must be specified.
|
|
28
|
+
#
|
|
29
|
+
# @param ref [Integer, nil] An A1-style cell reference (that will be parsed into it's row/cell indexes).
|
|
30
|
+
# @param a1_ref [Integer, nil] An A1-style cell reference (that will be parsed into it's row/cell indexes).
|
|
31
|
+
def initialize(ref: nil, a1_ref: nil)
|
|
32
|
+
super()
|
|
33
|
+
|
|
34
|
+
raise(::ArgumentError, 'Must specify :ref or :a1_ref') unless ref || a1_ref
|
|
35
|
+
|
|
36
|
+
@ref = ref
|
|
37
|
+
@a1_ref = a1_ref
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
sig { override.params(other: ::BasicObject).returns(::T::Boolean) }
|
|
41
|
+
# @param other [BasicObject]
|
|
42
|
+
#
|
|
43
|
+
# @return [boolean]
|
|
44
|
+
def ==(other)
|
|
45
|
+
case other
|
|
46
|
+
when self.class
|
|
47
|
+
a1_ref == other.a1_ref
|
|
48
|
+
else
|
|
49
|
+
false
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
sig { returns(::CSVPlusPlus::A1Reference) }
|
|
54
|
+
# @return [A1Reference]
|
|
55
|
+
def a1_ref
|
|
56
|
+
@a1_ref ||= ::CSVPlusPlus::A1Reference.new(ref: ::T.must(ref))
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
sig { override.params(position: ::CSVPlusPlus::Runtime::Position).returns(::String) }
|
|
60
|
+
# Get the A1-style cell reference
|
|
61
|
+
#
|
|
62
|
+
# @param position [Position] The current position
|
|
63
|
+
#
|
|
64
|
+
# @return [::String] An A1-style reference
|
|
65
|
+
def evaluate(position)
|
|
66
|
+
# TODO: ugh make to_a1_ref not return a nil
|
|
67
|
+
ref || a1_ref.to_a1_ref(position) || ''
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
sig { returns(::T.nilable(::Symbol)) }
|
|
71
|
+
# @return [Symbol]
|
|
72
|
+
def id
|
|
73
|
+
ref && identifier(::T.must(ref).to_sym)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
@@ -1,41 +1,54 @@
|
|
|
1
1
|
# typed: strict
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
|
+
require_relative './entity'
|
|
5
|
+
|
|
4
6
|
module CSVPlusPlus
|
|
5
7
|
module Entities
|
|
6
|
-
# A runtime value. These are values which can be materialized at any point via the +resolve_fn+
|
|
7
|
-
#
|
|
8
|
-
|
|
9
|
-
# @attr_reader resolve_fn [lambda] A lambda that is called when the runtime value is resolved
|
|
10
|
-
class RuntimeValue < Entity
|
|
8
|
+
# A runtime value. These are values which can be materialized at any point via the +resolve_fn+ which
|
|
9
|
+
# will provide a value depending on the +Runtime+'s current state.
|
|
10
|
+
class RuntimeValue < ::CSVPlusPlus::Entities::Entity
|
|
11
11
|
extend ::T::Sig
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
13
|
+
ResolveFn =
|
|
14
|
+
::T.type_alias do
|
|
15
|
+
::T.proc.params(
|
|
16
|
+
position: ::CSVPlusPlus::Runtime::Position,
|
|
17
|
+
arguments: ::T::Enumerable[::CSVPlusPlus::Entities::Entity]
|
|
18
|
+
).returns(::CSVPlusPlus::Entities::Entity)
|
|
19
|
+
end
|
|
20
|
+
public_constant :ResolveFn
|
|
18
21
|
|
|
19
22
|
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
|
|
23
|
+
params(resolve_fn: ::CSVPlusPlus::Entities::RuntimeValue::ResolveFn).void
|
|
24
24
|
end
|
|
25
25
|
# @param resolve_fn [lambda] A lambda that is called when the runtime value is resolved
|
|
26
|
-
|
|
27
|
-
def initialize(resolve_fn, arguments: [])
|
|
28
|
-
super(::CSVPlusPlus::Entities::Type::RuntimeValue)
|
|
29
|
-
|
|
30
|
-
@arguments = arguments
|
|
26
|
+
def initialize(resolve_fn)
|
|
31
27
|
@resolve_fn = resolve_fn
|
|
28
|
+
super()
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
sig do
|
|
32
|
+
params(
|
|
33
|
+
position: ::CSVPlusPlus::Runtime::Position,
|
|
34
|
+
arguments: ::T::Enumerable[::CSVPlusPlus::Entities::Entity]
|
|
35
|
+
).returns(::CSVPlusPlus::Entities::Entity)
|
|
36
|
+
end
|
|
37
|
+
# Using the +@resolve_fn+, evaluate this runtime value into an +Entity+ that can be later evaluated
|
|
38
|
+
def call(position, arguments)
|
|
39
|
+
@resolve_fn.call(position, arguments)
|
|
32
40
|
end
|
|
33
41
|
|
|
34
|
-
sig { override.params(
|
|
35
|
-
#
|
|
42
|
+
sig { override.params(_position: ::CSVPlusPlus::Runtime::Position).returns(::String) }
|
|
43
|
+
# Given the current runtime, call +@resolve_fn+ to produce a value
|
|
44
|
+
#
|
|
45
|
+
# @param _position [Position]
|
|
36
46
|
#
|
|
37
|
-
# @return [
|
|
38
|
-
def evaluate(
|
|
47
|
+
# @return [Entities::Entity]
|
|
48
|
+
def evaluate(_position)
|
|
49
|
+
# TODO: we can do a check on arguments here and make sure that the RuntimeValue is being called
|
|
50
|
+
# with the number of arguments it requires
|
|
51
|
+
# @resolve_fn.call(position, ::T.must(arguments)).evaluate(position)
|
|
39
52
|
'(runtime value)'
|
|
40
53
|
end
|
|
41
54
|
end
|
|
@@ -6,7 +6,7 @@ module CSVPlusPlus
|
|
|
6
6
|
# A string value
|
|
7
7
|
#
|
|
8
8
|
# @attr_reader value [String]
|
|
9
|
-
class String < Entity
|
|
9
|
+
class String < ::CSVPlusPlus::Entities::Entity
|
|
10
10
|
extend ::T::Sig
|
|
11
11
|
|
|
12
12
|
sig { returns(::String) }
|
|
@@ -15,27 +15,30 @@ module CSVPlusPlus
|
|
|
15
15
|
sig { params(value: ::String).void }
|
|
16
16
|
# @param value [String] The string that has been parsed out of the template
|
|
17
17
|
def initialize(value)
|
|
18
|
-
super(
|
|
18
|
+
super()
|
|
19
19
|
|
|
20
20
|
@value = ::T.let(value.gsub(/^"|"$/, ''), ::String)
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
-
sig { override.params(
|
|
24
|
-
# @param
|
|
23
|
+
sig { override.params(_position: ::CSVPlusPlus::Runtime::Position).returns(::String) }
|
|
24
|
+
# @param _position [Position]
|
|
25
25
|
#
|
|
26
26
|
# @return [::String]
|
|
27
|
-
def evaluate(
|
|
27
|
+
def evaluate(_position)
|
|
28
28
|
"\"#{@value}\""
|
|
29
29
|
end
|
|
30
30
|
|
|
31
|
-
sig { override.params(other: ::
|
|
32
|
-
# @param other [
|
|
31
|
+
sig { override.params(other: ::BasicObject).returns(::T::Boolean) }
|
|
32
|
+
# @param other [BasicObject]
|
|
33
33
|
#
|
|
34
34
|
# @return [T::Boolean]
|
|
35
35
|
def ==(other)
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
case other
|
|
37
|
+
when self.class
|
|
38
|
+
@value == other.value
|
|
39
|
+
else
|
|
40
|
+
false
|
|
41
|
+
end
|
|
39
42
|
end
|
|
40
43
|
end
|
|
41
44
|
end
|
|
@@ -3,36 +3,20 @@
|
|
|
3
3
|
|
|
4
4
|
require_relative 'entities/entity'
|
|
5
5
|
require_relative 'entities/entity_with_arguments'
|
|
6
|
+
require_relative 'entities/has_identifier'
|
|
6
7
|
|
|
7
8
|
require_relative 'entities/boolean'
|
|
8
|
-
require_relative 'entities/cell_reference'
|
|
9
9
|
require_relative 'entities/date'
|
|
10
10
|
require_relative 'entities/function'
|
|
11
11
|
require_relative 'entities/function_call'
|
|
12
12
|
require_relative 'entities/number'
|
|
13
|
+
require_relative 'entities/reference'
|
|
13
14
|
require_relative 'entities/runtime_value'
|
|
14
15
|
require_relative 'entities/string'
|
|
15
|
-
require_relative 'entities/variable'
|
|
16
16
|
|
|
17
17
|
module CSVPlusPlus
|
|
18
18
|
# The entities that form abstract syntax trees which make up the language
|
|
19
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
20
|
end
|
|
37
21
|
end
|
|
38
22
|
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module CSVPlusPlus
|
|
5
|
+
module Error
|
|
6
|
+
# An error that represents an invalid CLI option or state
|
|
7
|
+
class CLIError < ::CSVPlusPlus::Error::Error
|
|
8
|
+
extend ::T::Sig
|
|
9
|
+
|
|
10
|
+
sig { override.returns(::String) }
|
|
11
|
+
# @return [String]
|
|
12
|
+
def error_message
|
|
13
|
+
message
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module CSVPlusPlus
|
|
5
|
+
module Error
|
|
6
|
+
# An error that represents an invalid state during compilation.
|
|
7
|
+
class CompilerError < ::CSVPlusPlus::Error::Error
|
|
8
|
+
extend ::T::Sig
|
|
9
|
+
|
|
10
|
+
sig { override.returns(::String) }
|
|
11
|
+
# @return [String]
|
|
12
|
+
def error_message
|
|
13
|
+
message
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -4,17 +4,30 @@
|
|
|
4
4
|
module CSVPlusPlus
|
|
5
5
|
module Error
|
|
6
6
|
# An error thrown by our code (generally to be handled at the top level bin/ command)
|
|
7
|
-
class Error < StandardError
|
|
7
|
+
class Error < ::StandardError
|
|
8
8
|
extend ::T::Sig
|
|
9
9
|
extend ::T::Helpers
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
abstract!
|
|
12
|
+
|
|
13
|
+
sig { returns(::T.nilable(::StandardError)) }
|
|
14
|
+
attr_reader :wrapped_error
|
|
15
|
+
|
|
16
|
+
sig { params(message: ::String, wrapped_error: ::T.nilable(::StandardError)).void }
|
|
17
|
+
# @param wrapped_error [StandardError] The underlying error that caused the syntax error. For example a
|
|
18
|
+
# Racc::ParseError that was thrown
|
|
19
|
+
def initialize(message, wrapped_error: nil)
|
|
20
|
+
super(message)
|
|
21
|
+
|
|
22
|
+
@message = message
|
|
23
|
+
@wrapped_error = wrapped_error
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
sig { abstract.returns(::String) }
|
|
12
27
|
# Return an error message for display to a command-line user.
|
|
13
28
|
#
|
|
14
29
|
# @return [::String]
|
|
15
|
-
def error_message
|
|
16
|
-
message
|
|
17
|
-
end
|
|
30
|
+
def error_message; end
|
|
18
31
|
end
|
|
19
32
|
end
|
|
20
33
|
end
|
|
@@ -1,36 +1,35 @@
|
|
|
1
|
-
# typed:
|
|
1
|
+
# typed: strict
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
|
-
require_relative './syntax_error'
|
|
5
|
-
|
|
6
4
|
module CSVPlusPlus
|
|
7
5
|
module Error
|
|
8
6
|
# An error that can be thrown when there is an error parsing a modifier
|
|
9
7
|
#
|
|
10
8
|
# @attr_reader message [::String] A helpful error message
|
|
11
9
|
# @attr_reader bad_input [String] The offending input that caused the error to be thrown
|
|
12
|
-
class FormulaSyntaxError < ::CSVPlusPlus::Error::
|
|
13
|
-
|
|
10
|
+
class FormulaSyntaxError < ::CSVPlusPlus::Error::Error
|
|
11
|
+
extend ::T::Sig
|
|
12
|
+
include ::CSVPlusPlus::Error::PositionalError
|
|
14
13
|
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
sig { returns(::String) }
|
|
15
|
+
attr_reader :bad_input
|
|
16
|
+
|
|
17
|
+
sig { params(message: ::String, bad_input: ::String, wrapped_error: ::T.nilable(::StandardError)).void }
|
|
17
18
|
# @param message [String] A relevant message to show
|
|
18
19
|
# @param bad_input [String] The offending input that caused the error to be thrown
|
|
19
|
-
# @param runtime [Runtime] The current runtime
|
|
20
20
|
# @param wrapped_error [StandardError] The underlying error that caused the syntax error. For example a
|
|
21
21
|
# Racc::ParseError that was thrown
|
|
22
|
-
def initialize(message, bad_input
|
|
22
|
+
def initialize(message, bad_input:, wrapped_error: nil)
|
|
23
|
+
super(message, wrapped_error:)
|
|
23
24
|
@bad_input = bad_input
|
|
24
|
-
@message = message
|
|
25
|
-
|
|
26
|
-
super(runtime, wrapped_error:)
|
|
27
25
|
end
|
|
28
26
|
|
|
27
|
+
sig { override.returns(::String) }
|
|
29
28
|
# Create a relevant error message given +@bad_input+ and +@message+.
|
|
30
29
|
#
|
|
31
30
|
# @return [::String]
|
|
32
31
|
def error_message
|
|
33
|
-
"#{
|
|
32
|
+
"#{message}: \"#{bad_input}\""
|
|
34
33
|
end
|
|
35
34
|
end
|
|
36
35
|
end
|