defmodule Codegen do def trace(ip, cmd, s) do ip = to_string(ip) |> String.pad_leading(4, " ") s = inspect(s) |> String.slice(0, 20) |> String.pad_trailing(20, " ") IO.puts(" #{ip} | #{s} | #{cmd} ") end defp emit_inst(ip, inst, _options) do case inst do {:nop} -> quote do {ctx, s, si, unquote(ip + 1)} end {:any, n} -> quote do if Enum.count(s) >= unquote(n) do {ctx, Enum.drop(s, unquote(n)), si + unquote(n), unquote(ip + 1)} else {ctx, s, si, :fail} end end {:chr, c} -> quote do case s do [unquote(c) | s] -> {ctx, s, si + 1, unquote(ip + 1)} _ -> {ctx, s, si, :fail} end end {:set, cs} -> quote do case s do [c | s] when c in unquote(cs) -> {ctx, s, si + 1, unquote(ip + 1)} _ -> {ctx, s, si, :fail} end end {:span, cs} -> quote do {s1, s2} = Enum.split_while(s, fn c -> c in unquote(cs) end) {ctx, s2, si + Enum.count(s1), unquote(ip) + 1} end {:return} -> quote do case Xpeg.state(ctx, :ret_stack) do [ip | ret_stack] -> ctx = Xpeg.state(ctx, ret_stack: ret_stack) {ctx, s, si, ip} [] -> ctx = Xpeg.state(ctx, status: :ok) {ctx, s, si, ip} end end {:choice, off_back, off_commit} -> quote do frame = %{ ip_back: ip + unquote(off_back), ip_commit: ip + unquote(off_commit), ret_stack: Xpeg.state(ctx, :ret_stack), cap_stack: Xpeg.state(ctx, :cap_stack), s: s, si: si } back_stack = Xpeg.state(ctx, :back_stack) ctx = Xpeg.state(ctx, back_stack: [frame | back_stack]) {ctx, s, si, unquote(ip + 1)} end {:commit} -> quote do [frame | back_stack] = Xpeg.state(ctx, :back_stack) ctx = Xpeg.state(ctx, back_stack: back_stack) {ctx, s, si, frame.ip_commit} end {:call, addr} -> quote do ret_stack = Xpeg.state(ctx, :ret_stack) ctx = Xpeg.state(ctx, ret_stack: [ip+1 | ret_stack]) {ctx, s, si, unquote(addr)} end {:jump, addr} -> quote do {ctx, s, si, unquote(addr)} end {:capopen} -> quote do cap_stack = Xpeg.state(ctx, :cap_stack) ctx = Xpeg.state(ctx, cap_stack: [{:open, s, si} | cap_stack]) {ctx, s, si, unquote(ip + 1)} end {:capclose} -> quote do cap_stack = Xpeg.state(ctx, :cap_stack) ctx = Xpeg.state(ctx, cap_stack: [{:close, s, si} | cap_stack]) {ctx, s, si, unquote(ip + 1)} end {:code, code} -> quote do ctx = Xpeg.collect_captures(ctx) func = unquote(code) {captures, data} = case {Xpeg.state(ctx, :captures), Xpeg.state(ctx, :userdata)} do {captures, :nodata} -> {func.(captures), :nodata} {captures, data} -> func.(captures, data) end ctx = Xpeg.state(ctx, captures: captures, userdata: data) {ctx, s, si, unquote(ip + 1)} end {:fail} -> quote do case Xpeg.state(ctx, :back_stack) do [frame | back_stack] -> ctx = Xpeg.state(ctx, back_stack: back_stack, ret_stack: frame.ret_stack, cap_stack: frame.cap_stack ) {ctx, frame.s, frame.si, frame.ip_back} [] -> ctx = Xpeg.state(ctx, status: :error) {ctx, s, si, 0} end end end end def emit(program, options \\ []) do cases = program.instructions |> Enum.map(fn {ip, inst} -> body = emit_inst(ip, inst, options) body = if options[:trace] do trace = quote do: Xpeg.trace(unquote(ip), unquote(inspect(inst)), s) {:__block__, [], [trace, body]} else body end {:->, [], [[ip], body]} end) f = quote do fn ctx, s, si, ip -> {ctx, s, si, ip} = case ip do unquote(cases) end func = Xpeg.state(ctx, :func) case Xpeg.state(ctx, :status) do :running -> func.(ctx, s, si, ip) _ -> {ctx, si} end end end if options[:debug] do IO.puts(Macro.to_string(f)) end f end end # set ft=elixir