Class: Kl::Environment

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Primitives::Arithmetic

#*, #+, #-, #/, #<, #<=, #>, #>=, #number?

Methods included from Primitives::Streams

#close, #open, #pr

Methods included from Primitives::Vectors

#absvector, #absvector?

Methods included from Primitives::GenericFunctions

#freeze

Methods included from Primitives::Lists

#cons, #cons?, #hd, #tl

Methods included from Primitives::Assignments

#set, #value

Methods included from Primitives::Strings

#cn, #pos, #str, #string?, #tlstr

Methods included from Primitives::Symbols

#intern

Methods included from Primitives::Booleans

#and, #or

Constructor Details

#initializeEnvironment

Returns a new instance of Environment.



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/kl/environment.rb', line 30

def initialize
  @dump_code = false
  @tramp_fn = @tramp_args  = nil
  @variables = Hash.new do |_, k|
    raise Kl::Error, "#{k} is not a symbol" unless k.kind_of? Symbol
    raise Kl::Error, "variable #{k} has no value"
  end
  @functions = Hash.new do |h, k|
    if respond_to? k
      fn = method(k).to_proc
      h[k] = fn
    else
      raise Kl::Error, "The function #{k} is undefined"
    end
  end
  @eigenklass = class << self; self; end
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(f, *args) ⇒ Object



109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/kl/environment.rb', line 109

def method_missing(f, *args)
  if @functions.has_key?(f)
    fn = @functions[f]
  else
    coerced_f = Kl::Environment.ruby_to_kl(f)
    if @functions.has_key?(coerced_f)
      fn = @functions[coerced_f]
    else
      super
    end
  end
  coerced_args = args.map { |arg| Kl::Environment.ruby_to_kl(arg)}
  Kl::Environment.kl_to_ruby(__apply(fn, coerced_args))
end

Class Method Details

.kl_to_ruby(x) ⇒ Object

Coerce Ruby types and conventions to K Lambda



141
142
143
144
145
146
147
148
149
# File 'lib/kl/environment.rb', line 141

def kl_to_ruby(x)
  if x.kind_of? Kl::Cons
    x.to_a.map { |y| kl_to_ruby(y) }
  elsif x.kind_of? Symbol
    x.to_s.gsub(/-/, '_').to_sym
  else
    x
  end
end

.load_file(env, path) ⇒ Object



131
132
133
134
135
136
137
138
# File 'lib/kl/environment.rb', line 131

def load_file(env, path)
  File.open(path, 'r') do |file|
    reader = Kl::Reader.new(file)
    while form = reader.next
      env.__eval(form)
    end
  end
end

.ruby_to_kl(x) ⇒ Object

Coerce K Lambda types and conventions to Ruby



152
153
154
155
156
157
158
159
160
# File 'lib/kl/environment.rb', line 152

def ruby_to_kl(x)
  if x.kind_of? Array
    Kl::Cons.list(x.map {|x| ruby_to_kl(x)})
  elsif x.kind_of? Symbol
    x.to_s.gsub(/_/, '-').to_sym
  else
    x
  end
end

Instance Method Details

#__apply(fn, args) ⇒ Object

Trampoline-aware function application



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/kl/environment.rb', line 49

def __apply(fn, args)
  while fn
    @tramp_fn = nil
    fn = @functions[fn] if fn.kind_of? Symbol
    arity = fn.arity
    if arity == args.size || arity == -1
      result = fn.call(*args)
    elsif arity > args.size
      # Partial application
      result = fn.curry.call(*args)
    else
      # Uncurrying. Apply fn to its expected number of arguments
      # and hope that the result is a function that can be applied
      # to the remainder.
      fn = __apply(fn, args[0, arity])
      unless fn.kind_of?(Proc) || fn.kind_of?(Symbol)
        raise ::Kl::Error,
              "The value #{str(fn)} is neither a function nor a symbol."
      end
      args = args[arity..-1]
      next
    end

    if fn = @tramp_fn
      # Bounce on the trampoline
      args = @tramp_args
      @tramp_args = nil
    end
  end
  result
rescue SystemStackError
  raise ::Kl::Error, 'maximum stack depth exceeded'
end

#__eval(form) ⇒ Object



83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/kl/environment.rb', line 83

def __eval(form)
  if @dump_code
    puts "=" * 70
    puts "Compiling:"
    puts Kl::Cons.list_to_string(form)
    puts '-----'
  end
  code = ::Kl::Compiler.compile(form, {}, true)
  if @dump_code
    puts code
    puts "=" * 70
  end
  @tramp_fn = nil
  result = instance_eval(code)
  # Handle top-level trampolines
  if @tramp_fn
    fn = @tramp_fn
    args = @tramp_args
    @tramp_fn = nil
    @tramp_args = nil
    __apply(fn, args)
  else
    result
  end
end

#respond_to?(f) ⇒ Boolean

Returns:

  • (Boolean)


124
125
126
127
128
# File 'lib/kl/environment.rb', line 124

def respond_to?(f)
  @functions.has_key?(f) ||
    @functions.has_key?(Kl::Environment.ruby_to_kl(f)) ||
      super
end