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.
Files changed (93) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +18 -63
  3. data/{CHANGELOG.md → docs/CHANGELOG.md} +17 -0
  4. data/lib/csv_plus_plus/benchmarked_compiler.rb +112 -0
  5. data/lib/csv_plus_plus/cell.rb +46 -24
  6. data/lib/csv_plus_plus/cli.rb +44 -17
  7. data/lib/csv_plus_plus/cli_flag.rb +1 -2
  8. data/lib/csv_plus_plus/color.rb +42 -11
  9. data/lib/csv_plus_plus/compiler.rb +178 -0
  10. data/lib/csv_plus_plus/entities/ast_builder.rb +50 -0
  11. data/lib/csv_plus_plus/entities/boolean.rb +40 -0
  12. data/lib/csv_plus_plus/entities/builtins.rb +58 -0
  13. data/lib/csv_plus_plus/entities/cell_reference.rb +231 -0
  14. data/lib/csv_plus_plus/entities/date.rb +63 -0
  15. data/lib/csv_plus_plus/entities/entity.rb +50 -0
  16. data/lib/csv_plus_plus/entities/entity_with_arguments.rb +57 -0
  17. data/lib/csv_plus_plus/entities/function.rb +45 -0
  18. data/lib/csv_plus_plus/entities/function_call.rb +50 -0
  19. data/lib/csv_plus_plus/entities/number.rb +48 -0
  20. data/lib/csv_plus_plus/entities/runtime_value.rb +43 -0
  21. data/lib/csv_plus_plus/entities/string.rb +42 -0
  22. data/lib/csv_plus_plus/entities/variable.rb +37 -0
  23. data/lib/csv_plus_plus/entities.rb +40 -0
  24. data/lib/csv_plus_plus/error/error.rb +20 -0
  25. data/lib/csv_plus_plus/error/formula_syntax_error.rb +37 -0
  26. data/lib/csv_plus_plus/error/modifier_syntax_error.rb +75 -0
  27. data/lib/csv_plus_plus/error/modifier_validation_error.rb +69 -0
  28. data/lib/csv_plus_plus/error/syntax_error.rb +71 -0
  29. data/lib/csv_plus_plus/error/writer_error.rb +17 -0
  30. data/lib/csv_plus_plus/error.rb +10 -2
  31. data/lib/csv_plus_plus/google_api_client.rb +11 -2
  32. data/lib/csv_plus_plus/google_options.rb +23 -18
  33. data/lib/csv_plus_plus/lexer/lexer.rb +17 -6
  34. data/lib/csv_plus_plus/lexer/tokenizer.rb +6 -1
  35. data/lib/csv_plus_plus/lexer.rb +24 -0
  36. data/lib/csv_plus_plus/modifier/conditional_formatting.rb +18 -0
  37. data/lib/csv_plus_plus/modifier/data_validation.rb +138 -0
  38. data/lib/csv_plus_plus/modifier/expand.rb +61 -0
  39. data/lib/csv_plus_plus/modifier/google_sheet_modifier.rb +133 -0
  40. data/lib/csv_plus_plus/modifier/modifier.rb +222 -0
  41. data/lib/csv_plus_plus/modifier/modifier_validator.rb +243 -0
  42. data/lib/csv_plus_plus/modifier/rubyxl_modifier.rb +84 -0
  43. data/lib/csv_plus_plus/modifier.rb +82 -150
  44. data/lib/csv_plus_plus/options.rb +64 -19
  45. data/lib/csv_plus_plus/{language → parser}/cell_value.tab.rb +25 -25
  46. data/lib/csv_plus_plus/{language → parser}/code_section.tab.rb +86 -95
  47. data/lib/csv_plus_plus/parser/modifier.tab.rb +478 -0
  48. data/lib/csv_plus_plus/row.rb +53 -15
  49. data/lib/csv_plus_plus/runtime/can_define_references.rb +87 -0
  50. data/lib/csv_plus_plus/runtime/can_resolve_references.rb +209 -0
  51. data/lib/csv_plus_plus/runtime/graph.rb +68 -0
  52. data/lib/csv_plus_plus/runtime/position_tracker.rb +231 -0
  53. data/lib/csv_plus_plus/runtime/references.rb +110 -0
  54. data/lib/csv_plus_plus/runtime/runtime.rb +126 -0
  55. data/lib/csv_plus_plus/runtime.rb +42 -0
  56. data/lib/csv_plus_plus/source_code.rb +66 -0
  57. data/lib/csv_plus_plus/template.rb +63 -36
  58. data/lib/csv_plus_plus/version.rb +2 -1
  59. data/lib/csv_plus_plus/writer/base_writer.rb +30 -5
  60. data/lib/csv_plus_plus/writer/csv.rb +11 -9
  61. data/lib/csv_plus_plus/writer/excel.rb +9 -2
  62. data/lib/csv_plus_plus/writer/file_backer_upper.rb +7 -4
  63. data/lib/csv_plus_plus/writer/google_sheet_builder.rb +88 -45
  64. data/lib/csv_plus_plus/writer/google_sheets.rb +79 -29
  65. data/lib/csv_plus_plus/writer/open_document.rb +6 -1
  66. data/lib/csv_plus_plus/writer/rubyxl_builder.rb +103 -33
  67. data/lib/csv_plus_plus/writer.rb +39 -9
  68. data/lib/csv_plus_plus.rb +41 -15
  69. metadata +44 -30
  70. data/lib/csv_plus_plus/code_section.rb +0 -101
  71. data/lib/csv_plus_plus/expand.rb +0 -18
  72. data/lib/csv_plus_plus/graph.rb +0 -62
  73. data/lib/csv_plus_plus/language/ast_builder.rb +0 -68
  74. data/lib/csv_plus_plus/language/benchmarked_compiler.rb +0 -65
  75. data/lib/csv_plus_plus/language/builtins.rb +0 -46
  76. data/lib/csv_plus_plus/language/compiler.rb +0 -152
  77. data/lib/csv_plus_plus/language/entities/boolean.rb +0 -33
  78. data/lib/csv_plus_plus/language/entities/cell_reference.rb +0 -33
  79. data/lib/csv_plus_plus/language/entities/entity.rb +0 -86
  80. data/lib/csv_plus_plus/language/entities/function.rb +0 -35
  81. data/lib/csv_plus_plus/language/entities/function_call.rb +0 -37
  82. data/lib/csv_plus_plus/language/entities/number.rb +0 -36
  83. data/lib/csv_plus_plus/language/entities/runtime_value.rb +0 -28
  84. data/lib/csv_plus_plus/language/entities/string.rb +0 -31
  85. data/lib/csv_plus_plus/language/entities/variable.rb +0 -25
  86. data/lib/csv_plus_plus/language/entities.rb +0 -28
  87. data/lib/csv_plus_plus/language/references.rb +0 -70
  88. data/lib/csv_plus_plus/language/runtime.rb +0 -205
  89. data/lib/csv_plus_plus/language/scope.rb +0 -192
  90. data/lib/csv_plus_plus/language/syntax_error.rb +0 -66
  91. data/lib/csv_plus_plus/modifier.tab.rb +0 -907
  92. data/lib/csv_plus_plus/writer/google_sheet_modifier.rb +0 -56
  93. data/lib/csv_plus_plus/writer/rubyxl_modifier.rb +0 -59
