Browse Source

Replace Application with List

I see what I was going for with the Application AST node but
Chervil is a Lisp after all and lists are the fundamental data
structure. The direct reason I wanted to do this was for quoted values,
since a quoted list isn't an application.
master
Dylan Baker 6 years ago
parent
commit
26a93cd9f0

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

2
 
2
 
3
 module Chervil
3
 module Chervil
4
   module AST
4
   module AST
5
-    require "chervil/ast/application"
6
     require "chervil/ast/boolean"
5
     require "chervil/ast/boolean"
7
     require "chervil/ast/conditional"
6
     require "chervil/ast/conditional"
8
     require "chervil/ast/definition"
7
     require "chervil/ast/definition"
9
     require "chervil/ast/function"
8
     require "chervil/ast/function"
10
     require "chervil/ast/identifier"
9
     require "chervil/ast/identifier"
10
+    require "chervil/ast/list"
11
     require "chervil/ast/number"
11
     require "chervil/ast/number"
12
     require "chervil/ast/string"
12
     require "chervil/ast/string"
13
   end
13
   end

+ 0
- 27
lib/chervil/ast/application.rb View File

1
-module Chervil::AST
2
-  class Application
3
-    attr_reader :expr
4
-    attr_reader :arguments
5
-
6
-    def initialize(expr, arguments)
7
-      @expr = expr
8
-      @arguments = arguments
9
-    end
10
-
11
-    def ==(other)
12
-      @expr == other.expr && @arguments == other.arguments
13
-    end
14
-
15
-    def evaluate(env)
16
-      if @expr.class == Identifier
17
-        function = env.get(@expr.name)
18
-
19
-        if function.nil?
20
-          ::Chervil::Error.new("Unbound variable #{@expr.name}")
21
-        else
22
-          function.call(@arguments.map { |arg| arg.evaluate(env) }, env)
23
-        end
24
-      end
25
-    end
26
-  end
27
-end

+ 30
- 0
lib/chervil/ast/list.rb View File

1
+module Chervil::AST
2
+  class List
3
+    attr_reader :elements
4
+
5
+    def initialize(elements)
6
+      @elements = elements
7
+    end
8
+
9
+    def ==(other)
10
+      @elements == other.elements
11
+    end
12
+
13
+    def evaluate(env)
14
+      if elements.empty?
15
+        []
16
+      else
17
+        head = @elements.first
18
+        if head.class == Identifier
19
+          function = env.get(head.name)
20
+
21
+          if function.nil?
22
+            ::Chervil::Error.new("Unbound variable #{head.name}")
23
+          else
24
+            function.call(@elements[1..-1].map { |arg| arg.evaluate(env) }, env)
25
+          end
26
+        end
27
+      end
28
+    end
29
+  end
30
+end

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

21
       elsif @current_token.type == :identifier
21
       elsif @current_token.type == :identifier
22
         identifier
22
         identifier
23
       elsif @current_token.type == :lparen
23
       elsif @current_token.type == :lparen
24
-        application
24
+        list
25
       else
25
       else
26
         @error = Error.new("Unexpected token #{@current_token.type}: #{@current_token.value}")
26
         @error = Error.new("Unexpected token #{@current_token.type}: #{@current_token.value}")
27
       end
27
       end
28
     end
28
     end
29
 
29
 
30
-    def application
30
+    def list
31
       eat(:lparen)
31
       eat(:lparen)
32
 
32
 
33
       if @current_token.type == :identifier
33
       if @current_token.type == :identifier
38
         end
38
         end
39
       end
39
       end
40
 
40
 
41
-      expression = expr
42
-
43
-      args = Array.new
41
+      elements = Array.new
44
       until @current_token.type == :rparen || @current_token.type == :eof
42
       until @current_token.type == :rparen || @current_token.type == :eof
45
-        args << expr
43
+        elements << expr
46
       end
44
       end
47
 
45
 
48
       eat(:rparen)
46
       eat(:rparen)
49
 
47
 
50
-      AST::Application.new(expression, args)
48
+      AST::List.new(elements)
51
     end
49
     end
52
 
50
 
53
     def definition
51
     def definition

+ 0
- 41
spec/ast/application_spec.rb View File

