Browse Source

Add unary operations

master
Dylan Baker 4 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,5 +17,6 @@ module AST
17 17
   require 'ahem/ast/number'
18 18
   require 'ahem/ast/operators'
19 19
   require 'ahem/ast/string'
20
+  require 'ahem/ast/unary'
20 21
   require 'ahem/ast/variable_declaration'
21 22
 end

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

@@ -10,4 +10,5 @@ module AST::Operators
10 10
   DOUBLE_EQUALS = :==
11 11
   OR = :or
12 12
   AND = :and
13
+  NOT = :not
13 14
 end

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

@@ -0,0 +1,21 @@
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,6 +82,9 @@ class Lexer
82 82
     elsif source.match(/^=>/)
83 83
       @position += 2
84 84
       Token.new(TokenKinds::ROCKET)
85
+    elsif source.match(/^!/)
86
+      @position += 1
87
+      Token.new(TokenKinds::OPERATOR, :!)
85 88
     elsif source.match(/^==/)
86 89
       @position += 2
87 90
       Token.new(TokenKinds::OPERATOR, :==)
@@ -103,6 +106,9 @@ class Lexer
103 106
     elsif source.match(/^or/)
104 107
       @position += 2
105 108
       Token.new(TokenKinds::OPERATOR, :or)
109
+    elsif source.match(/^not/)
110
+      @position += 3
111
+      Token.new(TokenKinds::OPERATOR, :not)
106 112
     elsif source.match(/^\=/)
107 113
       @position += 1
108 114
       Token.new(TokenKinds::EQUALS)

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

@@ -189,19 +189,30 @@ class Parser
189 189
   end
190 190
 
191 191
   def multiplication
192
-    left = primary
192
+    left = unary
193 193
 
194 194
     if @current_token.type == TokenKinds::OPERATOR &&
195 195
        %i[* /].include?(@current_token.value)
196 196
       operator = @current_token.value
197 197
       advance
198
-      right = primary
198
+      right = unary
199 199
       AST::Binary.new(operator, left, right)
200 200
     else
201 201
       left
202 202
     end
203 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 216
   def primary
206 217
     token = @current_token
207 218
     expr = case token.type

+ 2
- 1
spec/lexer_spec.rb View File

@@ -32,7 +32,7 @@ RSpec.describe Lexer do
32 32
   end
33 33
 
34 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 37
         Token.new(TokenKinds::OPERATOR, :+),
38 38
         Token.new(TokenKinds::OPERATOR, :-),
@@ -45,6 +45,7 @@ RSpec.describe Lexer do
45 45
         Token.new(TokenKinds::OPERATOR, :==),
46 46
         Token.new(TokenKinds::OPERATOR, :and),
47 47
         Token.new(TokenKinds::OPERATOR, :or),
48
+        Token.new(TokenKinds::OPERATOR, :not),
48 49
         Token.new(TokenKinds::EOF)
49 50
       ]
50 51
     )

+ 5
- 0
spec/parser_spec.rb View File

@@ -355,4 +355,9 @@ RSpec.describe Parser do
355 355
       ]
356 356
     )
357 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 363
 end

Loading…
Cancel
Save