2010-07-29 15 views
1

Je suis en train de faire un analyseur récursif-descente en Ruby pour une grammaire, qui est définie par les règles suivantesParser en Ruby: traiter des commentaires collants et citations

  1. entrée se compose de blanc- espace séparé Cartes commençant par un arrêt mot, où white-space est regex /[ \n\t]+/
  2. carte peut consister en Mots-clés et/ou valeurs également séparés par des espaces blancs, qui ont ordre spécifique de carte/motif
  3. Tous les stop-mots et mots-clés sont insensibles à la casse, à savoir: /^[a-z]+[a-z0-9]*$/i
  4. La valeur peut être chaîne double cité, qui peut être séparé de ne pas d'autres termes par un espace blanc, par exemple:

    word"quoted string"word 
    
  5. la valeur peut aussi être un mot/^[a-z]+[a-z0-9]*$/, ou entier, ou flotteur (par ex. -1.15 ou 1.0e+2)

  6. commentaire sur une ligne est désignée par # et peut ne pas être séparé de d'autres termes, par exemple:

    word#single-line comment\n 
    
  7. commentaire multi-ligne est désignée par /* et */ et peut ne pas être séparé des autres mots, p.ex.:

    word/*multi-line 
    comment*/word 
    

# Input example. Stop-words are chosen just to highlight them: set, object 
set title"Input example"set objects 2#not-separated by white-space. test: "/* 
set test "#/*" 
object 1 shape box/* shape is a Keyword, 
box is a Value. test: "#*/object 2 shape sphere 
set data # message and complete are Values 
0 0 0 0 1 18 18 18 1 35 35 35 72 35 35 # all numbers are Values of the Card "set" 

Puisque la plupart des mots sont séparés par des espaces blancs, pendant un certain temps, je pensais à diviser l'entrée entier et l'analyse syntaxique mot par mot. Pour faire face à des commentaires et des citations, j'allais faire

words = input_text.gsub(/([\"\#\n]|\/\*|\*\/)/, ' \1 ').split(/[ \t]+/) 

Cependant, de cette manière le contenu des chaînes (et commentaires, si je veux les garder) est modifié. Comment traiteriez-vous ces commentaires et citations?

+0

En relation avec http://stackoverflow.com/questions/3343726/parser-in-ruby-slice-inside-each-with-index-missing-element – Andrei

+0

Je ne crois pas que le découpage de texte sur les espaces blancs soit un bonne idée pour analyser tout sauf les grammaires les plus simples. Je ne veux pas écrire un essai sur la création de parseurs ici ... Quoi qu'il en soit, google pour "compilateur compilateur ruby", "parser génération ruby" ... Ceci est un exemple http://treetop.rubyforge.org/ – skalee

+0

Bien , la cime des arbres est un peu difficile à comprendre. Peut-être pouvez-vous me montrer comment je peux l'appliquer à ma grammaire? Je pensais que pour une grammaire aussi facile je pourrais faire quelque chose moi-même avec l'aide aimable des utilisateurs SO. – Andrei

Répondre

0

OK, je l'ai fait moi-même. On peut réduire au minimum le code suivant si sa lisibilité est pas nécessaire

class WordParser 
    attr_reader :words 

    def initialize text 
    @text = text 
    end 

    def parse 
    reset_parser 
    until eof? 
     case curr_char 
     when '"' then 
      start_word and add_chars_until? '"' 
      close_word 
     when '#','%' then 
      start_word and add_chars_until? "\n" 
      close_word 
     when '/' then 
      if next_is? '*' then 
      start_word and 2.times { add_char } 
      add_char until curr_is? '*' and next_is? '/' or eof? 
      2.times { add_char } unless eof? 
      close_word 
      else 
      # parser_error "unexpected symbol '/'" # if not allowed in the grammar 
      start_word unless word_already_started? 
      add_char 
      end 
     when /[^\s]/ then 
      start_word unless word_already_started? 
      add_char 
     else # skip whitespaces etc. between words 
     move and close_word 
     end 
    end 
    return @words 
    end 

private 

    def reset_parser 
    @position = 0 
    @line, @column = 1, 1 
    @words = [] 
    @word_started = false 
    end 

    def parser_error s 
    Kernel.puts 'Parser error on line %d, col %d: ' + s 
    raise 'Parser error' 
    end 

    def word_already_started? 
    @word_started 
    end 

    def close_word 
    @word_started = false 
    end 

    def add_chars_until? ch 
    add_char until next_is? ch or eof? 
    2.times { add_char } unless eof? 
    end 

    def add_char 
    @words.last[:to] = @position 
    # @words.last[:length] += 1 
    # @word.last += curr_char # if one just collects words 
    move 
    end 

    def start_word 
    @words.push from: @position, to: @position, line: @line, column: @column 
    # @words.push '' unless @words.last.empty? # if one just collects words 
    @word_started = true 
    end 

    def move 
    increase :@position 
    return if eof? 
    if prev_is? "\n" 
     increase :@line 
     reset :@column 
    else 
     increase :@column 
    end 
    end 

    def reset var; instance_variable_set(var, 1) end 
    def increase var; instance_variable_set(var, instance_variable_get(var)+1) end 

    def eof?; @position >= @text.length end 

    def prev_is? ch; prev_char == ch end 
    def curr_is? ch; curr_char == ch end 
    def next_is? ch; next_char == ch end 

    def prev_char; @text[ @position-1 ] end 
    def curr_char; @text[ @position ] end 
    def next_char; @text[ @position+1 ] end 
end 

test en utilisant l'exemple que j'ai dans ma question

words = WordParser.new(text).parse 
p words.collect { |w| text[ w[:from]..w[:to] ] } .to_a 

# >> ["# Input example. Stop-words are chosen just to highlight them: set, object\n", 
# >> "set", "title", "\"Input example\"", "set", "objects", "2", 
# >> "#not-separated by white-space. test: \"/*\n", "set", "test", "\"#/*\"", 
# >> "object", "1", "shape", "box", "/* shape is a Keyword, \nbox is a Value. test: \"#*/", 
# >> "object", "2", "shape", "sphere", "set", "data", "# message and complete are Values\n", 
# >> "0", "0", "0", "0", "1", "18", "18", "18", "1", "35", "35", "35", "72", 
# >> "35", "35", "# all numbers are Values of the Card \"set\"\n"] 

Alors maintenant, je peux utiliser something like this pour analyser davantage les mots.