Browse Source

Implement lambdas

master
Dylan Baker 5 years ago
parent
commit
0a4d8d334d
4 changed files with 69 additions and 1 deletions
  1. 3
    1
      lib/chervil/ast/list.rb
  2. 24
    0
      lib/chervil/parser.rb
  3. 24
    0
      spec/ast/list_spec.rb
  4. 18
    0
      spec/parser_spec.rb

+ 3
- 1
lib/chervil/ast/list.rb View File

15
         []
15
         []
16
       else
16
       else
17
         head = @elements.first
17
         head = @elements.first
18
-        if head.class == Identifier
18
+        if head.is_a?(Identifier)
19
           function = env.get(head.name)
19
           function = env.get(head.name)
20
 
20
 
21
           if function.nil?
21
           if function.nil?
23
           else
23
           else
24
             function.call(@elements[1..-1].map { |arg| arg.evaluate(env) }, env)
24
             function.call(@elements[1..-1].map { |arg| arg.evaluate(env) }, env)
25
           end
25
           end
26
+        elsif head.is_a?(Function)
27
+          head.call(@elements[1..-1].map { |arg| arg.evaluate(env) }, env)
26
         else
28
         else
27
           @elements.map { |el| el.evaluate(env) }
29
           @elements.map { |el| el.evaluate(env) }
28
         end
30
         end

+ 24
- 0
lib/chervil/parser.rb View File

43
           return definition
43
           return definition
44
         elsif @current_token.value == 'if'
44
         elsif @current_token.value == 'if'
45
           return conditional
45
           return conditional
46
+        elsif @current_token.value == 'lambda'
47
+          return lambda_
46
         end
48
         end
47
       end
49
       end
48
 
50
 
92
       AST::Conditional.new(predicate, true_branch, false_branch)
94
       AST::Conditional.new(predicate, true_branch, false_branch)
93
     end
95
     end
94
 
96
 
97
+    def lambda_
98
+      eat(:identifier) # the `lambda`
99
+
100
+      name = AST::Identifier.new("lambda")
101
+      params = Array.new
102
+
103
+      eat(:lparen)
104
+      while @current_token.type == :identifier
105
+        params.push(identifier)
106
+      end
107
+      eat(:rparen)
108
+
109
+      body = Array.new
110
+      until [:rparen, :eof].include?(@current_token.type)
111
+        body << expr
112
+      end
113
+
114
+      eat(:rparen)
115
+
116
+      AST::Function.new(name, params, body)
117
+    end
118
+
95
     def identifier
119
     def identifier
96
       identifier = eat(:identifier)
120
       identifier = eat(:identifier)
97
       AST::Identifier.new(identifier.value)
121
       AST::Identifier.new(identifier.value)

+ 24
- 0
spec/ast/list_spec.rb View File

43
         .evaluate(env))
43
         .evaluate(env))
44
         .to eq(Error.new("Unbound variable x"))
44
         .to eq(Error.new("Unbound variable x"))
45
     end
45
     end
46
+
47
+    it 'applies a function in the first position' do
48
+      env = Env.new
49
+      list = AST::List.new(
50
+        [
51
+          AST::Function.new(
52
+            AST::Identifier.new("lambda"),
53
+            [AST::Identifier.new("x")],
54
+            [
55
+              AST::List.new(
56
+                [
57
+                  AST::Identifier.new("+"),
58
+                  AST::Identifier.new("x"),
59
+                  AST::Number.new(1.0),
60
+                ]
61
+              )
62
+            ]
63
+          ),
64
+          AST::Number.new(5.0),
65
+        ]
66
+      )
67
+
68
+      expect(list.evaluate(env)).to eq(6.0)
69
+    end
46
   end
70
   end
47
 end
71
 end

+ 18
- 0
spec/parser_spec.rb View File

111
         )
111
         )
112
       )
112
       )
113
     end
113
     end
114
+
115
+    it 'parses a lambda expression' do
116
+      expect(parse('(lambda (x) (* x x))').first).to eq(
117
+        AST::Function.new(
118
+          AST::Identifier.new("lambda"),
119
+          [AST::Identifier.new("x")],
120
+          [
121
+            AST::List.new(
122
+              [
123
+                AST::Identifier.new("*"),
124
+                AST::Identifier.new("x"),
125
+                AST::Identifier.new("x"),
126
+              ]
127
+            ),
128
+          ]
129
+        )
130
+      )
131
+    end
114
   end
132
   end
115
 end
133
 end

Loading…
Cancel
Save