csv_plus_plus 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +8 -0
  3. data/README.md +18 -62
  4. data/lib/csv_plus_plus/benchmarked_compiler.rb +62 -0
  5. data/lib/csv_plus_plus/{code_section.rb → can_define_references.rb} +22 -35
  6. data/lib/csv_plus_plus/can_resolve_references.rb +8 -0
  7. data/lib/csv_plus_plus/cell.rb +3 -3
  8. data/lib/csv_plus_plus/cli.rb +24 -7
  9. data/lib/csv_plus_plus/color.rb +12 -6
  10. data/lib/csv_plus_plus/compiler.rb +156 -0
  11. data/lib/csv_plus_plus/data_validation.rb +138 -0
  12. data/lib/csv_plus_plus/{language → entities}/ast_builder.rb +3 -5
  13. data/lib/csv_plus_plus/entities/boolean.rb +31 -0
  14. data/lib/csv_plus_plus/{language → entities}/builtins.rb +2 -4
  15. data/lib/csv_plus_plus/entities/cell_reference.rb +60 -0
  16. data/lib/csv_plus_plus/entities/date.rb +30 -0
  17. data/lib/csv_plus_plus/entities/entity.rb +84 -0
  18. data/lib/csv_plus_plus/entities/function.rb +33 -0
  19. data/lib/csv_plus_plus/entities/function_call.rb +35 -0
  20. data/lib/csv_plus_plus/entities/number.rb +34 -0
  21. data/lib/csv_plus_plus/entities/runtime_value.rb +26 -0
  22. data/lib/csv_plus_plus/entities/string.rb +29 -0
  23. data/lib/csv_plus_plus/entities/variable.rb +25 -0
  24. data/lib/csv_plus_plus/entities.rb +33 -0
  25. data/lib/csv_plus_plus/error/error.rb +10 -0
  26. data/lib/csv_plus_plus/error/formula_syntax_error.rb +36 -0
  27. data/lib/csv_plus_plus/error/modifier_syntax_error.rb +27 -0
  28. data/lib/csv_plus_plus/error/modifier_validation_error.rb +49 -0
  29. data/lib/csv_plus_plus/{language → error}/syntax_error.rb +6 -14
  30. data/lib/csv_plus_plus/error/writer_error.rb +9 -0
  31. data/lib/csv_plus_plus/error.rb +9 -2
  32. data/lib/csv_plus_plus/expand.rb +3 -1
  33. data/lib/csv_plus_plus/google_api_client.rb +4 -0
  34. data/lib/csv_plus_plus/lexer/lexer.rb +13 -6
  35. data/lib/csv_plus_plus/modifier/conditional_formatting.rb +17 -0
  36. data/lib/csv_plus_plus/modifier.rb +73 -65
  37. data/lib/csv_plus_plus/{language → parser}/cell_value.tab.rb +20 -20
  38. data/lib/csv_plus_plus/{language → parser}/code_section.tab.rb +83 -87
  39. data/lib/csv_plus_plus/parser/modifier.tab.rb +484 -0
  40. data/lib/csv_plus_plus/references.rb +68 -0
  41. data/lib/csv_plus_plus/row.rb +0 -3
  42. data/lib/csv_plus_plus/runtime.rb +199 -0
  43. data/lib/csv_plus_plus/scope.rb +196 -0
  44. data/lib/csv_plus_plus/template.rb +10 -10
  45. data/lib/csv_plus_plus/validated_modifier.rb +164 -0
  46. data/lib/csv_plus_plus/version.rb +1 -1
  47. data/lib/csv_plus_plus/writer/file_backer_upper.rb +6 -4
  48. data/lib/csv_plus_plus/writer/google_sheet_builder.rb +24 -29
  49. data/lib/csv_plus_plus/writer/google_sheet_modifier.rb +33 -12
  50. data/lib/csv_plus_plus/writer/rubyxl_builder.rb +3 -6
  51. data/lib/csv_plus_plus.rb +19 -10
  52. metadata +34 -24
  53. data/lib/csv_plus_plus/language/benchmarked_compiler.rb +0 -65
  54. data/lib/csv_plus_plus/language/compiler.rb +0 -152
  55. data/lib/csv_plus_plus/language/entities/boolean.rb +0 -33
  56. data/lib/csv_plus_plus/language/entities/cell_reference.rb +0 -33
  57. data/lib/csv_plus_plus/language/entities/entity.rb +0 -86
  58. data/lib/csv_plus_plus/language/entities/function.rb +0 -35
  59. data/lib/csv_plus_plus/language/entities/function_call.rb +0 -37
  60. data/lib/csv_plus_plus/language/entities/number.rb +0 -36
  61. data/lib/csv_plus_plus/language/entities/runtime_value.rb +0 -28
  62. data/lib/csv_plus_plus/language/entities/string.rb +0 -31
  63. data/lib/csv_plus_plus/language/entities/variable.rb +0 -25
  64. data/lib/csv_plus_plus/language/entities.rb +0 -28
  65. data/lib/csv_plus_plus/language/references.rb +0 -70
  66. data/lib/csv_plus_plus/language/runtime.rb +0 -205
  67. data/lib/csv_plus_plus/language/scope.rb +0 -192
  68. data/lib/csv_plus_plus/modifier.tab.rb +0 -907
