parent
cf72deef8b
commit
22a126dd0b
@ -1,135 +1,156 @@
|
||||
require_relative "error"
|
||||
require_relative "expr"
|
||||
require_relative "stmt"
|
||||
|
||||
module Lox
|
||||
class Parser
|
||||
class State < Struct.new(:tokens, :current)
|
||||
def initialize(tokens)
|
||||
super(tokens, 0)
|
||||
|
||||
def initialize(tokens)
|
||||
@tokens = tokens
|
||||
@current = 0
|
||||
end
|
||||
|
||||
def parse!
|
||||
statements = []
|
||||
until eot?
|
||||
statements << statement
|
||||
end
|
||||
statements
|
||||
end
|
||||
|
||||
def expression = equality
|
||||
private
|
||||
|
||||
def equality
|
||||
expr = comparison
|
||||
def statement
|
||||
return print if match?(:PRINT)
|
||||
|
||||
while match?(:BANG_EQUAL, :EQUAL_EQUAL)
|
||||
op = prev
|
||||
right = comparison
|
||||
expr = Expr::Binary.new(expr, op, right)
|
||||
end
|
||||
expressionStmt
|
||||
end
|
||||
|
||||
expr
|
||||
end
|
||||
def print
|
||||
value = expression
|
||||
consume!(:SEMICOLON, "Expect ';' after value.")
|
||||
Stmt::Print.new(value)
|
||||
end
|
||||
|
||||
def comparison
|
||||
expr = term
|
||||
def expressionStmt
|
||||
value = expression
|
||||
consume!(:SEMICOLON, "Expect ';' after value.")
|
||||
Stmt::Expr.new(value)
|
||||
end
|
||||
|
||||
while match?(:GREATER, :GREATER_EQUAL, :LESS, :LESS_EQUAL)
|
||||
op = prev
|
||||
right = term
|
||||
expr = Expr::Binary.new(expr, op, right)
|
||||
end
|
||||
def expression = equality
|
||||
|
||||
expr
|
||||
end
|
||||
def equality
|
||||
expr = comparison
|
||||
|
||||
while match?(:BANG_EQUAL, :EQUAL_EQUAL)
|
||||
op = prev
|
||||
right = comparison
|
||||
expr = Expr::Binary.new(expr, op, right)
|
||||
end
|
||||
|
||||
def term
|
||||
expr = factor
|
||||
expr
|
||||
end
|
||||
|
||||
while match?(:MINUS, :PLUS)
|
||||
op = prev
|
||||
right = factor
|
||||
expr = Expr::Binary.new(expr, op, right)
|
||||
end
|
||||
def comparison
|
||||
expr = term
|
||||
|
||||
expr
|
||||
while match?(:GREATER, :GREATER_EQUAL, :LESS, :LESS_EQUAL)
|
||||
op = prev
|
||||
right = term
|
||||
expr = Expr::Binary.new(expr, op, right)
|
||||
end
|
||||
|
||||
def factor
|
||||
expr = unary
|
||||
expr
|
||||
end
|
||||
|
||||
while match?(:SLASH, :STAR)
|
||||
op = prev
|
||||
right = unary
|
||||
expr = Expr::Binary.new(expr, op, right)
|
||||
end
|
||||
def term
|
||||
expr = factor
|
||||
|
||||
expr
|
||||
while match?(:MINUS, :PLUS)
|
||||
op = prev
|
||||
right = factor
|
||||
expr = Expr::Binary.new(expr, op, right)
|
||||
end
|
||||
|
||||
def unary
|
||||
return primary unless match?(:BANG, :MINUS)
|
||||
expr
|
||||
end
|
||||
|
||||
def factor
|
||||
expr = unary
|
||||
|
||||
while match?(:SLASH, :STAR)
|
||||
op = prev
|
||||
right = unary
|
||||
Expr::Unary.new(op, right)
|
||||
expr = Expr::Binary.new(expr, op, right)
|
||||
end
|
||||
|
||||
def primary
|
||||
return Expr::Literal.new(false) if match?(:FALSE)
|
||||
return Expr::Literal.new(true) if match?(:TRUE)
|
||||
return Expr::Literal.new(nil) if match?(:NIL)
|
||||
return Expr::Literal.new(prev.literal) if match?(:NUMBER, :STRING)
|
||||
|
||||
if match?(:LEFT_PAREN)
|
||||
expr = expression
|
||||
consume!(:RIGHT_PAREN, "Expect ')' after expression.")
|
||||
return Expr::Grouping.new(expr)
|
||||
end
|
||||
expr
|
||||
end
|
||||
|
||||
raise ParseError.new(peek, "Expect expression.")
|
||||
end
|
||||
def unary
|
||||
return primary unless match?(:BANG, :MINUS)
|
||||
|
||||
private
|
||||
op = prev
|
||||
right = unary
|
||||
Expr::Unary.new(op, right)
|
||||
end
|
||||
|
||||
def match?(*types)
|
||||
return false unless check?(*types)
|
||||
def primary
|
||||
return Expr::Literal.new(false) if match?(:FALSE)
|
||||
return Expr::Literal.new(true) if match?(:TRUE)
|
||||
return Expr::Literal.new(nil) if match?(:NIL)
|
||||
return Expr::Literal.new(prev.literal) if match?(:NUMBER, :STRING)
|
||||
|
||||
advance!
|
||||
return true
|
||||
if match?(:LEFT_PAREN)
|
||||
expr = expression
|
||||
consume!(:RIGHT_PAREN, "Expect ')' after expression.")
|
||||
return Expr::Grouping.new(expr)
|
||||
end
|
||||
|
||||
def consume!(type, message)
|
||||
raise ParseError.new(peek, message) unless check?(type)
|
||||
raise ParseError.new(peek, "Expect expression.")
|
||||
end
|
||||
|
||||
advance!
|
||||
end
|
||||
private
|
||||
|
||||
def check?(*types)
|
||||
return false if eot?
|
||||
def match?(*types)
|
||||
return false unless check?(*types)
|
||||
|
||||
types.include?(peek.type)
|
||||
end
|
||||
advance!
|
||||
return true
|
||||
end
|
||||
|
||||
def advance!
|
||||
self.current += 1 unless eot?
|
||||
prev
|
||||
end
|
||||
def consume!(type, message)
|
||||
raise ParseError.new(peek, message) unless check?(type)
|
||||
|
||||
def eot? = peek.type === :EOF
|
||||
def peek = tokens.fetch(current)
|
||||
def prev = tokens.fetch(current - 1)
|
||||
advance!
|
||||
end
|
||||
|
||||
def synchronize!
|
||||
advance!
|
||||
def check?(*types)
|
||||
return false if eot?
|
||||
|
||||
until eot?
|
||||
return if prev.type == :SEMICOLON
|
||||
return if %i[CLASS FUN VAR FOR IF WHILE PRINT RETURN].include?(peek.type)
|
||||
types.include?(peek.type)
|
||||
end
|
||||
|
||||
advance!
|
||||
end
|
||||
end
|
||||
def advance!
|
||||
@current += 1 unless eot?
|
||||
prev
|
||||
end
|
||||
|
||||
# In the book, this returns nil when there's an error, but
|
||||
# that feels weird so let's move that error handling up the
|
||||
# stack for now.
|
||||
def parse(tokens)
|
||||
state = State.new(tokens)
|
||||
state.expression
|
||||
def eot? = peek.type === :EOF
|
||||
def peek = @tokens.fetch(@current)
|
||||
def prev = @tokens.fetch(@current - 1)
|
||||
|
||||
def synchronize!
|
||||
advance!
|
||||
|
||||
until eot?
|
||||
return if prev.type == :SEMICOLON
|
||||
return if %i[CLASS FUN VAR FOR IF WHILE PRINT RETURN].include?(peek.type)
|
||||
|
||||
advance!
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -0,0 +1,13 @@
|
||||
require_relative "visitable"
|
||||
|
||||
module Lox
|
||||
module Stmt
|
||||
Expr = Struct.new(:expr) do
|
||||
include Visitable
|
||||
end
|
||||
|
||||
Print = Struct.new(:expr) do
|
||||
include Visitable
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,8 @@
|
||||
module Lox
|
||||
module Visitable
|
||||
def accept(visitor)
|
||||
klass = self.class.to_s.split("::").last.downcase
|
||||
visitor.send("visit_#{klass}", self)
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in new issue