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