Написание iterpreter на Ruby, не в состоянии правильно использовать пустое пространство - PullRequest
0 голосов
/ 04 ноября 2019

Итак, я следую «Писанию книги переводчика» и внедряю ее в 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 символа, чтобы точно знать, что это \nreturn и ILLEGAL)

Этого не должно было случиться, так как потребление_пробела должно пропускать все виды пробелов, но это все равно не было для строк новой строки после этого мымогут сканировать следующую строку, которая является ten = 10, но последняя точка с запятой нигде не отображается в выводе, как и первая

Если я использую входную строку без какого-либо идентификатора или числа, она отлично работает.

Вот ссылка на полную кодовую базу https://gitlab.com/palash25/gundoochy

1 Ответ

2 голосов
/ 04 ноября 2019

В вашем коде Lexer (который вы должны включить в исходный вопрос) у вас есть следующий метод:

def consume_whitespace
  while @ch == ' ' || @ch =='\t' || @ch == '\n' || @ch == '\r' do
    read_char
  end
end

Здесь вы пытаетесь указать различные символы пробела. Однако, поскольку вы назвали их одинарными кавычками, escape-последовательности с обратной косой чертой не применяются. Вместо этого вы используете обратную косую черту, за которой следует символ t, n или r.

Если вы используете здесь двойные кавычки, символы в вашем исходном коде интерпретируются как tab, newline,или символы возврата каретки соответственно:

def consume_whitespace
  while @ch == ' ' || @ch == "\t" || @ch == "\n" || @ch == "\r" do
    read_char
  end
end
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...