@@ -1,3 +1,4 @@
1
+ # typed: strict
1
2
  # frozen_string_literal: true
2
3
 
3
4
  require_relative './writer/base_writer'
@@ -7,18 +8,47 @@ require_relative './writer/google_sheets'
7
8
  require_relative './writer/open_document'
8
9
 
9
10
  module CSVPlusPlus
10
- # Various strategies for writing to various formats (excel, google sheets, CSV, OpenDocument)
11
+ # Various strategies for writing to various formats (excel, google sheets, CSV & OpenDocument (not yet implemented))
11
12
  module Writer
12
- # Return an instance of a writer depending on the given +options+
13
- def self.writer(options)
14
- return ::CSVPlusPlus::Writer::GoogleSheets.new(options) if options.google
13
+ extend ::T::Sig
15
14
 
16
- case options.output_filename
17
- when /\.csv$/ then ::CSVPlusPlus::Writer::CSV.new(options)
18
- when /\.ods$/ then ::CSVPlusPlus::Writer::OpenDocument.new(options)
19
- when /\.xl(sx|sm|tx|tm)$/ then ::CSVPlusPlus::Writer::Excel.new(options)
20
- else raise(::CSVPlusPlus::Error, "Unsupported file extension: #{options.output_filename}")
15
+ sig do
16
+ params(
17
+ options: ::CSVPlusPlus::Options,
18
+ runtime: ::CSVPlusPlus::Runtime::Runtime
19
+ ).returns(
20
+ ::T.any(
21
+ ::CSVPlusPlus::Writer::CSV,
22
+ ::CSVPlusPlus::Writer::Excel,
23
+ ::CSVPlusPlus::Writer::GoogleSheets,
24
+ ::CSVPlusPlus::Writer::OpenDocument
25
+ )
26
+ )
27
+ end
28
+ # Return an instance of a writer depending on the given +options+
29
+ #
30
+ # @param options [Options] The supplied options.
31
+ # @param runtime [Runtime] The current runtime.
32
+ #
33
+ # @return [Writer::CSV | Writer::Excel | Writer::GoogleSheets | Writer::OpenDocument]
34
+ # rubocop:disable Metrics/MethodLength
35
+ def self.writer(options, runtime)
36
+ output_format = options.output_format
37
+ case output_format
38
+ when ::CSVPlusPlus::Options::OutputFormat::CSV then ::CSVPlusPlus::Writer::CSV.new(options, runtime)
39
+ when ::CSVPlusPlus::Options::OutputFormat::Excel then ::CSVPlusPlus::Writer::Excel.new(options, runtime)
40
+ when ::CSVPlusPlus::Options::OutputFormat::GoogleSheets then ::CSVPlusPlus::Writer::GoogleSheets.new(
41
+ options,
42
+ runtime
43
+ )
44
+ when ::CSVPlusPlus::Options::OutputFormat::OpenDocument then ::CSVPlusPlus::Writer::OpenDocument.new(
45
+ options,
46
+ runtime
47
+ )
48
+ else
49
+ ::T.absurd(output_format)
21
50
  end
