CodeTools::AST << {
ParameterAssembly < Node { node_type params field required: [] field optional: [] field rest: null field post: [] field kwrequired: [] field kwoptional: [] field kwrest: null field block: null keywords_any?: self.kwrequired.any? || self.kwoptional.any? || self.kwrest # Called on a new method or block generator # to set up various parameter-related properties. generator_setup: |g| { g.required_args = self.required.size + self.post.size g.post_args = self.post.size g.total_args = self.required.size + self.optional.size + self.post.size + (self.keywords_any? &? 1 ?? 0) g.splat_index = self.rest &? (self.required.size + self.optional.size) ?? null g.block_index = self.block && ( self.required.size + self.optional.size + (self.rest &? 1 ?? 0) + self.post.size + (self.keywords_any? &? 1 ?? 0) + 1 # for self.block ) g.arity = self.arity g.keywords = self.keyword_entries } # TODO: remove/refactor out keyword_entries: { entries = \ kwrequired.map |x| { [x.name, true] } + kwoptional.map |x| { [x.name, false] } entries = entries.flatten(1) entries.empty? &? null ?? entries } # TODO: remove/refactor out arity: { arity = self.required.size + self.post.size kwrequired.any? && (arity = arity + 1) (self.rest || self.optional.any? || kwoptional.any?) && ( arity = arity + 1 arity = arity * -1 ) arity } all_params: [ *self.required, *self.optional, *(self.rest &? [self.rest] ?? []), *self.post, *self.kwrequired, *self.kwoptional, *(self.kwrest &? [self.kwrest] ?? []), *(self.block &? [self.block] ?? []), ] names: all_params.map(&:name).compact # TODO: simplify bytecode: |g| { g.state.check_for_locals = false all_params.each |param| { param.map_local(g.state.scope) } self.required.each |param| { param.bytecode(g) } self.optional.each |param| { param.bytecode(g) } self.rest && self.rest.bytecode(g) self.keywords_any? && ( kw_done = g.new_label assignments_label = g.new_label missing_value_label = g.new_label defaults_label = g.new_label g.state.scope.search_local(:__myco_keywords_value__).get_bytecode(g) g.dup_top; g.goto_if_not_nil(assignments_label) # TODO: trivially remove this line? g.pop g.push_cpath_top g.find_const(:Hash) g.send(:allocate, 0, true) assignments_label.set! self.kwrequired.each |param| { g.dup_top g.push_literal(param.name) g.send(:find_item, 1, true) g.dup_top g.goto_if_false(missing_value_label) g.send(:value, 0, true) param_var = g.state.scope.search_local(param.name) param_var.set_bytecode(g) g.pop } g.goto(defaults_label) missing_value_label.set! g.pop g.push_rubinius g.find_const(:Runtime) g.swap g.send(:keywords_missing, 1, true) g.goto(kw_done) defaults_label.set! extra_keys_label = g.new_label self.kwoptional.empty? &? ( g.dup_top g.send(:size, 0, true) g.push(self.kwrequired.size) g.goto_if_not_equal(extra_keys_label) self.kwrest && ( g.push_cpath_top g.find_const(:Hash) g.send(:allocate, 0, true) kwrest_asgn = g.state.scope.search_local(self.kwrest.name) kwrest_asgn.set_bytecode(g) g.pop ) g.goto(kw_done) ) ?? ( self.kwoptional.each |param| { next_value_label = g.new_label default_value_label = g.new_label g.dup_top g.push_literal(param.name) g.send(:find_item, 1, true) g.dup_top g.goto_if_false(default_value_label) g.send(:value, 0, true) g.state.scope.search_local(param.name).set_bytecode(g) g.goto(next_value_label) default_value_label.set! g.pop param.value.bytecode(g) g.state.scope.search_local(param.name).set_bytecode(g) next_value_label.set! g.pop } ) extra_keys_label.set! g.dup_top g.push_rubinius g.find_const(:Runtime) g.swap self.kwrest &? g.push(:true) ?? g.push(:false) g.send(:keywords_extra, 2, true) self.kwrest && ( kwrest_asgn = g.state.scope.search_local(self.kwrest.name) kwrest_asgn.set_bytecode(g) ) g.pop kw_done.set! ) self.block && self.block.bytecode(g) g.state.check_for_locals = true } }
}