Class: CSVPlusPlus::Runtime::Scope
- Inherits:
-
Object
- Object
- CSVPlusPlus::Runtime::Scope
- Extended by:
- T::Sig
- Defined in:
- lib/csv_plus_plus/runtime/scope.rb
Overview
Responsible for storing and resolving variables and function references rubocop:disable Metrics/ClassLength
Instance Attribute Summary collapse
-
#functions ⇒ Object
readonly
Returns the value of attribute functions.
-
#variables ⇒ Object
readonly
Returns the value of attribute variables.
Instance Method Summary collapse
-
#call_function(function, function_call) ⇒ Entities::Entity
Since functions are just built up from existing functions, a “call” is effectively replacing the variable references in the @body with the ones being passed as arguments.
- #call_function_or_builtin(position, function_or_builtin, function_call) ⇒ Entities::Entity
-
#def_function(id, function) ⇒ Entities::Function
Define a (or re-define an existing) function.
-
#def_variable(id, entity) ⇒ Entity
Define a (or re-define an existing) variable.
-
#def_variables(vars) ⇒ Object
Define (or re-define existing) variables.
-
#function_replace(position, node, fn_id, replacement) ⇒ Object
Make a copy of the AST represented by
node
and replacefn_id
withreplacement
throughout rubocop:disable Metrics/MethodLength. -
#function_summary ⇒ String
Create a summary of all currently defined functions.
-
#in_scope?(var_id, position) ⇒ boolean
Variables outside of an ![[expand=…] are always in scope. If it’s defined within an expand then things get trickier because the variable is only in scope while we’re processing cells within that expand..
-
#initialize(functions: {}, variables: {}) ⇒ Scope
constructor
A new instance of Scope.
- #resolve_function(fn_id) ⇒ Entities::Function
- #resolve_functions(position, ast, refs) ⇒ Entity
- #resolve_variable(position, var_id) ⇒ Entities::Entity
- #resolve_variables(position, ast, refs) ⇒ Entity
-
#variable_replace(node, var_id, replacement) ⇒ Object
Make a copy of the AST represented by
node
and replacevar_id
withreplacement
throughout. -
#variable_summary ⇒ String
Create a summary of all currently defined variables.
-
#verbose_summary ⇒ ::String
Provide a summary of the functions and variables compiled (to show in verbose mode).
Constructor Details
#initialize(functions: {}, variables: {}) ⇒ Scope
Returns a new instance of Scope.
25 26 27 28 |
# File 'lib/csv_plus_plus/runtime/scope.rb', line 25 def initialize(functions: {}, variables: {}) @functions = functions @variables = variables end |
Instance Attribute Details
#functions ⇒ Object (readonly)
Returns the value of attribute functions.
12 13 14 |
# File 'lib/csv_plus_plus/runtime/scope.rb', line 12 def functions @functions end |
#variables ⇒ Object (readonly)
Returns the value of attribute variables.
15 16 17 |
# File 'lib/csv_plus_plus/runtime/scope.rb', line 15 def variables @variables end |
Instance Method Details
#call_function(function, function_call) ⇒ Entities::Entity
Since functions are just built up from existing functions, a “call” is effectively replacing the variable references in the @body with the ones being passed as arguments
210 211 212 213 214 215 216 217 |
# File 'lib/csv_plus_plus/runtime/scope.rb', line 210 def call_function(function, function_call) i = 0 function.arguments.reduce(function.body.dup) do |ast, argument| variable_replace(ast, argument, ::T.must(function_call.arguments[i])).tap do i += 1 end end end |
#call_function_or_builtin(position, function_or_builtin, function_call) ⇒ Entities::Entity
189 190 191 192 193 194 195 |
# File 'lib/csv_plus_plus/runtime/scope.rb', line 189 def call_function_or_builtin(position, function_or_builtin, function_call) if function_or_builtin.is_a?(::CSVPlusPlus::Entities::RuntimeValue) function_or_builtin.call(position, function_call.arguments) else call_function(function_or_builtin, function_call) end end |
#def_function(id, function) ⇒ Entities::Function
Define a (or re-define an existing) function
58 59 60 |
# File 'lib/csv_plus_plus/runtime/scope.rb', line 58 def def_function(id, function) @functions[id.to_sym] = function end |
#def_variable(id, entity) ⇒ Entity
Define a (or re-define an existing) variable
37 38 39 |
# File 'lib/csv_plus_plus/runtime/scope.rb', line 37 def def_variable(id, entity) @variables[id] = entity end |
#def_variables(vars) ⇒ Object
Define (or re-define existing) variables
45 46 47 |
# File 'lib/csv_plus_plus/runtime/scope.rb', line 45 def def_variables(vars) vars.each { |id, entity| def_variable(id, entity) } end |
#function_replace(position, node, fn_id, replacement) ⇒ Object
Make a copy of the AST represented by node
and replace fn_id
with replacement
throughout rubocop:disable Metrics/MethodLength
145 146 147 148 149 150 151 152 153 154 155 156 157 158 |
# File 'lib/csv_plus_plus/runtime/scope.rb', line 145 def function_replace(position, node, fn_id, replacement) if node.is_a?(::CSVPlusPlus::Entities::FunctionCall) && node.id == fn_id call_function_or_builtin(position, replacement, node) elsif node.is_a?(::CSVPlusPlus::Entities::FunctionCall) # not our function, but continue our depth first search on it ::CSVPlusPlus::Entities::FunctionCall.new( node.id, node.arguments.map { |n| function_replace(position, n, fn_id, replacement) }, infix: node.infix ) else node end end |
#function_summary ⇒ String
Create a summary of all currently defined functions
271 272 273 274 275 276 |
# File 'lib/csv_plus_plus/runtime/scope.rb', line 271 def function_summary return '(no functions defined)' if @functions.empty? @functions.map { |k, f| "#{k}: #{f}" } .join("\n") end |
#in_scope?(var_id, position) ⇒ boolean
Variables outside of an ![[expand=…] are always in scope. If it’s defined within an expand then things get trickier because the variable is only in scope while we’re processing cells within that expand.
70 71 72 73 74 75 76 77 |
# File 'lib/csv_plus_plus/runtime/scope.rb', line 70 def in_scope?(var_id, position) value = @variables[var_id] return false unless value = value.is_a?(::CSVPlusPlus::Entities::Reference) && value.a1_ref. ! || .position_within?(position) end |
#resolve_function(fn_id) ⇒ Entities::Function
168 169 170 171 172 173 174 175 |
# File 'lib/csv_plus_plus/runtime/scope.rb', line 168 def resolve_function(fn_id) return ::T.must(@functions[fn_id]) if @functions.key?(fn_id) builtin = ::CSVPlusPlus::Entities::Builtins::FUNCTIONS[fn_id] raise(::CSVPlusPlus::Error::FormulaSyntaxError.new('Undefined function', bad_input: fn_id.to_s)) unless builtin builtin end |
#resolve_functions(position, ast, refs) ⇒ Entity
109 110 111 112 113 |
# File 'lib/csv_plus_plus/runtime/scope.rb', line 109 def resolve_functions(position, ast, refs) refs.reduce(ast.dup) do |acc, elem| function_replace(position, acc, elem.id, resolve_function(elem.id)) end end |
#resolve_variable(position, var_id) ⇒ Entities::Entity
246 247 248 249 250 251 252 253 254 |
# File 'lib/csv_plus_plus/runtime/scope.rb', line 246 def resolve_variable(position, var_id) return ::T.must(@variables[var_id]) if @variables.key?(var_id) unless ::CSVPlusPlus::Entities::Builtins.builtin_variable?(var_id) raise(::CSVPlusPlus::Error::FormulaSyntaxError.new('Undefined variable', bad_input: var_id.to_s)) end ::T.must(::CSVPlusPlus::Entities::Builtins::VARIABLES[var_id]).call(position, []) end |
#resolve_variables(position, ast, refs) ⇒ Entity
127 128 129 130 131 132 133 |
# File 'lib/csv_plus_plus/runtime/scope.rb', line 127 def resolve_variables(position, ast, refs) refs.reduce(ast.dup) do |acc, elem| next acc unless (id = elem.id) variable_replace(acc, id, resolve_variable(position, id)) end end |
#variable_replace(node, var_id, replacement) ⇒ Object
Make a copy of the AST represented by node
and replace var_id
with replacement
throughout
227 228 229 230 231 232 233 234 235 236 237 |
# File 'lib/csv_plus_plus/runtime/scope.rb', line 227 def variable_replace(node, var_id, replacement) if node.is_a?(::CSVPlusPlus::Entities::FunctionCall) arguments = node.arguments.map { |n| variable_replace(n, var_id, replacement) } # TODO: refactor these places where we copy functions... it's brittle with the kwargs ::CSVPlusPlus::Entities::FunctionCall.new(node.id, arguments, infix: node.infix) elsif node.is_a?(::CSVPlusPlus::Entities::Reference) && node.id == var_id replacement else node end end |
#variable_summary ⇒ String
Create a summary of all currently defined variables
260 261 262 263 264 265 |
# File 'lib/csv_plus_plus/runtime/scope.rb', line 260 def variable_summary return '(no variables defined)' if @variables.empty? @variables.map { |k, v| "#{k} := #{v}" } .join("\n") end |
#verbose_summary ⇒ ::String
Provide a summary of the functions and variables compiled (to show in verbose mode)
83 84 85 86 87 88 89 90 91 92 93 94 95 |
# File 'lib/csv_plus_plus/runtime/scope.rb', line 83 def verbose_summary <<~SUMMARY # Code Section Summary ## Resolved Variables #{variable_summary} ## Functions #{function_summary} SUMMARY end |