22
51
  end
52
+ # rubocop:enable Metrics/MethodLength
23
53
  end
24
54
  end
data/lib/csv_plus_plus.rb CHANGED
@@ -1,30 +1,54 @@
1
+ # typed: strict
1
2
  # frozen_string_literal: true
2
3
 
4
+ require 'sorbet-runtime'
5
+
6
+ require 'benchmark'
7
+ require 'csv'
8
+ require 'fileutils'
3
9
  require 'google/apis/drive_v3'
4
10
  require 'google/apis/sheets_v4'
5
11
  require 'googleauth'
12
+ require 'optparse'
13
+ require 'pathname'
6
14
  require 'rubyXL'
7
15
  require 'rubyXL/convenience_methods'
8
16
  require 'set'
17
+ require 'tempfile'
18
+
19
+ require_relative 'csv_plus_plus/source_code'
20
+
21
+ require_relative 'csv_plus_plus/runtime'
22
+
23
+ require_relative 'csv_plus_plus/cli_flag'
24
+ require_relative 'csv_plus_plus/entities'
25
+ require_relative 'csv_plus_plus/error'
9
26
 
10
27
  require_relative 'csv_plus_plus/cell'
11
28
  require_relative 'csv_plus_plus/cli'
12
- require_relative 'csv_plus_plus/code_section'
13
29
  require_relative 'csv_plus_plus/color'
14
- require_relative 'csv_plus_plus/error'
15
- require_relative 'csv_plus_plus/language/builtins'
16
- require_relative 'csv_plus_plus/language/compiler'
17
- require_relative 'csv_plus_plus/language/runtime'
18
- require_relative 'csv_plus_plus/language/syntax_error'
19
30
  require_relative 'csv_plus_plus/modifier'
20
- require_relative 'csv_plus_plus/modifier.tab'
31
+
32
+ require_relative 'csv_plus_plus/parser/cell_value.tab'
33
+ require_relative 'csv_plus_plus/parser/code_section.tab'
34
+ require_relative 'csv_plus_plus/parser/modifier.tab'
35
+
36
+ require_relative 'csv_plus_plus/compiler'
37
+
38
+ require_relative 'csv_plus_plus/google_options'
39
+ require_relative 'csv_plus_plus/lexer'
21
40
  require_relative 'csv_plus_plus/options'
22
41
  require_relative 'csv_plus_plus/row'
23
42
  require_relative 'csv_plus_plus/template'
24
43
  require_relative 'csv_plus_plus/writer'
25
44
 
