RSpec.describe Lexer do it 'lexes null' do expect(Lexer.new('null').scan_all).to eq( [Token.new(TokenKinds::NULL), Token.new(TokenKinds::EOF)] ) end it 'lexes booleans' do expect(Lexer.new('true false').scan_all).to eq( [ Token.new(TokenKinds::BOOLEAN, true), Token.new(TokenKinds::BOOLEAN, false), Token.new(TokenKinds::EOF) ] ) end it 'lexes numbers' do expect(Lexer.new('1 2.3').scan_all).to eq( [ Token.new(TokenKinds::NUMBER, 1.0), Token.new(TokenKinds::NUMBER, 2.3), Token.new(TokenKinds::EOF) ] ) end it 'lexes strings' do expect(Lexer.new('"hello world"').scan_all).to eq( [Token.new(TokenKinds::STRING, 'hello world'), Token.new(TokenKinds::EOF)] ) end it 'lexes operators' do expect(Lexer.new('+-*/<<=>>=== and or not').scan_all).to eq( [ Token.new(TokenKinds::OPERATOR, :+), Token.new(TokenKinds::OPERATOR, :-), Token.new(TokenKinds::OPERATOR, :*), Token.new(TokenKinds::OPERATOR, :/), Token.new(TokenKinds::OPERATOR, :<), Token.new(TokenKinds::OPERATOR, :<=), Token.new(TokenKinds::OPERATOR, :>), Token.new(TokenKinds::OPERATOR, :>=), Token.new(TokenKinds::OPERATOR, :==), Token.new(TokenKinds::OPERATOR, :and), Token.new(TokenKinds::OPERATOR, :or), Token.new(TokenKinds::OPERATOR, :not), Token.new(TokenKinds::EOF) ] ) end it 'lexes punctuation' do expect(Lexer.new('{}()[];,.').scan_all).to eq( [ Token.new(TokenKinds::LBRACE), Token.new(TokenKinds::RBRACE), Token.new(TokenKinds::LPAREN), Token.new(TokenKinds::RPAREN), Token.new(TokenKinds::LBRACKET), Token.new(TokenKinds::RBRACKET), Token.new(TokenKinds::SEMICOLON), Token.new(TokenKinds::COMMA), Token.new(TokenKinds::DOT), Token.new(TokenKinds::EOF) ] ) end it 'lexes if elseif else' do expect(Lexer.new('if elseif else').scan_all).to eq( [ Token.new(TokenKinds::IF), Token.new(TokenKinds::ELSEIF), Token.new(TokenKinds::ELSE), Token.new(TokenKinds::EOF) ] ) end it 'lexes =' do expect(Lexer.new('=').scan_all).to eq( [Token.new(TokenKinds::EQUALS), Token.new(TokenKinds::EOF)] ) end it 'lexes let' do expect(Lexer.new('let').scan_all).to eq( [Token.new(TokenKinds::LET), Token.new(TokenKinds::EOF)] ) end it 'lexes identifiers' do expect(Lexer.new('x').scan_all).to eq( [Token.new(TokenKinds::IDENTIFIER, 'x'), Token.new(TokenKinds::EOF)] ) end it 'lexes function' do expect(Lexer.new('function').scan_all).to eq( [Token.new(TokenKinds::FUNCTION), Token.new(TokenKinds::EOF)] ) end it 'lexes class' do expect(Lexer.new('class').scan_all).to eq( [Token.new(TokenKinds::CLASS), Token.new(TokenKinds::EOF)] ) end it 'lexes class names' do expect(Lexer.new('Class').scan_all).to eq( [Token.new(TokenKinds::CLASS_NAME, 'Class'), Token.new(TokenKinds::EOF)] ) end it 'lexes public and private' do expect(Lexer.new('public private').scan_all).to eq( [ Token.new(TokenKinds::PUBLIC), Token.new(TokenKinds::PRIVATE), Token.new(TokenKinds::EOF) ] ) end it 'lexes hash' do expect(Lexer.new('{ :a => 1 }').scan_all).to eq( [ Token.new(TokenKinds::LBRACE), Token.new(TokenKinds::ATOM, :a), Token.new(TokenKinds::ROCKET), Token.new(TokenKinds::NUMBER, 1.0), Token.new(TokenKinds::RBRACE), Token.new(TokenKinds::EOF) ] ) end it 'lexes for' do expect(Lexer.new('for in').scan_all).to eq( [ Token.new(TokenKinds::FOR), Token.new(TokenKinds::IN), Token.new(TokenKinds::EOF) ] ) end it 'allows escaping quotes in strings' do expect(Lexer.new('"hello \"world\""').scan_all).to eq( [ Token.new(TokenKinds::STRING, 'hello "world"'), Token.new(TokenKinds::EOF) ] ) end it 'supports newlines and tabs in strings' do expect(Lexer.new('"hello\nworld" "hello\tworld"').scan_all).to eq( [ Token.new(TokenKinds::STRING, "hello\nworld"), Token.new(TokenKinds::STRING, "hello\tworld"), Token.new(TokenKinds::EOF) ] ) end it 'ignores comments' do expect(Lexer.new("// prints 5\nprint(5)").scan_all).to eq( [ Token.new(TokenKinds::IDENTIFIER, 'print'), Token.new(TokenKinds::LPAREN), Token.new(TokenKinds::NUMBER, 5), Token.new(TokenKinds::RPAREN), Token.new(TokenKinds::EOF) ] ) end it 'lexes hashes' do expect(Lexer.new('#').scan_all).to eq( [Token.new(TokenKinds::HASH), Token.new(TokenKinds::EOF)] ) end it 'lexes method and classmethod' do expect(Lexer.new('method classmethod').scan_all).to eq( [ Token.new(TokenKinds::METHOD), Token.new(TokenKinds::CLASSMETHOD), Token.new(TokenKinds::EOF) ] ) end end