Browse Source

Parse function definitions

master
Dylan Baker 5 years ago
parent
commit
71b2364613
4 changed files with 72 additions and 11 deletions
  1. 1
    0
      lib/chervil/ast.rb
  2. 15
    0
      lib/chervil/ast/function.rb
  3. 28
    5
      lib/chervil/parser.rb
  4. 28
    6
      spec/parser_spec.rb

+ 1
- 0
lib/chervil/ast.rb View File

@@ -5,6 +5,7 @@ module Chervil
5 5
     require "chervil/ast/application"
6 6
     require "chervil/ast/boolean"
7 7
     require "chervil/ast/definition"
8
+    require "chervil/ast/function"
8 9
     require "chervil/ast/identifier"
9 10
     require "chervil/ast/number"
10 11
     require "chervil/ast/string"

+ 15
- 0
lib/chervil/ast/function.rb View File

@@ -0,0 +1,15 @@
1
+module Chervil::AST
2
+  class Function
3
+    attr_reader :params
4
+    attr_reader :body
5
+
6
+    def initialize(params, body)
7
+      @params = params
8
+      @body = body
9
+    end
10
+
11
+    def ==(other)
12
+      @params == other.params && @body == other.body
13
+    end
14
+  end
15
+end

+ 28
- 5
lib/chervil/parser.rb View File

@@ -28,11 +28,7 @@ module Chervil
28 28
       eat(:lparen)
29 29
 
30 30
       if @current_token.type == :identifier && @current_token.value == 'define'
31
-        eat(:identifier)
32
-        name = identifier
33
-        value = expr
34
-        eat(:rparen)
35
-        return AST::Definition.new(name, value)
31
+        return definition
36 32
       end
37 33
 
38 34
       expression = expr
@@ -47,6 +43,33 @@ module Chervil
47 43
       AST::Application.new(expression, args)
48 44
     end
49 45
 
46
+    def definition
47
+      eat(:identifier) # the `define`
48
+      if @current_token.type == :identifier
49
+        name = identifier
50
+        value = expr
51
+        eat(:rparen)
52
+        AST::Definition.new(name, value)
53
+      elsif @current_token.type == :lparen
54
+        eat(:lparen)
55
+        name = identifier
56
+        params = Array.new
57
+        until [:rparen, :eof].include?(@current_token.type)
58
+          params << expr
59
+        end
60
+        eat(:rparen)
61
+
62
+        body = Array.new
63
+        until [:rparen, :eof].include?(@current_token.type)
64
+          body << expr
65
+        end
66
+
67
+        eat(:rparen)
68
+
69
+        AST::Definition.new(name, AST::Function.new(params, body))
70
+      end
71
+    end
72
+
50 73
     def identifier
51 74
       identifier = eat(:identifier)
52 75
       AST::Identifier.new(identifier.value)

+ 28
- 6
spec/parser_spec.rb View File

@@ -18,6 +18,15 @@ module Chervil
18 18
       expect(parse('+').first).to eq(AST::Identifier.new('+'))
19 19
     end
20 20
 
21
+    it 'parses booleans' do
22
+      expect(parse('#t #f')).to eq(
23
+        [
24
+          AST::Boolean.new(true),
25
+          AST::Boolean.new(false),
26
+        ]
27
+      )
28
+    end
29
+
21 30
     it 'parses an application' do
22 31
       expect(parse('(+ 1 2)').first).to eq(
23 32
         AST::Application.new(
@@ -57,12 +66,25 @@ module Chervil
57 66
       )
58 67
     end
59 68
 
60
-    it 'parses booleans' do
61
-      expect(parse('#t #f')).to eq(
62
-        [
63
-          AST::Boolean.new(true),
64
-          AST::Boolean.new(false),
65
-        ]
69
+    it 'parses a function definition' do
70
+      expect(parse('(define (square x) (* x x))').first).to eq(
71
+        AST::Definition.new(
72
+          AST::Identifier.new("square"),
73
+          AST::Function.new(
74
+            [
75
+              AST::Identifier.new("x")
76
+            ],
77
+            [
78
+              AST::Application.new(
79
+                AST::Identifier.new("*"),
80
+                [
81
+                  AST::Identifier.new("x"),
82
+                  AST::Identifier.new("x"),
83
+                ]
84
+              )
85
+            ]
86
+          )
87
+        )
66 88
       )
67 89
     end
68 90
   end

Loading…
Cancel
Save