%
% This is the code from the appendix of
%
% Micha Meier, "Compilation of Compound Terms in Prolog"
%

  % Compile a head compound term
  head(Term) :-
      functor(Term, F, A),
      compile_args(Term, 1, A, 1, Code,
            [branch(lab(_)), label(LR)|ReadCode]),
      read_seq(Code, ReadCode, []),
      pwrite([get_structure(F/A, reg(i), lab(LR))|Code]).

  % Compile a compound subgoal argument
  body(Term) :-
      functor(Term, F, A),
      A1 is A + 1,
      compile_body([Term|Cont], Cont, A1, [_|Code], []),
      pwrite([put_structure(F/A, reg(i))|Code]).

  % Write sequence for the arguments of a compound term
  compile_args(Term, A, A, Reg) -->
      {arg(A, Term, Arg)},
      compile_arg(Arg, Reg, last).
  compile_args(Term, I, A, Reg) -->
      {I < A, arg(I, Term, Arg), I1 is I + 1},
      compile_arg(Arg, Reg, notlast),
      compile_args(Term, I1, A, Reg).

  % Generate the write sequence for one argument
  compile_arg(Struct, Reg, last) -->
      {compound(Struct), functor(Struct, F, A)},
       [label(_), write_structure(F/A)],
       compile_args(Struct, 1, A, Reg).
   compile_arg(Struct, Reg, notlast) -->
       {compound(Struct), functor(Struct, F, A), Reg1 is Reg+1},
       [write_down(reg(Reg)), label(_), write_structure(F/A)],
       compile_args(Struct, 1, A, Reg1),
       [write_up(reg(Reg)), write_test(lab(_))].
   compile_arg(Const, _, _) -->
       {atomic(Const)},
       [write_constant(Const)].

   % Generate the read sequence and fill in the labels
   read_seq([branch(lab(L))|_]) -->
       [label(L)].
   read_seq([write_down(R)|T]) -->
       [read_down(R)],
       read_seq(T).
   read_seq([label(L), write_structure(S)|T]) -->
       [read_test(lab(L)), read_structure(S)],
       read_seq(T).
   read_seq([write_up(R), write_test(lab(L))|T]) -->
       [read_up(R), label(L)],
       read_seq(T).
   read_seq([write_constant(C)|T]) -->
       [read_constant(C)],
       read_seq(T).

   % Compile a queue of body structures
   compile_body([], [], _) --> {true}.
   compile_body([Struct|Rest], Cont, Off) -->
       {functor(Struct, F, A), Off1 is Off - 1},
       [push_constant(F/A)],
       compile_struct(Struct, 1, A, Off1, NewOff, Cont, NewCont),
       compile_body(Rest, NewCont, NewOff).

   % Compile one body structure
   compile_struct(Struct, A, A, Off, NewOff, Cont, NewCont) -->
       {arg(A, Struct, Arg)},
       compile_body_arg(Arg, Off, NewOff, Cont, NewCont).
   compile_struct(Struct, I, A, Off, NewOff, Cont, NewCont) -->
       {I < A, arg(I, Struct, Arg), I1 is I + 1},
       compile_body_arg(Arg, Off, N0, Cont, NC),
       compile_struct(Struct, I1, A, N0, NewOff, NC, NewCont).

   % Compile one argument of a body structure
   compile_body_arg(Const, Off, NewOff, C, C) -->
       {atomic(Const), NewOff is Off - 1},
       [push_constant(Const)].
   compile_body_arg(Struct, Off, NewOff, [Struct|C], C) -->
       {compound(Struct), functor(Struct, _, A),
           NewOff is Off + A},
       [push_structure(Off)].

   % Print the generated code
   pwrite([]).
   pwrite([label(Lab)|Rest]) :-
       write(Lab),
       write(:),
       pwrite(Rest).
   pwrite([Instr|Rest]) :-
       put(9),
       functor(Instr, F, A),
       write(F),
       name(F, LS),
       length(LS, Length),
       tab(20-Length),
       writeargs(Instr, 1, A),
       nl,
       pwrite(Rest).

   writeargs(Instr, A, A) :-
       arg(A, Instr, Arg),
       writearg(Arg).
   writeargs(Instr, I, A) :-
       I < A,
       arg(I, Instr, Arg),
       writearg(Arg),
       write(', '),
       I1 is I + 1,
       writeargs(Instr, I1, A).

   writearg(lab(L)) :-
       write(L).
   writearg(reg(R)) :-
       write('X'),
       write(R).
   writearg(Arg) :-
       write(Arg).

   tab(Expr) :-
   	N is Expr,
	printf("%*c", [N,0' ]).
