|
1 | 1 | require_relative "parser" |
2 | 2 |
|
3 | 3 | module Scheme |
4 | | - GLOBAL_ENV = { |
| 4 | + class Environment |
| 5 | + def initialize(params, args, outer_environment = nil) |
| 6 | + @outer_environment = outer_environment |
| 7 | + |
| 8 | + @env = params.zip(args).to_h |
| 9 | + end |
| 10 | + |
| 11 | + def [](key) |
| 12 | + @env[key] |
| 13 | + end |
| 14 | + |
| 15 | + def []=(key, value) |
| 16 | + @env[key] = value |
| 17 | + end |
| 18 | + |
| 19 | + def find(key) |
| 20 | + if @env.key?(key) |
| 21 | + self |
| 22 | + elsif !@outer_environment.nil? |
| 23 | + @outer_environment.find(key) |
| 24 | + else |
| 25 | + raise "Lookup error for #{key}" |
| 26 | + end |
| 27 | + end |
| 28 | + end |
| 29 | + |
| 30 | + class Procedure |
| 31 | + def initialize(params, body, environment) |
| 32 | + @params = params |
| 33 | + @body = body |
| 34 | + @environment = environment |
| 35 | + end |
| 36 | + |
| 37 | + def call(*args) |
| 38 | + Scheme.evaluate(@body, Environment.new(@params, args, @environment)) |
| 39 | + end |
| 40 | + end |
| 41 | + |
| 42 | + GLOBAL_DICT = { |
5 | 43 | :+ => proc { |a, b| a + b }, |
6 | | - :* => proc { |a, b| a * b } |
7 | | - } |
| 44 | + :* => proc { |a, b| a * b }, |
| 45 | + :- => proc { |a, b| a - b }, |
| 46 | + :/ => proc { |a, b| a / b }, |
| 47 | + :> => proc { |a, b| a > b }, |
| 48 | + :< => proc { |a, b| a < b } |
| 49 | + }.entries.transpose |
| 50 | + GLOBAL_ENV = Environment.new(GLOBAL_DICT[0], GLOBAL_DICT[1]) |
8 | 51 |
|
9 | | - def evaluate(tokens) |
| 52 | + def evaluate(tokens, environment = GLOBAL_ENV) |
10 | 53 | if tokens.is_a?(String) |
11 | 54 | tokens |
12 | 55 | elsif tokens.is_a?(Numeric) |
13 | 56 | tokens |
14 | 57 | elsif tokens.is_a?(Array) |
15 | | - token = tokens.shift |
16 | | - procedure = evaluate(token) |
17 | | - procedure.call( |
18 | | - *(tokens.map { |t| evaluate(t) }) |
19 | | - ) |
| 58 | + case tokens.first |
| 59 | + when :if |
| 60 | + condition = evaluate(tokens[1], environment) |
| 61 | + branch = condition ? tokens[2] : tokens[3] |
| 62 | + evaluate(branch, environment) |
| 63 | + when :define |
| 64 | + environment[tokens[1]] = evaluate(tokens[2], environment) |
| 65 | + nil |
| 66 | + when :quote |
| 67 | + tokens[1] |
| 68 | + when :lambda |
| 69 | + Procedure.new(tokens[1], tokens[2], environment) |
| 70 | + else |
| 71 | + token = tokens.first |
| 72 | + procedure = evaluate(token, environment) |
| 73 | + procedure.call( |
| 74 | + *(tokens[1..].map { |t| evaluate(t, environment) }) |
| 75 | + ) |
| 76 | + end |
20 | 77 | elsif tokens.is_a?(Symbol) |
21 | | - GLOBAL_ENV[tokens] |
| 78 | + environment.find(tokens)[tokens] |
22 | 79 | end |
23 | 80 | end |
24 | 81 |
|
|
0 commit comments