45
+ require_relative 'csv_plus_plus/benchmarked_compiler'
46
+
26
47
  # A programming language for writing rich CSV files
27
48
  module CSVPlusPlus
49
+ extend ::T::Sig
50
+
51
+ sig { params(input: ::String, filename: ::T.nilable(::String), options: ::CSVPlusPlus::Options).void }
28
52
  # Parse the input into a +Template+ and write it to the desired format
29
53
  #
30
54
  # @param input [String] The csvpp input to compile
@@ -33,25 +57,27 @@ module CSVPlusPlus
33
57
  def self.apply_template_to_sheet!(input, filename, options)
34
58
  warn(options.verbose_summary) if options.verbose
35
59
 
36
- runtime = ::CSVPlusPlus::Language::Runtime.new(input:, filename:)
60
+ runtime = ::CSVPlusPlus::Runtime.new(source_code: ::CSVPlusPlus::SourceCode.new(input:, filename:))
37
61
 
38
- ::CSVPlusPlus::Language::Compiler.with_compiler(options:, runtime:) do |compiler|
62
+ ::CSVPlusPlus::Compiler.with_compiler(options:, runtime:) do |compiler|
39
63
  template = compiler.compile_template
40
-
41
64
  warn(template.verbose_summary) if options.verbose
42
65
 
43
- write_template(template, compiler, options)
66
+ write_template(template:, compiler:, options:)
44
67
  end
45
68
  end
46
69
 
70
+ sig do
71
+ params(compiler: ::CSVPlusPlus::Compiler, options: ::CSVPlusPlus::Options, template: ::CSVPlusPlus::Template).void
72
+ end
47
73
  # Write the results (and possibly make a backup) of a compiled +template+
48
74
  #
49
- # @param template [Template] The compiled template
50
75
  # @param compiler [Compiler] The compiler currently in use
51
76
  # @param options [Options] The options we're running with
52
- def self.write_template(template, compiler, options)
53
- output = ::CSVPlusPlus::Writer.writer(options)
54
- compiler.outputting! do
77
+ # @param template [Template] The compiled template
78
+ def self.write_template(compiler:, options:, template:)
79
+ compiler.outputting! do |runtime|
80
+ output = ::CSVPlusPlus::Writer.writer(options, runtime)
55
81
  output.write_backup if options.backup
56
82
  output.write(template)
57
83
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: csv_plus_plus
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Patrick Carroll
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-03-11 00:00:00.000000000 Z
11
+ date: 2023-04-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: google-apis-drive_v3
@@ -118,48 +118,64 @@ executables:
118
118
  extensions: []
119
119
  extra_rdoc_files: []
120
120
  files:
121
- - CHANGELOG.md
122
121
  - README.md
123
122
  - bin/csv++
124
123
  - bin/csvpp
124
+ - docs/CHANGELOG.md
125
125
  - lib/csv_plus_plus.rb
126
+ - lib/csv_plus_plus/benchmarked_compiler.rb
126
127
  - lib/csv_plus_plus/cell.rb
127
128
  - lib/csv_plus_plus/cli.rb
128
129
  - lib/csv_plus_plus/cli_flag.rb
129
- - lib/csv_plus_plus/code_section.rb
130
130
  - lib/csv_plus_plus/color.rb
131
+ - lib/csv_plus_plus/compiler.rb
132
+ - lib/csv_plus_plus/entities.rb
133
+ - lib/csv_plus_plus/entities/ast_builder.rb
134
+ - lib/csv_plus_plus/entities/boolean.rb
135
+ - lib/csv_plus_plus/entities/builtins.rb
136
+ - lib/csv_plus_plus/entities/cell_reference.rb
137
+ - lib/csv_plus_plus/entities/date.rb
138
+ - lib/csv_plus_plus/entities/entity.rb
139
+ - lib/csv_plus_plus/entities/entity_with_arguments.rb
140
+ - lib/csv_plus_plus/entities/function.rb
141
+ - lib/csv_plus_plus/entities/function_call.rb
142
+ - lib/csv_plus_plus/entities/number.rb
143
+ - lib/csv_plus_plus/entities/runtime_value.rb
144
+ - lib/csv_plus_plus/entities/string.rb
145
+ - lib/csv_plus_plus/entities/variable.rb
131
146
  - lib/csv_plus_plus/error.rb
