Dans le dernier épisode, j’utilisais un AST pour capturer la grammaire de Naam, et le code n’était pas très propre. Cette fois je nettoie un peu tout ça en mettant les règles de grammaire dans leur propres classes.

De ce fait, le syntaxer a beaucoup maigri puisqu’il se contente maintenant de lancer la première règle:

module Naam

  # Public: Here we transform a list of lexical units in an AST.
  class Syntaxer

    def initialize
      @ast = AST.new "ast"
    end

    # Public: Compile lexical units from a Naam program in an AST.
    #
    # units - Array of LexicalUnits
    #
    # Returns the AST.
    def run units
      ProgramRule.new(units, @ast).apply!
      @ast
    end

  end
end

Voici la règle de base:

module Naam
  class BaseRule

    def initialize(units, ast_node)
      @units = units
      @ast_node = ast_node
      @series = []
    end

    def apply!
      raise NotImplementedError
    end

    private

    def accept(type, value = '')
      unit = @units.slice!(0)
      @series << unit
      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

  end
end

Reste à écrire une classe par règle de grammaire. Voici par exemple la règle pour la else clause:

module Naam
  class ElseClauseRule < BaseRule
    def apply!
      accept(:int)
      accept(:keyword, 'else')
      accept(:eol)
      else_node = ElseClauseAST.new
      else_node.add_child(ReturnValueAST.new(@series[0].value))
      @ast_node.add_child(else_node)
    end
  end
end

La prochaine étape sera de sortir le code PIR à partir de l’AST.

À demain.