Итак, я следую «Писанию книги переводчика» и внедряю ее в Ruby вместо Go. Я могу сканировать токены, такие как ;
=
, +
и т. Д., Но, похоже, они ведут себя по-разному, когда в моей входной строке есть идентификаторы, такие как let
, 10
и т. Д. Пытался охотиться на эту ошибку всю эту неделю, но тщетно, поэтому я подумал, что свежая пара глаз сможет ее поймать.
Вот обзор.
База кода очень мала, и большая часть логики находится в lib/lexer/lexer.rb
Класс Lexer
поддерживает следующее состояние курсорадля текущего символа во входной строке, курсор для следующего символа и текущий символ во входной строке
Lexer
имеет следующие методы
read_char
, который устанавливает элементы данных в соответствующие значения
read_indentifier
, который используется для извлечения всех символов, принадлежащих строкам, которые не являются зарезервированными ключевыми словами, но идентификаторами и вызовомread_char
перед возвратом
read_number
аналогично read_identifier
, но для чисел
consume_whitespace
для пропуска пробелов, переносов,etc
next_token
используется для сопоставления текущего символа с соответствующим регистром и возврата его Token
объекта, определенного в lib/token/token.rb
, и вызова read_char
для увеличения курсоров перед возвратом
require_relative '../token/token'
def is_letter(ch) #basically decides syntax acceptable for variable names
#puts ch.class
'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_'
end
def is_digit(ch) #checks if digit
'0' <= ch && ch <= '9'
end
class Lexer
def initialize(input)
@input = input
@position = 0
@readPosition = 0
@ch =''
read_char
end
def read_char
#puts caller[0]
@ch = @readPosition >= @input.length ? '' : @input[@readPosition]
@position = @readPosition
@readPosition += 1
#puts "INSIDE READ_CHAR #{@position} #{@readPosition} #{@ch}"
end
# SUPPOSED TO BE A LOOP WAS JUST A CONDITION. NOW FIXED.
def consume_whitespace
while @ch == ' ' || @ch =='\t' || @ch == '\n' || @ch == '\r' do
read_char
end
end
def read_identifier
pos = @position
#puts "RI: char #{@ch} pos #{pos} position #{@position}"
while is_letter(@ch) do
#puts @ch
read_char
end
puts "METHOD read_identifier: char #{@ch} pos #{pos} position #{@position}\n"
@input[pos..@position-1]
end
def read_number
pos = @position
#puts "RN: char #{@ch} pos #{pos} position #{@position}"
while is_digit(@ch) do
read_char
end
puts "METHOD read_number: char #{@ch} pos #{pos} position #{@position}\n"
@input[pos..@position-1]
end
def next_token
#puts @ch, @ch.class
#puts "\nX=X=X=X=X=X=X=X=X=: #{@ch}, #{@ch.ord}, X=X=X=X=X=X=X=X=X=\n"
tok = nil
consume_whitespace
tok =
case @ch
when '=' then Token.new(ASSIGN, @ch)
when '+' then Token.new(PLUS, @ch)
when '-' then Token.new(MINUS, @ch)
when '/' then Token.new(DIVIDE, @ch)
when '*' then Token.new(MULTIPLY, @ch)
when '%' then Token.new(MODULO, @ch)
#when '==' then Token.new(EQUAL_TO, @ch)
when '>' then Token.new(GREATER_THAN, @ch)
when '<' then Token.new(LESS_THAN, @ch)
#when '!=' then Token.new(UNEQUAL_TO, @ch)
#when '&&' then Token.new(AND, @ch)
#when '||' then Token.new(OR, @ch)
when '!' then Token.new(NOT, @ch)
when ',' then Token.new(COMMA, @ch)
when ';' then Token.new(SEMICOLON, @ch)
when '?' then Token.new(QUESTION, @ch)
when '(' then Token.new(LPAREN, @ch)
when ')' then Token.new(RPAREN, @ch)
when '[' then Token.new(LSQUARE, @ch)
when ']' then Token.new(RSQUARE, @ch)
when '{' then Token.new(LCURLY, @ch)
when '}' then Token.new(RCURLY, @ch)
else
#puts 'hello from next_token', @ch.ord
# STATE WAS BEING MUTATED NOW FIXED
puts "letter #{@ch}"
puts "letter ascii #{@ch.ord}"
#puts "isletter "
if is_letter(@ch)
literal = read_identifier
Token.new(look_up_ident(literal), literal)
elsif is_digit(@ch)
Token.new(INT, read_number)
else
Token.new(ILLEGAL, "ILLEGAL")
end
end
read_char
return tok
end
end
Теперь тест рейкаСбои не помогли в отладке, поэтому я решил сделать это, просто написав main.rb
скрипт, который импортировал бы и запустил мой лексер, и разбросал много puts
s по всей базе кода
Этоmy main.rb
require_relative 'lib/lexer/lexer'
lex = Lexer.new('five = 5;
ten = 10;')
i = 1
while i <= 8
tok = lex.next_token
puts "\nIN_MAIN: #{tok.type} ==> #{tok.literal}\n\n"
i=i+1
end
Это вывод ruby main.rb
letter f
letter ascii 102
METHOD read_identifier: char pos 0 position 4
IN_MAIN: IDENTIFIER ==> five
IN_MAIN: = ==> =
letter 5
letter ascii 53
METHOD read_number: char ; pos 7 position 8
IN_MAIN: INT ==> 5
letter
letter ascii 10
IN_MAIN: ILLEGAL ==> ILLEGAL
letter t
letter ascii 116
METHOD read_identifier: char pos 27 position 30
IN_MAIN: IDENTIFIER ==> ten
IN_MAIN: = ==> =
letter 1
letter ascii 49
METHOD read_number: char ; pos 33 position 35
IN_MAIN: INT ==> 10
letter
Traceback (most recent call last):
2: from main.rb:8:in `<main>'
1: from /home/palash25/gundoochy/lib/lexer/lexer.rb:89:in `next_token'
/home/palash25/gundoochy/lib/lexer/lexer.rb:89:in `ord': empty string (ArgumentError)
Мы можем игнорировать последнюю строку, потому что я не могу обработать, как вернуть объект для EOF
прямо сейчас, но вот суть того, что происходит до этого
Лексер может правильно сканировать токены до five = 5
, после чего он пропускает следующий непосредственный символ, который был ;
и делаетдля этого возвращает объект токена и вместо этого возвращает объект токена типа ILLEGAL
для \n
, который идет сразу после ;
(я даже распечатал значения ascii символа, чтобы точно знать, что это \n
return и ILLEGAL)
Этого не должно было случиться, так как потребление_пробела должно пропускать все виды пробелов, но это все равно не было для строк новой строки после этого мымогут сканировать следующую строку, которая является ten = 10
, но последняя точка с запятой нигде не отображается в выводе, как и первая
Если я использую входную строку без какого-либо идентификатора или числа, она отлично работает.
Вот ссылка на полную кодовую базу https://gitlab.com/palash25/gundoochy