@@ -1,192 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative '../code_section'
4
- require_relative '../graph'
5
- require_relative './entities'
6
- require_relative './references'
7
- require_relative './syntax_error'
8
-
9
- module CSVPlusPlus
10
- module Language
11
- # A class representing the scope of the current Template and responsible for resolving variables
12
- #
13
- # @attr_reader code_section [CodeSection] The CodeSection containing variables and functions to be resolved
14
- # @attr_reader runtime [Runtime] The compiler's current runtime
15
- #
16
- # rubocop:disable Metrics/ClassLength
17
- class Scope
18
- attr_reader :code_section, :runtime
19
-
20
- # initialize with a +Runtime+ and optional +CodeSection+
21
- #
22
- # @param runtime [Runtime]
23
- # @param code_section [Runtime, nil]
24
- def initialize(runtime:, code_section: nil)
25
- @code_section = code_section if code_section
26
- @runtime = runtime
27
- end
28
-
29
- # Resolve all values in the ast of the current cell being processed
30
- #
31
- # @return [Entity]
32
- def resolve_cell_value
33
- return unless (ast = @runtime.cell&.ast)
34
-
35
- last_round = nil
36
- loop do
37
- refs = ::CSVPlusPlus::Language::References.extract(ast, @code_section)
38
- return ast if refs.empty?
39
-
40
- # TODO: throw an error here instead I think - basically we did a round and didn't make progress
41
- return ast if last_round == refs
42
-
43
- ast = resolve_functions(resolve_variables(ast, refs.variables), refs.functions)
44
- end
45
- end
46
-
47
- # Set the +code_section+ and resolve all inner dependencies in it's variables and functions.
48
- #
49
- # @param code_section [CodeSection] The code_section to be resolved
50
- def code_section=(code_section)
51
- @code_section = code_section
52
- resolve_static_variables!
53
- end
54
-
55
- # @return [String]
56
- def to_s
57
- "Scope(code_section: #{@code_section}, runtime: #{@runtime})"
58
- end
59
-
60
- private
61
-
62
- # Resolve all variable references defined statically in the code section
63
- # TODO: experiment with getting rid of this - does it even play correctly with runtime vars?
64
- def resolve_static_variables!
65
- variables = @code_section.variables
66
- last_var_dependencies = {}
67
- loop do
68
- var_dependencies, resolution_order = variable_resolution_order(only_static_vars(variables))
69
- return if var_dependencies == last_var_dependencies
70
-
71
- # TODO: make the contract better here where we're not seting the variables of another class
72
- @code_section.variables = resolve_dependencies(var_dependencies, resolution_order, variables)
73
- last_var_dependencies = var_dependencies.clone
74
- end
75
- end
76
-
77
- def only_static_vars(var_dependencies)
78
- var_dependencies.reject { |k| @runtime.runtime_variable?(k) }
79
- end
80
-
81
- def resolve_functions(ast, refs)
82
- refs.reduce(ast.dup) do |acc, elem|
83
- function_replace(acc, elem.id, resolve_function(elem.id))
84
- end
85
- end
86
-
87
- def resolve_variables(ast, refs)
88
- refs.reduce(ast.dup) do |acc, elem|
89
- variable_replace(acc, elem.id, resolve_variable(elem.id))
90
- end
91
- end
92
-
93
- # Make a copy of the AST represented by +node+ and replace +fn_id+ with +replacement+ throughout
94
- # rubocop:disable Metrics/MethodLength
95
- def function_replace(node, fn_id, replacement)
96
- if node.function_call? && node.id == fn_id
97
- call_function_or_runtime_value(replacement, node)
98
- elsif node.function_call?
99
- # not our function, but continue our depth first search on it
100
- ::CSVPlusPlus::Language::Entities::FunctionCall.new(
101
- node.id,
102
- node.arguments.map { |n| function_replace(n, fn_id, replacement) },
103
- infix: node.infix
104
- )
105
- else
106
- node
107
- end
108
- end
109
- # rubocop:enable Metrics/MethodLength
110
-
111
- def resolve_function(fn_id)
112
- id = fn_id.to_sym
113
- return @code_section.functions[id] if @code_section.defined_function?(id)
114
-
115
- ::CSVPlusPlus::Language::Builtins::FUNCTIONS[id]
116
- end
117
-
118
- def call_function_or_runtime_value(function_or_runtime_value, function_call)
119
- if function_or_runtime_value.function?
120
- call_function(function_or_runtime_value, function_call)
121
- else
122
- function_or_runtime_value.resolve_fn.call(@runtime, function_call.arguments)
123
- end
124
- end
125
-
126
- def call_function(function, function_call)
127
- i = 0
128
- function.arguments.reduce(function.body.dup) do |ast, argument|
129
- variable_replace(ast, argument, function_call.arguments[i]).tap do
130
- i += 1
131
- end
132
- end
133
- end
134
-
135
- # Make a copy of the AST represented by +node+ and replace +var_id+ with +replacement+ throughout
136
- def variable_replace(node, var_id, replacement)
137
- if node.function_call?
138
- arguments = node.arguments.map { |n| variable_replace(n, var_id, replacement) }
139
- # TODO: refactor these places where we copy functions... it's brittle with the kwargs
140
- ::CSVPlusPlus::Language::Entities::FunctionCall.new(node.id, arguments, infix: node.infix)
141
- elsif node.variable? && node.id == var_id
142
- replacement
143
- else
144
- node
145
- end
146
- end
147
-
148
- def resolve_variable(var_id)
149
- id = var_id.to_sym
150
- return @code_section.variables[id] if @code_section.defined_variable?(id)
151
-
152
- # this will throw a syntax error if it doesn't exist (which is what we want)
153
- @runtime.runtime_value(id)
154
- end
155
-
156
- def check_unbound_vars(dependencies, variables)
157
- unbound_vars = dependencies.values.flatten - variables.keys
158
- return if unbound_vars.empty?
159
-
160
- @runtime.raise_syntax_error('Undefined variables', unbound_vars.map(&:to_s).join(', '))
161
- end
162
-
163
- def variable_resolution_order(variables)
164
- # we have a hash of variables => ASTs but they might have references to each other, so
165
- # we need to interpolate them first (before interpolating the cell values)
166
- var_dependencies = ::CSVPlusPlus::Graph.dependency_graph(variables, @runtime)
167
- # are there any references that we don't have variables for? (undefined variable)
168
- check_unbound_vars(var_dependencies, variables)
169
-
170
- # a topological sort will give us the order of dependencies
171
- [var_dependencies, ::CSVPlusPlus::Graph.topological_sort(var_dependencies)]
172
- # TODO: don't expose this exception directly to the caller
173
- rescue ::TSort::Cyclic
174
- @runtime.raise_syntax_error('Cyclic variable dependency detected', var_refs.keys)
175
- end
176
-
177
- def resolve_dependencies(var_dependencies, resolution_order, variables)
178
- {}.tap do |resolved_vars|
179
- # for each var and each dependency it has, build up and mutate resolved_vars
180
- resolution_order.each do |var|
181
- resolved_vars[var] = variables[var].dup
182
-
183
- var_dependencies[var].each do |dependency|
184
- resolved_vars[var] = variable_replace(resolved_vars[var], dependency, variables[dependency])
185
- end
186
- end
187
- end
188
- end
189
- end
190
- # rubocop:enable Metrics/ClassLength
191
- end
192
- end