1
-module Chervil
2
-  RSpec.describe AST::Application do
3
-    it 'evaluates addition' do
4
-      env = Env.new
5
-      expect(
6
-        AST::Application.new(
7
-          AST::Identifier.new('+'),
8
-          [AST::Number.new(1.0), AST::Number.new(2.0)]
9
-        )
10
-          .evaluate(env)
11
-      ).to eq(3.0)
12
-    end
13
-
14
-    it 'evaluates nested arithmetic' do
15
-      env = Env.new
16
-      expect(
17
-        AST::Application.new(
18
-          AST::Identifier.new('+'),
19
-          [
20
-            AST::Application.new(
21
-              AST::Identifier.new('+'),
22
-              [AST::Number.new(1.0), AST::Number.new(2.0)]
23
-            ),
24
-            AST::Application.new(
25
-              AST::Identifier.new('+'),
26
-              [AST::Number.new(3.0), AST::Number.new(4.0)]
27
-            )
28
-          ]
29
-        )
30
-          .evaluate(env)
31
-      ).to eq(10.0)
32
-    end
33
-
34
-    it 'returns an error if the function is not defined' do
35
-      env = Env.new
36
-      expect(AST::Application.new(AST::Identifier.new("x"), []).evaluate(env)).to eq(
37
-        Error.new("Unbound variable x")
38
-      )
39
-    end
40
-  end
41
-end

+ 4
- 4
spec/ast/conditional_spec.rb View File

22
 
22
 
23
     it 'evaluates complex predicates' do
23
     it 'evaluates complex predicates' do
