Browse Source

Function expressions/higher order functions

master
Dylan Baker 5 years ago
parent
commit
ebfc3e529c
4 changed files with 90 additions and 1 deletions
  1. 2
    1
      lib/ahem/ast/function_definition.rb
  2. 9
    0
      lib/ahem/parser.rb
  3. 56
    0
      spec/ast/function_call_spec.rb
  4. 23
    0
      spec/parser_spec.rb

+ 2
- 1
lib/ahem/ast/function_definition.rb View File

@@ -14,6 +14,7 @@ class AST::FunctionDefinition
14 14
   end
15 15
 
16 16
   def execute(env)
17
-    env.set(@name.name, self)
17
+    env.set(@name.name, self) unless @name.nil?
18
+    self
18 19
   end
19 20
 end

+ 9
- 0
lib/ahem/parser.rb View File

@@ -254,6 +254,8 @@ class Parser
254 254
         e = expression
255 255
         eat(TokenKinds::RPAREN)
256 256
         e
257
+      when TokenKinds::FUNCTION
258
+        function_expression
257 259
       else
258 260
         raise "Unexpected token #{token.type}"
259 261
       end
@@ -271,6 +273,13 @@ class Parser
271 273
     end
272 274
   end
273 275
 
276
+  def function_expression
277
+    eat(TokenKinds::FUNCTION)
278
+    params = parameters
279
+    body = block
280
+    AST::FunctionDefinition.new(nil, params, body)
281
+  end
282
+
274 283
   def identifier
275 284
     token = eat(TokenKinds::IDENTIFIER)
276 285
     AST::Identifier.new(token.value)

+ 56
- 0
spec/ast/function_call_spec.rb View File

@@ -127,4 +127,60 @@ RSpec.describe AST::FunctionCall do
127 127
       ).execute(env)
128 128
     end.to raise_error('Undefined variable n')
129 129
   end
130
+
131
+  # let add_one = function(n) { n + 1; };
132
+  # let do = function(f, n) { f(n); };
133
+  # do(5);
134
+  it 'executes higher order functions' do
135
+    env = Environment.new
136
+    AST::VariableDeclaration.new(
137
+      AST::Identifier.new('add_one'),
138
+      AST::FunctionDefinition.new(
139
+        nil,
140
+        [
141
+          AST::Identifier.new('n'),
142
+        ],
143
+        AST::Block.new(
144
+          [
145
+            AST::Binary.new(
146
+              AST::Operators::ADD,
147
+              AST::Identifier.new('n'),
148
+              AST::Number.new(1.0)
149
+            )
150
+          ]
151
+        )
152
+      )
153
+    ).execute(env)
154
+
155
+    AST::VariableDeclaration.new(
156
+      AST::Identifier.new('do'),
157
+      AST::FunctionDefinition.new(
158
+        nil,
159
+        [
160
+          AST::Identifier.new('f'),
161
+          AST::Identifier.new('x'),
162
+        ],
163
+        AST::Block.new(
164
+          [
165
+            AST::FunctionCall.new(
166
+              AST::Identifier.new('f'),
167
+              [
168
+                AST::Identifier.new('x')
169
+              ]
170
+            )
171
+          ]
172
+        )
173
+      )
174
+    ).execute(env)
175
+
176
+    result = AST::FunctionCall.new(
177
+      AST::Identifier.new('do'),
178
+      [
179
+        AST::Identifier.new('add_one'),
180
+        AST::Number.new(5.0)
181
+      ]
182
+    ).execute(env)
183
+
184
+    expect(result).to eq(6)
185
+  end
130 186
 end

+ 23
- 0
spec/parser_spec.rb View File

@@ -379,4 +379,27 @@ RSpec.describe Parser do
379 379
       ]
380 380
     )
381 381
   end
382
+
383
+  it 'parses a function expression' do
384
+    expect(parse('let sum = function(x, y) { x + y; };')).to eq(
385
+      [
386
+        AST::VariableDeclaration.new(
387
+          AST::Identifier.new('sum'),
388
+          AST::FunctionDefinition.new(
389
+            nil,
390
+            [AST::Identifier.new('x'), AST::Identifier.new('y')],
391
+            AST::Block.new(
392
+              [
393
+                AST::Binary.new(
394
+                  AST::Operators::ADD,
395
+                  AST::Identifier.new('x'),
396
+                  AST::Identifier.new('y'),
397
+                )
398
+              ]
399
+            )
400
+          )
401
+        )
402
+      ]
403
+    )
404
+  end
382 405
 end

Loading…
Cancel
Save