Après avoir écrit un tokenizer, avoir produit les unités lexicales et avoir défini une grammaire pour le langage Naam, je passe maintenant à la vérification de la syntaxe.

La vérification de la syntaxe se passe dans la classe Compiler. - C’est pas le meilleur choix de nom et ça changera par la suite -. Basiquement, cette classe ne fait que suivre la logique de la grammaire.

class Compiler

  # units - Array of LexicalUnits
  def compile units
    @units = units
    program
  end

  private

  def program
    while @units.size > 0
      case @units.first.type
      when :eol then accept(:eol)
      else
        instruction
      end
    end
  end

  def instruction
    case @units.first.type
    when :keyword then print_statement
    when :word then function_def
    else
      raise Error
    end
  end

  def print_statement
    accept(:keyword, 'print')
    accept_series(:word, :paro, :int, :parc, :eol)
  end

  def function_def
    accept_series(:word, :paro, :word, :parc, :affect, :eol)
    if_clause while if_clause?
    else_clause
  end

  def if_clause
    accept(:int)
    accept(:keyword, 'if')
    test
    accept(:eol)
  end

  def else_clause
    accept(:int)
    accept(:keyword, 'else')
    accept(:eol)
  end

  def test
    accept_series(:word, :op, :int)
  end

  def accept(type, value = '')
    unit = @units.slice!(0)
    raise Error unless unit.type == type
    if value != ''
      raise Error unless unit.value == value
    end
  end

  def accept_series(*args)
    args.each {|arg| accept(arg) }
  end

  def if_clause?
    @units[1].type == :keyword && @units[1].value == 'if'
  end
end

Quelques commentaires sur ce code:

def instruction
  # ...
  else
    raise Error
  end
end

Plusieurs fois j’utilise la classe Error, qui n’existe pas. C’est parce que je ne veux pas encore réfléchir à la gestion des erreurs. Les seuls cas qui m’intéressent pour l’instant sont ceux où ça fonctionne.

def accept(type, value = '')
  unit = @units.slice!(0)
  raise Error unless unit.type == type
  if value != ''
    raise Error unless unit.value == value
  end
end

C’est la méthode accept, toute simple, qui effectue la vérification de la syntaxe en comparant l’unité lexicale attendue avec celle réellement disponible. On constate que les unités lexicales (représentées par @units) sont détruites au fur et à mesure de leur consommation.

La prochaine, il sera enfin temps d’émettre le code PIR.

À demain.