defmodule Xpeg.Codegen do @moduledoc false defp emit_inst(ip, inst, options) do case inst do {:nop} -> quote location: :keep do def parse(unquote(ip), s, si, ctx, back_stack, ret_stack, cap_stack, captures) do parse(unquote(ip+1), s, si, ctx, back_stack, ret_stack, cap_stack, captures) end end {:any, n} -> quote location: :keep do def parse(unquote(ip), s, si, ctx, back_stack, ret_stack, cap_stack, captures) do parse(unquote(ip), s, si, ctx, back_stack, ret_stack, cap_stack, captures, unquote(n)) end def parse(unquote(ip), s, si, ctx, back_stack, ret_stack, cap_stack, captures, n) do case {s, n} do {[_|s2], 1} -> parse(unquote(ip+1), s2, si+1, ctx, back_stack, ret_stack, cap_stack, captures) {[_|s2], m} -> parse(unquote(ip), s2, si+1, ctx, back_stack, ret_stack, cap_stack, captures, m-1) {[], _} -> parse(:fail, [], si, ctx, back_stack, ret_stack, cap_stack, captures) end end end {:chr, cmatch} -> quote location: :keep do def parse(unquote(ip), s=[c|s2], si, ctx, back_stack, ret_stack, cap_stack, captures) when c == unquote(cmatch) do parse(unquote(ip+1), s2, si+1, ctx, back_stack, ret_stack, cap_stack, captures) end def parse(unquote(ip), s, si, ctx, back_stack, ret_stack, cap_stack, captures) do parse(:fail, s, si, ctx, back_stack, ret_stack, cap_stack, captures) end end {:set, cs} -> quote location: :keep do def parse(unquote(ip), s=[c|s2], si, ctx, back_stack, ret_stack, cap_stack, captures) when c in unquote(cs) do parse(unquote(ip+1), s2, si+1, ctx, back_stack, ret_stack, cap_stack, captures) end def parse(unquote(ip), s, si, ctx, back_stack, ret_stack, cap_stack, captures) do parse(:fail, s, si, ctx, back_stack, ret_stack, cap_stack, captures) end end {:span, cs} -> quote location: :keep do def parse(unquote(ip), s=[c|s2], si, ctx, back_stack, ret_stack, cap_stack, captures) when c in unquote(cs) do parse(unquote(ip), s2, si+1, ctx, back_stack, ret_stack, cap_stack, captures) end def parse(unquote(ip), s, si, ctx, back_stack, ret_stack, cap_stack, captures) do parse(unquote(ip+1), s, si, ctx, back_stack, ret_stack, cap_stack, captures) end end {:return} -> quote location: :keep do def parse(unquote(ip), s, si, ctx, back_stack, ret_stack, cap_stack, captures) do case ret_stack do [ip | ret_stack] -> parse(ip, s, si, ctx, back_stack, ret_stack, cap_stack, captures) [] -> {ctx, s, si, :ok, cap_stack, captures} end end end {:choice, ip_back, ip_commit} -> quote location: :keep do def parse(unquote(ip), s, si, ctx, back_stack, ret_stack, cap_stack, captures) do frame = { unquote(ip_back), unquote(ip_commit), ret_stack, cap_stack, s, si } back_stack = [frame | back_stack] parse(unquote(ip+1), s, si, ctx, back_stack, ret_stack, cap_stack, captures) end end {:commit} -> quote location: :keep do def parse(unquote(ip), s, si, ctx, back_stack, ret_stack, cap_stack, captures) do [frame | back_stack] = back_stack { _, ip, _, _, _, _ } = frame parse(ip, s, si, ctx, back_stack, ret_stack, cap_stack, captures) end end {:call, addr} -> quote location: :keep do def parse(unquote(ip), s, si, ctx, back_stack, ret_stack, cap_stack, captures) do ret_stack = [unquote(ip+1) | ret_stack] parse(unquote(addr), s, si, ctx, back_stack, ret_stack, cap_stack, captures) end end {:jump, addr} -> quote location: :keep do def parse(unquote(ip), s, si, ctx, back_stack, ret_stack, cap_stack, captures) do parse(unquote(addr), s, si, ctx, back_stack, ret_stack, cap_stack, captures) end end {:capopen} -> quote location: :keep do def parse(unquote(ip), s, si, ctx, back_stack, ret_stack, cap_stack, captures) do cap_stack = [{:open, s, si} | cap_stack] parse(unquote(ip+1), s, si, ctx, back_stack, ret_stack, cap_stack, captures) end end {:capclose, type} -> quote location: :keep do def parse(unquote(ip), s, si, ctx, back_stack, ret_stack, cap_stack, captures) do cap_stack = [{:close, s, si, unquote(type)} | cap_stack] parse(unquote(ip+1), s, si, ctx, back_stack, ret_stack, cap_stack, captures) end end {:code, code} -> quote location: :keep do def parse(unquote(ip), s, si, ctx, back_stack, ret_stack, cap_stack, captures) do {cap_stack, captures} = Xpeg.collect_captures(cap_stack, captures) func = unquote(code) {captures, ctx} = case unquote(options[:userdata]) do true -> func.(captures, ctx) _ -> {func.(captures), ctx} end parse(unquote(ip+1), s, si, ctx, back_stack, ret_stack, cap_stack, captures) end end {:fail} -> quote location: :keep do def parse(unquote(ip), s, si, ctx, back_stack, ret_stack, cap_stack, captures) do case back_stack do [frame | back_stack] -> { ip, _, ret_stack, cap_stack, s, si } = frame parse(ip, s, si, ctx, back_stack, ret_stack, cap_stack, captures) [] -> {ctx, s, si, :error, cap_stack, captures} end end end end end def add_trace(options, ast, ip, inst) do if options[:trace] do {ast, _} = Macro.prewalk(ast, false, fn {:do, body}, false -> body = quote do Xpeg.trace(unquote(ip), unquote(inspect(inst)), s) unquote(body) end {{:do, body}, true} e, done -> {e, done} end) ast else ast end end def emit(program, options \\ []) do ast = Enum.reduce(program.instructions, [], fn {ip, inst}, defs -> ast = emit_inst(ip, inst, options) case ast do {:__block__, _, subs} -> Enum.map(subs, &add_trace(options, &1, ip, inst)) ++ defs _ -> [add_trace(options, ast, ip, inst) | defs] end end) ast = { :__block__, [], [quote do require Xpeg end ] ++ ast } if options[:dump_code] do IO.puts(Macro.to_string(ast)) end Macro.escape(ast) end end # set ft=elixir