class RubyLisp::Function
Attributes
bindings[RW]
body[RW]
env[RW]
is_macro[RW]
lambda[RW]
name[RW]
Public Class Methods
new(name, env, asts, &block)
click to toggle source
Calls superclass method
# File lib/rubylisp/function.rb, line 46 def initialize(name, env, asts, &block) super() @name = name @env = env @arities = construct_arities(asts) @is_macro = false @lambda = block end
Public Instance Methods
construct_arities(asts)
click to toggle source
# File lib/rubylisp/function.rb, line 55 def construct_arities(asts) arities_hash = asts.each_with_object({'arities' => [], 'required' => 0}) do |ast, result| arity = Arity.new(ast) if arity.rest_args.empty? # Prevent conflicts like [x] vs. [y] if result['arities'].any? {|existing| existing.required_args.count == arity.required_args.count && existing.rest_args.empty? } raise RuntimeError, "Can't have multiple overloads with the same arity." end # Prevent conflicts like [& xs] vs. [x] if result['rest_required'] unless arity.required_args.count <= result['rest_required'] raise RuntimeError, "Can't have a fixed arity function with more params than a " + "variadic function." end end else # Prevent conflicts like [x] vs. [& xs] if arity.required_args.count < result['required'] raise RuntimeError, "Can't have a fixed arity function with more params than a " + "variadic function." end # Prevent conflicts like [x & xs] vs. [x y & ys] if result['arities'].any? {|existing| !existing.rest_args.empty?} raise RuntimeError, "Can't have more than one variadic overload." end result['rest_required'] = arity.required_args.count end result['required'] = [result['required'], arity.required_args.count].max result['arities'] << arity end arities_hash['arities'] end
gen_env(arity, args, env)
click to toggle source
# File lib/rubylisp/function.rb, line 121 def gen_env(arity, args, env) # set out_env to the current namespace so that `def` occurring within # the fn's environment will define things in the namespace in which the # function is called out_env = env.out_env || env.find_namespace env = Environment.new(outer: @env, out_env: out_env) # so the fn can call itself recursively env.set(@name, self) if arity.rest_args.empty? # bind values to the required args arity.required_args.zip(args).each do |k, v| env.set(k, v) end else # bind values to the required args (the rest args are skipped here) arity.required_args.zip(args).each do |k, v| env.set(k, v) end # bind the rest argument to the remaining arguments or nil rest_args = if args.count > arity.required_args.count args[arity.required_args.count..-1].to_list else nil end env.set(arity.rest_args.first, rest_args) end env end
get_arity(args)
click to toggle source
# File lib/rubylisp/function.rb, line 102 def get_arity(args) # Assert that there are enough arguments provided for the arities we have. sexp = list [Symbol.new(@name), *args] variadic = @arities.find {|arity| !arity.rest_args.empty?} fixed_arities = @arities.select {|arity| arity.rest_args.empty?} fixed = fixed_arities.find {|arity| arity.required_args.count == args.count} # Return the arity most appropriate for the number of args provided. if fixed fixed elsif variadic assert_at_least_n_args sexp, variadic.required_args.count variadic else raise RuntimeError, "Wrong number of args (#{args.count}) passed to #{@name}" end end