24
       expect(AST::Conditional.new(
24
       expect(AST::Conditional.new(
25
-        AST::Application.new(
26
-          AST::Identifier.new(">"),
25
+        AST::List.new(
27
           [
26
           [
27
+            AST::Identifier.new(">"),
28
             AST::Number.new(2.0),
28
             AST::Number.new(2.0),
29
             AST::Number.new(1.0),
29
             AST::Number.new(1.0),
30
           ]
30
           ]
39
     it 'evaluates complex branches' do
39
     it 'evaluates complex branches' do
40
       expect(AST::Conditional.new(
40
       expect(AST::Conditional.new(
41
         AST::Boolean.new(true),
41
         AST::Boolean.new(true),
42
-        AST::Application.new(
43
-          AST::Identifier.new("+"),
42
+        AST::List.new(
44
           [
43
           [
44
+            AST::Identifier.new("+"),
45
             AST::Number.new(2.0),
45
             AST::Number.new(2.0),
46
             AST::Number.new(1.0),
46
             AST::Number.new(1.0),
47
           ]
47
           ]

+ 2
- 2
spec/ast/function_spec.rb View File

8
             AST::Identifier.new("x")
8
             AST::Identifier.new("x")
9
           ],
9
           ],
10
           [
10
           [
11
-            AST::Application.new(
12
-              AST::Identifier.new("*"),
11
+            AST::List.new(
13
               [
12
               [
13
+                AST::Identifier.new("*"),
14
                 AST::Identifier.new("x"),
14
                 AST::Identifier.new("x"),
15
                 AST::Identifier.new("x")
15
                 AST::Identifier.new("x")
16
               ]
16
               ]

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

1
+module Chervil
2
+  RSpec.describe AST::List do
3
+    it 'evaluates addition' do
4
+      env = Env.new
5
+      expect(
6
+        AST::List.new(
7
+          [AST::Identifier.new('+'),
8
+          AST::Number.new(1.0), AST::Number.new(2.0)]
9
+        )
10
+          .evaluate(env)
11
+      ).to eq(3.0)
12
+    end
13
+
14
+    it 'evaluates nested arithmetic' do
15
+      env = Env.new
16
+      expect(
17
+        AST::List.new(
18
+          [
19
+            AST::Identifier.new('+'),
20
+            AST::List.new(
21
+              [
22
+                AST::Identifier.new('+'),
23
+                AST::Number.new(1.0),
24
+                AST::Number.new(2.0)
25
+              ]
26
+            ),
27
+            AST::List.new(
28
+              [
29
+                AST::Identifier.new('+'),
30
+                AST::Number.new(3.0),
31
+                AST::Number.new(4.0)
32
+              ]
33
+            )
34
+          ]
35
+        )
36
+          .evaluate(env)
37
+      ).to eq(10.0)
38
+    end
39
+
40
+    it 'returns an error if the function is not defined' do
41
+      env = Env.new
42
+      expect(AST::List.new([AST::Identifier.new("x")])
43
+        .evaluate(env))
44
+        .to eq(Error.new("Unbound variable x"))
45
+    end
46
+  end
47
+end

+ 2
- 2
spec/interpreter_spec.rb View File

26
       expect(interpret('(plus-one 2)', env).first).to eq(3.0)
26
       expect(interpret('(plus-one 2)', env).first).to eq(3.0)
27
     end
27
     end
28
 
28
 
29
-    it 'doesn\'t leave variables bound after function execution' do
29
+    it "doesn't leave variables bound after function execution" do
30
       env = Env.new
30
       env = Env.new
31
       interpret('(define (plus-one x) (+ 1 x))', env)
31
       interpret('(define (plus-one x) (+ 1 x))', env)
32
       interpret('(plus-one 5)', env)
32
       interpret('(plus-one 5)', env)
35
 
35
 
36
     it 'returns a type error for invalid operation' do
36
     it 'returns a type error for invalid operation' do
37
       expect(interpret('(+ 1 "hello")', Env.new).first).to eq(
37
       expect(interpret('(+ 1 "hello")', Env.new).first).to eq(
38
-        Error.new("Expected an argument of type number but got string")
38
+        Error.new('Expected an argument of type number but got string')
39
       )
39
       )
40
     end
40
     end
41
   end
41
   end

+ 22
- 14
spec/parser_spec.rb View File

26
 
26
 
27
     it 'parses an application' do
27
     it 'parses an application' do
28
       expect(parse('(+ 1 2)').first).to eq(
28
       expect(parse('(+ 1 2)').first).to eq(
29
-        AST::Application.new(
30
-          AST::Identifier.new('+'),
31
-          [AST::Number.new(1.0), AST::Number.new(2.0)]
29
+        AST::List.new(
30
+          [AST::Identifier.new('+'), AST::Number.new(1.0), AST::Number.new(2.0)]
32
         )
31
         )
33
       )
32
       )
34
     end
33
     end
41
 
40
 
42
     it 'parses a nested expression' do
41
     it 'parses a nested expression' do
43
       expect(parse('(+ (+ 1 2) (- 3 4))').first).to eq(
42
       expect(parse('(+ (+ 1 2) (- 3 4))').first).to eq(
44
-        AST::Application.new(
45
-          AST::Identifier.new('+'),
43
+        AST::List.new(
46
           [
44
           [
47
-            AST::Application.new(
48
-              AST::Identifier.new('+'),
49
-              [AST::Number.new(1.0), AST::Number.new(2.0)]
45
+            AST::Identifier.new('+'),
46
+            AST::List.new(
47
+              [
48
+                AST::Identifier.new('+'),
49
+                AST::Number.new(1.0),
50
+                AST::Number.new(2.0)
51
+              ]
50
             ),
52
             ),
51
-            AST::Application.new(
52
-              AST::Identifier.new('-'),
53
-              [AST::Number.new(3.0), AST::Number.new(4.0)]
53
+            AST::List.new(
54
+              [
55
+                AST::Identifier.new('-'),
56
+                AST::Number.new(3.0),
57
+                AST::Number.new(4.0)
58
+              ]
54
             )
59
             )
55
           ]
60
           ]
56
         )
61
         )
65
             AST::Identifier.new('square'),
70
             AST::Identifier.new('square'),
66
             [AST::Identifier.new('x')],
71
             [AST::Identifier.new('x')],
67
             [
72
             [
68
-              AST::Application.new(
69
-                AST::Identifier.new('*'),
70
-                [AST::Identifier.new('x'), AST::Identifier.new('x')]
73
+              AST::List.new(
74
+                [
75
+                  AST::Identifier.new('*'),
76
+                  AST::Identifier.new('x'),
77
+                  AST::Identifier.new('x')
78
+                ]
71
               )
79
               )
72
             ]
80
             ]
73
           )
81
           )

Loading…
Cancel
Save