defmodule Linker do defp link_one(program, rules, name) do instructions = rules[name] program = %{ program | symtab: Map.put(program.symtab, name, Enum.count(program.instructions)), instructions: program.instructions ++ instructions ++ [{:return}] } #|> IO.inspect Enum.reduce(instructions, program, fn inst, program -> case inst do {:call, callname} -> if !Map.has_key?(rules, callname) do raise("XPeg: rule '#{name}' is referencing undefined rule '#{callname}'") end if !Map.has_key?(program.symtab, callname) do link_one(program, rules, callname) else program end _ -> program end end) end def link_grammar(grammar, options) do if options[:dump_ir] do IO.inspect(grammar) end program = %{ instructions: [], symtab: %{} } program = link_one(program, grammar.rules, grammar.start) is = program.instructions #|> IO.inspect(limit: :infinity) |> peephole() |> resolve_addresses(program) is = is ++ [{:fail, {:fail}}] program = %{program | instructions: is} program end def peephole(insts) do case insts do # tail call optimization [{:call, name}, {:return} | rest] -> [{:jump, name}, {:nop} | peephole(rest)] [a | rest] -> [a | peephole(rest)] e -> e end end defp resolve_addresses(insts, program) do Enum.with_index(insts, fn inst, ip -> case inst do {op, name} when op in [:call, :jump] -> {ip, {op, program.symtab[name]}} inst -> {ip, inst} end end) end end # set ft=elixir