132
- - lib/csv_plus_plus/expand.rb
147
+ - lib/csv_plus_plus/error/error.rb
148
+ - lib/csv_plus_plus/error/formula_syntax_error.rb
149
+ - lib/csv_plus_plus/error/modifier_syntax_error.rb
150
+ - lib/csv_plus_plus/error/modifier_validation_error.rb
151
+ - lib/csv_plus_plus/error/syntax_error.rb
152
+ - lib/csv_plus_plus/error/writer_error.rb
133
153
  - lib/csv_plus_plus/google_api_client.rb
134
154
  - lib/csv_plus_plus/google_options.rb
135
- - lib/csv_plus_plus/graph.rb
136
- - lib/csv_plus_plus/language/ast_builder.rb
137
- - lib/csv_plus_plus/language/benchmarked_compiler.rb
138
- - lib/csv_plus_plus/language/builtins.rb
139
- - lib/csv_plus_plus/language/cell_value.tab.rb
140
- - lib/csv_plus_plus/language/code_section.tab.rb
141
- - lib/csv_plus_plus/language/compiler.rb
142
- - lib/csv_plus_plus/language/entities.rb
143
- - lib/csv_plus_plus/language/entities/boolean.rb
144
- - lib/csv_plus_plus/language/entities/cell_reference.rb
145
- - lib/csv_plus_plus/language/entities/entity.rb
146
- - lib/csv_plus_plus/language/entities/function.rb
147
- - lib/csv_plus_plus/language/entities/function_call.rb
148
- - lib/csv_plus_plus/language/entities/number.rb
149
- - lib/csv_plus_plus/language/entities/runtime_value.rb
150
- - lib/csv_plus_plus/language/entities/string.rb
151
- - lib/csv_plus_plus/language/entities/variable.rb
152
- - lib/csv_plus_plus/language/references.rb
153
- - lib/csv_plus_plus/language/runtime.rb
154
- - lib/csv_plus_plus/language/scope.rb
155
- - lib/csv_plus_plus/language/syntax_error.rb
156
155
  - lib/csv_plus_plus/lexer.rb
157
156
  - lib/csv_plus_plus/lexer/lexer.rb
158
157
  - lib/csv_plus_plus/lexer/tokenizer.rb
159
158
  - lib/csv_plus_plus/modifier.rb
160
- - lib/csv_plus_plus/modifier.tab.rb
159
+ - lib/csv_plus_plus/modifier/conditional_formatting.rb
160
+ - lib/csv_plus_plus/modifier/data_validation.rb
161
+ - lib/csv_plus_plus/modifier/expand.rb
162
+ - lib/csv_plus_plus/modifier/google_sheet_modifier.rb
163
+ - lib/csv_plus_plus/modifier/modifier.rb
164
+ - lib/csv_plus_plus/modifier/modifier_validator.rb
165
+ - lib/csv_plus_plus/modifier/rubyxl_modifier.rb
161
166
  - lib/csv_plus_plus/options.rb
167
+ - lib/csv_plus_plus/parser/cell_value.tab.rb
168
+ - lib/csv_plus_plus/parser/code_section.tab.rb
169
+ - lib/csv_plus_plus/parser/modifier.tab.rb
162
170
  - lib/csv_plus_plus/row.rb
171
+ - lib/csv_plus_plus/runtime.rb
172
+ - lib/csv_plus_plus/runtime/can_define_references.rb
173
+ - lib/csv_plus_plus/runtime/can_resolve_references.rb
174
+ - lib/csv_plus_plus/runtime/graph.rb
175
+ - lib/csv_plus_plus/runtime/position_tracker.rb
176
+ - lib/csv_plus_plus/runtime/references.rb
177
+ - lib/csv_plus_plus/runtime/runtime.rb
178
+ - lib/csv_plus_plus/source_code.rb
163
179
  - lib/csv_plus_plus/template.rb
164
180
  - lib/csv_plus_plus/version.rb
165
181
  - lib/csv_plus_plus/writer.rb
@@ -168,11 +184,9 @@ files:
168
184
  - lib/csv_plus_plus/writer/excel.rb
169
185
  - lib/csv_plus_plus/writer/file_backer_upper.rb
170
186
  - lib/csv_plus_plus/writer/google_sheet_builder.rb
171
- - lib/csv_plus_plus/writer/google_sheet_modifier.rb
172
187
  - lib/csv_plus_plus/writer/google_sheets.rb
