Browse Source

Add unary operations

master
Dylan Baker 5 years ago
parent
commit
59efebdd72
7 changed files with 49 additions and 3 deletions
  1. 1
    0
      lib/ahem/ast.rb
  2. 1
    0
      lib/ahem/ast/operators.rb
  3. 21
    0
      lib/ahem/ast/unary.rb
  4. 6
    0
      lib/ahem/lexer.rb
  5. 13
    2
      lib/ahem/parser.rb
  6. 2
    1
      spec/lexer_spec.rb
  7. 5
    0
      spec/parser_spec.rb

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

17
   require 'ahem/ast/number'
17
   require 'ahem/ast/number'
18
   require 'ahem/ast/operators'
18
   require 'ahem/ast/operators'
19
   require 'ahem/ast/string'
19
   require 'ahem/ast/string'
20
+  require 'ahem/ast/unary'
20
   require 'ahem/ast/variable_declaration'
21
   require 'ahem/ast/variable_declaration'
21
 end
22
 end

+ 1
- 0
lib/ahem/ast/operators.rb View File

10
   DOUBLE_EQUALS = :==
10
   DOUBLE_EQUALS = :==
11
   OR = :or
11
   OR = :or
12
   AND = :and
12
   AND = :and
13
+  NOT = :not
13
 end
14
 end

+ 21
- 0
lib/ahem/ast/unary.rb View File

1
+class AST::Unary
2
+  attr_reader :operation, :expr
3
+
4
+  def initialize(operation, expr)
5
+    @operation = operation
6
+    @expr = expr
7
+  end
8
+
9
+  def ==(other)
10
+    other.operation == @operation && other.expr == @expr
11
+  end
12
+
13
+  def execute(env)
14
+    case @operation
15
+    when AST::Operators::SUBTRACT
16
+      0.0 - @expr.execute(env)
17
+    when AST::Operators::NOT
18
+      !@expr.execute(env)
19
+    end
20
+  end
21
+end

+ 6
- 0
lib/ahem/lexer.rb View File

82
     elsif source.match(/^=>/)
82
     elsif source.match(/^=>/)
83
       @position += 2
83
       @position += 2
84
       Token.new(TokenKinds::ROCKET)
84
       Token.new(TokenKinds::ROCKET)
85
+    elsif source.match(/^!/)
86
+      @position += 1
87
+      Token.new(TokenKinds::OPERATOR, :!)
85
     elsif source.match(/^==/)
88
     elsif source.match(/^==/)
86
       @position += 2
89
       @position += 2
87
       Token.new(TokenKinds::OPERATOR, :==)
90
       Token.new(TokenKinds::OPERATOR, :==)
103
     elsif source.match(/^or/)
106
     elsif source.match(/^or/)
104
       @position += 2
107
       @position += 2
105
       Token.new(TokenKinds::OPERATOR, :or)
108
       Token.new(TokenKinds::OPERATOR, :or)
109
+    elsif source.match(/^not/)
110
+      @position += 3
111
+      Token.new(TokenKinds::OPERATOR, :not)
106
     elsif source.match(/^\=/)
112
     elsif source.match(/^\=/)
107
       @position += 1
113
       @position += 1
108
       Token.new(TokenKinds::EQUALS)
114
       Token.new(TokenKinds::EQUALS)

+ 13
- 2
lib/ahem/parser.rb View File

189
   end
189
   end
190
 
190
 
191
   def multiplication
191
   def multiplication
192
-    left = primary
192
+    left = unary
193
 
193
 
194
     if @current_token.type == TokenKinds::OPERATOR &&
194
     if @current_token.type == TokenKinds::OPERATOR &&
195
        %i[* /].include?(@current_token.value)
195
        %i[* /].include?(@current_token.value)
196
       operator = @current_token.value
196
       operator = @current_token.value
197
       advance
197
       advance
198
-      right = primary
198
+      right = unary
199
       AST::Binary.new(operator, left, right)
199
       AST::Binary.new(operator, left, right)
200
     else
200
     else
201
       left
201
       left
202
     end
202
     end
203
   end
203
   end
204
 
204
 
205
+  def unary
206
+    if @current_token.type == TokenKinds::OPERATOR &&
207
+        %i[- !].include?(@current_token.value)
208
+      operator = eat(TokenKinds::OPERATOR).value
209
+      expr = primary
210
+      AST::Unary.new(operator, expr)
211
+    else
212
+      primary
213
+    end
214
+  end
215
+
205
   def primary
216
   def primary
206
     token = @current_token
217
     token = @current_token
207
     expr = case token.type
218
     expr = case token.type

+ 2
- 1
spec/lexer_spec.rb View File

32
   end
32
   end
33
 
33
 
34
   it 'lexes operators' do
34
   it 'lexes operators' do
35
-    expect(Lexer.new('+-*/<<=>>=== and or').scan_all).to eq(
35
+    expect(Lexer.new('+-*/<<=>>=== and or not').scan_all).to eq(
36
       [
36
       [
37
         Token.new(TokenKinds::OPERATOR, :+),
37
         Token.new(TokenKinds::OPERATOR, :+),
38
         Token.new(TokenKinds::OPERATOR, :-),
38
         Token.new(TokenKinds::OPERATOR, :-),
45
         Token.new(TokenKinds::OPERATOR, :==),
45
         Token.new(TokenKinds::OPERATOR, :==),
46
         Token.new(TokenKinds::OPERATOR, :and),
46
         Token.new(TokenKinds::OPERATOR, :and),
47
         Token.new(TokenKinds::OPERATOR, :or),
47
         Token.new(TokenKinds::OPERATOR, :or),
48
+        Token.new(TokenKinds::OPERATOR, :not),
48
         Token.new(TokenKinds::EOF)
49
         Token.new(TokenKinds::EOF)
49
       ]
50
       ]
50
     )
51
     )

+ 5
- 0
spec/parser_spec.rb View File

355
       ]
355
       ]
356
     )
356
     )
357
   end
357
   end
358
+
359
+  it 'parses unary expressions' do
360
+    expect(parse('-1;')).to eq([AST::Unary.new(:-, AST::Number.new(1.0))])
361
+    expect(parse('!true;')).to eq([AST::Unary.new(:!, AST::Boolean.new(true))])
362
+  end
358
 end
363
 end

Loading…
Cancel
Save