coucou747

Ce blog présente principalement les évolutions du compilateur metalang : les nouveaux backends, les nouvelles corrections de bugs, les nouvelles features, nouvaux tests, son utilisation dans le cadre du concours prologin.

le 27/11/2012

Brainfuck metacompiler

Dans la série des compilateurs jouets, j'ai voulu écrire un compilateur brainfuck en métalang. L'idée était plutot curieuse : générer par méta-programmation du code métalang en lisant une source écrite en brainfuck.

On suppose la mémoire contenue dans la variable "mem", la position courrante : current_pos, et la variable temporaire qui sert aux entrées : input. On peut alors écrire les différentes instructions brainfucks sous formes de fonctions métalang

def lexems read_current()
  return {
    read char input
    mem[current_pos] = int_of_char(input)
  }
end

def lexems print_current()
  return {
    print char char_of_int(mem[current_pos])
  }
end

def lexems incr()
  return {
    mem[current_pos] = mem[current_pos] + 1
  }
end

def lexems decr()
  return {
    mem[current_pos] = mem[current_pos] - 1
  }
end

def lexems prev()
  return { current_pos = current_pos - 1}
end

def lexems next()
  return { current_pos = current_pos + 1}
end

Ensuite, on définit une macro qui lit l'entrée standard et la convertie en AST métalang.


def lexems eval()
  def read char c
  if c == '.' then
    return { ${print_current()} ${eval()} }
  elsif c == ',' then
    return { ${read_current()} ${eval()} }
  elsif c == '>' then
    return { ${next()} ${eval()} }
  elsif c == '<' then
    return { ${prev()} ${eval()} }
  elsif c == '+' then
    return { ${incr()} ${eval()} }
  elsif c == '-' then
    return { ${decr()} ${eval()} }
  elsif c == '[' then
    return { while (mem[current_pos] != 0) do ${eval()} }
  elsif c == ']' then
    return { end ${eval()} }
  elsif c == ' ' || c == '\n' || c == '\r' then
    return eval()
  else
    return {}
  end
end

Il nous reste juste le code du main à écrire.

main
  def char input = ' '
  def current_pos = 500
  def array<int> mem[1000] with i do
    return 0
  end
  ${ eval() }
end

On obtient ainsi un compilateur brainfuck écrit en métalang. Pour obtenir le code C correspondant au code brainfuck, il suffit d'écrire : ./metalang brainfuck.metalang < brainfuck.source. Ce compilateur agit en fait comme une sorte de front-end à métalang.

Il s'agit d'avantage de l'écriture d'un frontend pour métalang sous forme de plugin que de l'écriture d'un compilateur.

Le code source du compilateur brainfuck

Dans Catégories/Metalang/Démonstration.

Sujets : #metalang #brainfuck #macro