173
188
  - lib/csv_plus_plus/writer/open_document.rb
174
189
  - lib/csv_plus_plus/writer/rubyxl_builder.rb
175
- - lib/csv_plus_plus/writer/rubyxl_modifier.rb
176
190
  homepage: https://github.com/patrickomatic/csv-plus-plus
177
191
  licenses:
178
192
  - MIT
@@ -182,7 +196,7 @@ metadata:
182
196
  github_repo: git://github.com/patrickomatic/csv-plus-plus
183
197
  homepage_uri: https://github.com/patrickomatic/csv-plus-plus
184
198
  source_code_uri: https://github.com/patrickomatic/csv-plus-plus
185
- changelog_uri: https://github.com/patrickomatic/csv-plus-plus/blob/main/CHANGELOG.md
199
+ changelog_uri: https://github.com/patrickomatic/csv-plus-plus/blob/main/docs/CHANGELOG.md
186
200
  rubygems_mfa_required: 'true'
187
201
  post_install_message:
188
202
  rdoc_options: []
@@ -1,101 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative './language/code_section.tab'
4
- require_relative './language/entities'
5
-
6
- module CSVPlusPlus
7
- # A representation of the code section part of a template (the variable and function definitions)
8
- #
9
- # @attr variables [Hash<Symbol, Variable>] All defined variables
10
- # @attr_reader functions [Hash<Symbol, Function>] All defined functions
11
- class CodeSection
12
- attr_reader :functions
13
- attr_accessor :variables
14
-
15
- # @param variables [Hash<Symbol, Variable>] Initial variables
16
- # @param functions [Hash<Symbol, Variable>] Initial functions
17
- def initialize(variables: {}, functions: {})
18
- @variables = variables
19
- @functions = functions
20
- end
21
-
22
- # Define a (or re-define an existing) variable
23
- #
24
- # @param id [String, Symbol] The identifier for the variable
25
- # @param entity [Entity] The value (entity) the variable holds
26
- def def_variable(id, entity)
27
- @variables[id.to_sym] = entity
28
- end
29
-
30
- # Define (or re-define existing) variables
31
- #
32
- # @param variables [Hash<Symbol, Variable>] Variables to define
33
- def def_variables(variables)
34
- variables.each { |id, entity| def_variable(id, entity) }
35
- end
36
-
37
- # Define a (or re-define an existing) function
38
- #
39
- # @param id [String, Symbol] The identifier for the function
40
- # @param entity [Entities::Function] The defined function
41
- def def_function(id, entity)
42
- @functions[id.to_sym] = entity
43
- end
44
-
45
- # Is the variable defined?
46
- #
47
- # @param var_id [Symbol, String] The identifier of the variable
48
- #
49
- # @return [boolean]
50
- def defined_variable?(var_id)
51
- @variables.key?(var_id.to_sym)
52
- end
53
-
54
- # Is the function defined?
55
- #
56
- # @param fn_id [Symbol, String] The identifier of the function
57
- #
58
- # @return [boolean]
59
- def defined_function?(fn_id)
60
- @functions.key?(fn_id.to_sym)
61
- end
62
-
63
- # @return [String]
64
- def to_s
65
- "CodeSection(functions: #{@functions}, variables: #{@variables})"
66
- end
67
-
68
- # Provide a summary of the functions and variables compiled (to show in verbose mode)
69
- #
70
- # @return [String]
71
- def verbose_summary
72
- <<~SUMMARY
73
- # Code Section Summary
74
-
75
- ## Resolved Variables
76
-
77
- #{variable_summary}
78
-
79
- ## Functions
80
-
81
- #{function_summary}
82
- SUMMARY
83
- end
84
-
85
- private
86
-
87
- def variable_summary
88
- return '(no variables defined)' if @variables.empty?
89
-
90
- @variables.map { |k, v| "#{k} := #{v}" }
91
- .join("\n")
92
- end
93
-
94
- def function_summary
95
- return '(no functions defined)' if @functions.empty?
96
-
97
- @functions.map { |k, f| "#{k}: #{f}" }
98
- .join("\n")
99
- end
100
- end
101
- end
@@ -1,18 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module CSVPlusPlus
4
- Expand =
5
- ::Struct.new(:repetitions) do
6
- # Does this infinitely expand?
7
- def infinite?
8
- repetitions.nil?
9
- end
10
-
11
- # to_s
12
- def to_s
13
- "Expand #{repetitions || 'infinity'}"
14
- end
15
- end
16
-
17
- public_constant :Expand
18
- end
@@ -1,62 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'tsort'
4
-
5
- module CSVPlusPlus
6
- # Graph ordering and searching functions
7
- module Graph
8
- # Get a list of all variables references in a given +ast+
9
- # TODO: this is only used in one place - refactor it
10
- def self.variable_references(ast, runtime, include_runtime_variables: false)
11
- depth_first_search(ast) do |node|
12
- next unless node.variable?
13
-
14
- node.id if !runtime.runtime_variable?(node.id) || include_runtime_variables
15
- end
16
- end
17
-
18
- # Create a dependency graph of +variables+
19
- def self.dependency_graph(variables, runtime)
20
- ::CSVPlusPlus::Graph::DependencyGraph[
21
- variables.map { |var_id, ast| [var_id, variable_references(ast, runtime)] }
22
- ]
23
- end
24
-
25
- # Perform a topological sort on a +DependencyGraph+. A toplogical sort is noteworthy
26
- # because it will give us the order in which we need to resolve our variable dependencies.
27
- #
28
- # Given this dependency graph:
29
- #
30
- # { a: [b c], b: [c], c: [d], d: [] }
31
- #
32
- # it will return:
33
- #
34
- # [d, c, b, a]
35
- #
36
- def self.topological_sort(dependencies)
37
- dependencies.tsort
38
- end
39
-
40
- # Do a DFS on an AST starting at +node+
41
- def self.depth_first_search(node, accum = [], &)
42
- ret = yield(node)
43
- accum << ret unless ret.nil?
44
-
45
- return accum unless node.function_call?
46
-
47
- node.arguments.each { |n| depth_first_search(n, accum, &) }
48
- accum
49
- end
50
-
51
- # A dependency graph represented as a +Hash+ which will be used by our +topological_sort+ function
52
- class DependencyGraph < Hash
53
- include ::TSort
54
- alias tsort_each_node each_key
55
-
56
- # sort each child
57
- def tsort_each_child(node, &)
58
- fetch(node).each(&)
59
- end
60
- end
61
- end
62
- end
@@ -1,68 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative './entities'
4
-
5
- module CSVPlusPlus
6
- module Language
7
- # Some helpful functions that can be mixed into a class to help building ASTs
8
- module ASTBuilder
9
- # Let the current class have functions which can build a given entity by calling it's type. For example
10
- # +number(1)+, +variable(:foo)+
11
- #
12
- # @param method_name [Symbol] The +method_name+ to respond to
13
- # @param arguments [] The arguments to create the entity with
14
- #
15
- # @return [Entity, #super]
16
- def method_missing(method_name, *args, **kwargs, &)
17
- entity_class = ::CSVPlusPlus::Language::TYPES[method_name.to_sym]
18
- return super unless entity_class
19
-
20
- entity_class.new(*args, **kwargs, &)
21
- end
22
-
23
- # Let the current class have functions which can build a given entity by calling it's type. For example
24
- # +number(1)+, +variable(:foo)+
25
- #
26
- # @param method_name [Symbol] The +method_name+ to respond to
27
- # @param arguments [] The arguments to create the entity with
28
- #
29
- # @return [Boolean, #super]
30
- def respond_to_missing?(method_name, *_arguments)
31
- ::CSVPlusPlus::Language::TYPES.include?(method_name.to_sym) || super
32
- end
33
-
34
- # Turns index-based/X,Y coordinates into a A1 format
35
- #
36
- # @param row_index [Integer]
37
- # @param cell_index [Integer]
38
- #
39
- # @return [String]
40
- def ref(row_index: nil, cell_index: nil)
41
- return unless row_index || cell_index
42
-
43
- rowref = row_index ? (row_index + 1).to_s : ''
44
- cellref = cell_index ? cell_ref(cell_index) : ''
45
- cell_reference([cellref, rowref].join)
46
- end
47
-
48
- private
49
-
50
- ALPHA = ('A'..'Z').to_a.freeze
51
- private_constant :ALPHA
52
-
53
- def cell_ref(cell_index)
54
- c = cell_index.dup
55
- ref = ''
56
-
57
- while c >= 0
58
- # rubocop:disable Lint/ConstantResolution
59
- ref += ALPHA[c % 26]
60
- # rubocop:enable Lint/ConstantResolution
61
- c = (c / 26).floor - 1
62
- end
63
-
64
- ref.reverse
65
- end
66
- end
67
- end
68
- end
@@ -1,65 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'benchmark'
4
-
5
- module CSVPlusPlus
6
- module Language
7
- # Extend a +Compiler+ class and add benchmark timings
8
- # @attr_reader timings [Array<Benchmark::Tms>] +Benchmark+ timings that have been accumulated by each step of
9
- # compilation
10
- # @attr_reader benchmark [Benchmark] A +Benchmark+ instance
11
- module BenchmarkedCompiler
12
- attr_reader :benchmark, :timings
13
-
14
- # Wrap a +Compiler+ with our instance methods that add benchmarks
15
- def self.with_benchmarks(compiler, &block)
16
- ::Benchmark.benchmark(::Benchmark::CAPTION, 25, ::Benchmark::FORMAT, '> Total') do |x|
17
- # compiler = new(options:, runtime:, benchmark: x)
18
- compiler.extend(self)
19
- compiler.benchmark = x
20
-
21
- block.call(compiler)
22
-
23
- [compiler.timings.reduce(:+)]
24
- end
25
- end
26
-
27
- # @param benchmark [Benchmark] A +Benchmark+ instance
28
- def benchmark=(benchmark)
29
- @benchmark = benchmark
30
- @timings = []
31
- end
32
-
33
- # Time the Compiler#outputting! stage
34
- def outputting!
35
- time_stage('Writing the spreadsheet') { super }
36
- end
37
-
38
- protected
39
-
40
- def parse_code_section!
41
- time_stage('Parsing code section') { super }
42
- end
43
-
44
- def parse_csv_section!
45
- time_stage('Parsing CSV section') { super }
46
- end
47
-
48
- def expanding
49
- time_stage('Expanding rows') { super }
50
- end
51
-
52
- def resolve_all_cells!(template)
53
- time_stage('Resolving each cell') { super(template) }
54
- end
55
-
56
- private
57
-
58
- def time_stage(stage, &block)
59
- ret = nil
60
- @timings << @benchmark.report(stage) { ret = block.call }
61
- ret
62
- end
63
- end
64
- end
65
- end
@@ -1,46 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative './ast_builder'
4
-
5
- module CSVPlusPlus
6
- module Language
7
- # Provides ASTs for builtin functions and variables
8
- module Builtins
9
- extend ::CSVPlusPlus::Language::ASTBuilder
10
-
11
- VARIABLES = {
12
- # The number (integer) of the current cell. Starts at 1
13
- cellnum: runtime_value(->(runtime) { number(runtime.cell_index + 1) }),
14
-
15
- # A reference to the current cell
16
- cellref: runtime_value(->(runtime) { ref(row_index: runtime.row_index, cell_index: runtime.cell_index) }),
17
-
18
- # A reference to the row above
19
- rowabove: runtime_value(->(runtime) { ref(row_index: [0, (runtime.row_index - 1)].max) }),
20
-
21
- # A reference to the row below
22
- rowbelow: runtime_value(->(runtime) { ref(row_index: runtime.row_index + 1) }),
23
-
24
- # The number (integer) of the current row. Starts at 1
25
- rownum: runtime_value(->(runtime) { number(runtime.rownum) }),
26
-
27
- # A reference to the current row
28
- rowref: runtime_value(->(runtime) { ref(row_index: runtime.row_index) })
29
- }.freeze
30
- public_constant :VARIABLES
31
-
32
- FUNCTIONS = {
33
- # TODO: A reference to a cell in a given row?
34
- # A reference to a cell above the current row
35
- cellabove: runtime_value(->(runtime, args) { cell_reference([args[0], [1, (runtime.rownum - 1)].max].join) }),
36
-
37
- # A reference to a cell in the current row
38
- celladjacent: runtime_value(->(runtime, args) { cell_reference([args[0], runtime.rownum].join) }),
39
-
40
- # A reference to a cell below the current row
41
- cellbelow: runtime_value(->(runtime, args) { cell_reference([args[0], runtime.rownum + 1].join) })
42
- }.freeze
43
- public_constant :FUNCTIONS
44
- end
45
- end
46
- end