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

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

@@ -43,6 +43,8 @@ module Chervil
43 43
           return definition
44 44
         elsif @current_token.value == 'if'
45 45
           return conditional
46
+        elsif @current_token.value == 'lambda'
47
+          return lambda_
46 48
         end
47 49
       end
48 50
 
@@ -92,6 +94,28 @@ module Chervil
92 94
       AST::Conditional.new(predicate, true_branch, false_branch)
93 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 119
     def identifier
96 120
       identifier = eat(:identifier)
97 121
       AST::Identifier.new(identifier.value)

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

@@ -43,5 +43,29 @@ module Chervil
43 43
         .evaluate(env))
44 44
         .to eq(Error.new("Unbound variable x"))
45 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 70
   end
47 71
 end

+ 18
- 0
spec/parser_spec.rb View File

@@ -111,5 +111,23 @@ module Chervil
111 111
         )
112 112
       )
113 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 132
   end
115 133
 end

Loading…
Cancel
Save