Class: Crisp::Functions::Core

Inherits:
Object
  • Object
show all
Defined in:
lib/crisp/functions/core.rb

Overview

Defining core crisp functions

Class Method Summary collapse

Class Method Details

.load(current_env) ⇒ Object

load the functions and bind them into the given environment



6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
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
82
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
108
109
110
111
112
# File 'lib/crisp/functions/core.rb', line 6

def self.load(current_env)

  # reserve nil/true/false in environment
  current_env['nil'] = nil
  current_env['true'] = true
  current_env['false'] = false

  # println
  # print arguments (seperated by whitspace) including a newline to the standard output
  #
  #  (println 123)
  #  123
  Function.new do
    print args_evaled.collect(&:to_s).join(' ') + "\n"
  end.bind('println', current_env)

  # def
  # bind the second argument to the symbol name of the first argument
  # actually a key/value pair will be stored in the environment
  #
  #  (def foo 1)
  #  => foo
  #  (println foo)
  #  1
  Function.new do
    validate_args_count(2, args.size)

    key = args[0].text_value
    value = args[1].resolve_and_eval(env)

    if value.class.name == "Crisp::Function"
      value.bind(key, env)
    else
      env[key] = value
    end
  end.bind('def', current_env)

  # fn
  # creates a function
  # the first argument has to be an array containing the argumentlist
  # the second argument is the function body
  #
  #  (fn [a b] (+ a b)
  #  => ...)
  Function.new do
    validate_args_count(2, args.size)

    if args[0].class.name != "Crisp::Nodes::ArrayLiteral"
      raise ArgumentError, "no argument list defined"
    end

    if args[1].class.name != "Crisp::Nodes::Operation"
      raise ArgumentError, "no function body defined"
    end

    fn_arg_list = args[0].raw_elements
    fn_operation = args[1]

    # create and return the new function
    Function.new do
      validate_args_count(fn_arg_list.size, args.size)

      local_env = Env.new
      fn_arg_list.each_with_index do |key, idx|
        local_env[key.text_value] = args[idx].resolve_and_eval(env)
      end

      chained_env = ChainedEnv.new(local_env, env)

      fn_operation.eval(chained_env)
    end
  end.bind('fn', current_env)

  # if
  # the if function evaluates the second argument if the condition (first argument) returns not nil or false.
  # Otherwise the third argument will be evaluated (optional)
  #
  #  (if (= 1 2) 1 2)
  #  => 2
  Function.new do
    validate_args_count((2..3), args.size)

    result = args[0].resolve_and_eval(env)

    res = if ![nil, false].include?(result)
      args[1]
    elsif args[2]
      args[2]
    end

    res ? res.resolve_and_eval(env) : res
  end.bind('if', current_env)

  # .
  # perform a native call on an object with optional arguments
  #
  #  (. upcase "abc")
  #  => "ABC"
  Function.new do
    meth = args[0].text_value.to_sym
    target = args[1].resolve_and_eval(env)
    values = args[2..-1].map { |arg| arg.resolve_and_eval(env) }

    NativeCallInvoker.new(target, meth, values).invoke!
  end.bind('.', current_env)

end