class Parser def initialize(lexer) @lexer = lexer @current_token = @lexer.get_token end def parse tree = Array.new tree << statement until at_end tree end def statement if @current_token.type == TokenKinds::IF conditional elsif @current_token.type == TokenKinds::LET variable_declaration elsif @current_token.type == TokenKinds::FUNCTION function_definition elsif @current_token.type == TokenKinds::CLASS class_definition elsif @current_token.type == TokenKinds::FOR for_loop else expr = expression eat(TokenKinds::SEMICOLON) expr end end def variable_declaration eat(TokenKinds::LET) name = identifier eat(TokenKinds::EQUALS) value = expression eat(TokenKinds::SEMICOLON) AST::VariableDeclaration.new(name, value) end def function_definition eat(TokenKinds::FUNCTION) name = identifier params = parameters body = block AST::FunctionDefinition.new(name, params, body) end def class_definition eat(TokenKinds::CLASS) class_name = AST::Identifier.new(eat(TokenKinds::CLASS_NAME).value) eat(TokenKinds::LBRACE) members = Array.new while [TokenKinds::PUBLIC, TokenKinds::PRIVATE].include?( @current_token.type ) is_public = if @current_token.type == TokenKinds::PUBLIC eat(TokenKinds::PUBLIC) true elsif @current_token.type == TokenKinds::PRIVATE eat(TokenKinds::PRIVATE) false end while !at_end && true name = identifier members << AST::Member.new(name, is_public) if @current_token.type == TokenKinds::COMMA eat(TokenKinds::COMMA) else break end end eat(TokenKinds::SEMICOLON) end methods = Array.new while @current_token.type == TokenKinds::FUNCTION methods << function_definition end eat(TokenKinds::RBRACE) AST::ClassDefinition.new(class_name, members, methods) end def conditional branches = Array.new eat(TokenKinds::IF) condition = expression block = self.block branches << AST::Branch.new(condition, block) while @current_token.type == TokenKinds::ELSEIF eat(TokenKinds::ELSEIF) condition = expression block = self.block branches << AST::Branch.new(condition, block) end if @current_token.type == TokenKinds::ELSE eat(TokenKinds::ELSE) block = self.block branches << AST::Branch.new(AST::Boolean.new(true), block) end AST::Conditional.new(branches) end def for_loop eat(TokenKinds::FOR) iterator = identifier eat(TokenKinds::IN) iterable = expression body = block AST::ForLoop.new(iterator, iterable, body) end def parameters params = Array.new eat(TokenKinds::LPAREN) while @current_token.type == TokenKinds::IDENTIFIER params << identifier if @current_token.type == TokenKinds::COMMA eat(TokenKinds::COMMA) else break end end eat(TokenKinds::RPAREN) params end def block statements = Array.new eat(TokenKinds::LBRACE) statements << statement until @current_token.type == TokenKinds::RBRACE eat(TokenKinds::RBRACE) AST::Block.new(statements) end def expression binary end private def binary left = comparison if @current_token.type == TokenKinds::OPERATOR operator = @current_token.value advance right = comparison AST::Binary.new(operator, left, right) else left end end def comparison left = multiplication if @current_token.type == TokenKinds::OPERATOR && %i[< > <= >=].include?(@current_token.type) operator = @current_token.value advance right = multiplication AST::Binary.new(operator, left, right) else left end end def addition left = multiplication if @current_token.type == TokenKinds::OPERATOR && %i[+ -].include?(@current_token.value) operator = @current_token.value advance right = multiplication AST::Binary.new(operator, left, right) else left end end def multiplication left = unary if @current_token.type == TokenKinds::OPERATOR && %i[* /].include?(@current_token.value) operator = @current_token.value advance right = unary AST::Binary.new(operator, left, right) else left end end def unary if @current_token.type == TokenKinds::OPERATOR && %i[- !].include?(@current_token.value) operator = eat(TokenKinds::OPERATOR).value expr = primary AST::Unary.new(operator, expr) else primary end end def primary token = @current_token expr = case token.type when TokenKinds::NULL advance AST::Null.new when TokenKinds::BOOLEAN advance AST::Boolean.new(token.value) when TokenKinds::NUMBER advance AST::Number.new(token.value) when TokenKinds::STRING advance AST::String.new(token.value) when TokenKinds::ATOM advance AST::Atom.new(token.value) when TokenKinds::IDENTIFIER identifier when TokenKinds::LBRACKET array when TokenKinds::LBRACE hash when TokenKinds::LPAREN eat(TokenKinds::LPAREN) e = expression eat(TokenKinds::RPAREN) e else raise "Unexpected token #{token.type}" end if @current_token.type == TokenKinds::LPAREN args = arguments AST::FunctionCall.new(expr, args) elsif @current_token.type == TokenKinds::LBRACKET eat(TokenKinds::LBRACKET) key = expression eat(TokenKinds::RBRACKET) AST::Index.new(expr, key) else expr end end def identifier token = eat(TokenKinds::IDENTIFIER) AST::Identifier.new(token.value) end def array elements = Array.new eat(TokenKinds::LBRACKET) until @current_token.type == TokenKinds::RBRACKET elements << expression if @current_token.type == TokenKinds::COMMA eat(TokenKinds::COMMA) else break end end eat(TokenKinds::RBRACKET) AST::Array.new(elements) end def hash h = Hash.new eat(TokenKinds::LBRACE) until @current_token.type == TokenKinds::RBRACE key = eat(TokenKinds::ATOM).value eat(TokenKinds::ROCKET) value = expression h[key] = value if @current_token.type == TokenKinds::COMMA eat(TokenKinds::COMMA) else break end end eat(TokenKinds::RBRACE) AST::Hash.new(h) end def arguments args = Array.new eat(TokenKinds::LPAREN) until @current_token.type == TokenKinds::RPAREN args << expression if @current_token.type == TokenKinds::COMMA eat(TokenKinds::COMMA) else break end end eat(TokenKinds::RPAREN) args end def eat(type) token = @current_token if token.type == type advance token else if token.nil? raise "Unexpected #{token.type} - expected #{type}" else raise "Unexpected #{token.type} - expected #{type}" end end end def advance @current_token = @lexer.get_token end def at_end @current_token.type == TokenKinds::EOF end end