2012-02-08 2 views
2

Я работаю над разбиением исходного файла на токены, в частности, для проверки идентификаторов. Тем не менее, существует требование, чтобы идентификаторы были длиной до 30 символов. Когда идентификатор достигает этой длины, я вызываю исключение с сообщением: 'Identifiers can only be 30 characters long, truncating..'.Сохранять переменную после исключения.

Так оно и должно быть, но когда я поднимаю это исключение, я выхожу из своего метода, который сканирует идентификаторы, прежде чем я смогу его сохранить. Мне нужно как-то поднять исключение и сохранить идентификатор, который я собрал до сих пор. Любые идеи относительно того, как это можно сделать?

# classify each character, and call approriate scan methods 
def tokenize() 
    @infile.each_char do |c| 
    begin 
     case c 
     when /[a-zA-Z\$]/ 
     scan_identifier(c) 
     when /\s/ 
     #ignore spaces 
     else 
     #do nothing 
     end 
    rescue TokenizerError => te 
     puts "#{te.class}: #{te.message}" 
    end 
    end 
end 

# Reads an identifier from the source program 
def scan_identifier(id) 
    this_id = id #initialize this identifier with the character read above 

    @infile.each_char do |c| 
    if c =~ /[a-zA-Z0-9_]/ 
     this_id += c 
     # raising this exception leaves this function before collecting the 
     # truncated identifier 
     raise TokenizerError, 'Identifiers can only be 30 characters long, truncating..' if this_id.length == 30 
    else 
     puts "#{this_id}" 
     break # not part of the identifier, or an error 
    end 
    end 
end 
+1

Исключения должны использоваться только в «исключительных» случаях. Не пытайтесь создать поток программ с ними. Просто верните токен из своего метода. –

+0

Это не предназначалось для проблемы с потоком программ. Я должен поднять предупреждение тем, кто использует программу, что их идентификатор слишком длинный, и он усекается. Я решил, что исключение будет логичным способом сделать это. Что было бы хорошей альтернативой? –

+0

Позвольте мне превратить это в ответ. –

ответ

3

Это злоупотребление исключениями, ИМО, потому что это не исключительный случай. Вместо этого следует просто войти что-то:

if c =~ /[a-zA-Z0-9_]/ 
     warn "Identifer was too long and was truncated" 
     this_id += c 

Если вы должны использовать исключения для какой-либо причине, то самый простой способ это просто положить this_id в переменной экземпляра вместо:

@this_identifier = id 
# ... 

Тогда, когда вы ломаете спасение, просто получите последнее выражение: @this_identifier, чтобы вернуть это значение (yuck).


Комментарий к бонусу: это действительно убогий способ разобрать исходные файлы. Вы должны использовать что-то вроде RubyParser, если вы разбираете Ruby, или Treetop, если вы разбираете что-то еще.

+0

К сожалению, для проекта я создаю компилятор с нуля. Если бы у меня был выбор, я бы использовал lex и yacc, чтобы избежать этой части компиляции. Кроме того, что плохого в этом подходе? (кроме злоупотребления исключением) –

+0

Во-первых, ваш токенизатор может быть намного короче, поскольку его единственным эффектом является разбор идентификаторов и игнорирование всего остального. Просто разделите слова и предоставьте каждому из них более 30 символов: 'good, bad = @ infile.read.split.partition {| word | word.length <30} '. –

+0

Я абстрагировал остальную часть токенизатора, и на самом деле он правильно декодирует полный язык ввода. За исключением случая, о котором я рассказывал. Исходные файлы, которые я разбираю, могут содержать все, что может быть в программе PL/0. –

1

Исключения должны использоваться только в «исключительных» случаях. Не пытайтесь создать поток программ с ними. Просто верните токен из своего метода.

Что-то вдоль этих линий:

def tokenize() 
    @infile.each_char do |c| 
     begin 
     case c 
     when /[a-zA-Z\$]/ 
      scan_identifier(c) 
     when /\s/ 
      #ignore spaces 
     else 
      #do nothing 
     end 
     end 
    end 
    end 

    #Reads an identifier from the source program 
    def scan_identifier(id) 
    this_id = id #initialize this identifier with the character read above 

    @infile.each_char do |c| 
     if c =~ /[a-zA-Z0-9_]/ 
      this_id += c 
      if this_id.length == 30 
      puts 'Identifiers can only be 30 characters long, truncating..' 
      break 
      end 
     else 
      break #not part of the identifier, or an error 
     end 
    end 
    puts "#{this_id}" 
    end 

Когда вам нужно, чтобы предупредить пользователей о чем-то они делают это нормально случай использования и в целом ожидается, просто вывести строку на стандартный вывод или/и поток ошибок в случае консольное приложение.

Смежные вопросы