#!/usr/bin/env swipl
% vim: ft=prolog textwidth=80 tabstop=4 softtabstop=4 shiftwidth=4 expandtab

%% :- use_module(library(pure_input)).
%%% :- use_module(library(dcg/basics)).

:- initialization main.
:- multifile prolog:message//1.

query_name([H|T]) -->
    [H], { code_type(H, csymf) },
    query_name_next(T).
query_name_next([H|T]) -->
    [H], { H = 0'.; code_type(H, csym) },
    !,
    query_name_next(T).
query_name_next([]) --> [].

query_default_text([H|T]) -->
    [H],
    {\+memberchk(H,`'<>`)},
    !,
    query_default_text(T).
query_default_text([]) --> [].

query_content([name(Name)|T]) --> query_name(Name), query_content(T).
query_content([expr(Expr)|T]) --> `'`, query_awk(Expr), `'`, query_content(T).
query_content([query(Query, Filters, Default)|T]) -->
    query_exp(Query, Filters, Default), query_content(T).
query_content([]) --> [].

query_default([string(String)|T]) --> awk_string(String), query_default(T).
query_default([expr(Expr)|T]) --> `'`, query_awk(Expr), `'`, query_default(T).
% query_default([query(Query)|T]) --> query_exp(Query), query_default(T).
query_default([query(Query, Filters, Default)|T]) -->
    query_exp(Query, Filters, Default), query_default(T).
query_default([]) --> [].

query_exp(Content, Filters, Default) -->
    `<`,
    query_content(Content),
    query_exp_filters(Filters),
    query_exp_default(Default),
    `>`.

query_exp_filters([H|T]) -->
    `|`, query_name(H), query_exp_filters(T).
query_exp_filters([]) --> [].

query_exp_default(none) --> [].
query_exp_default(Default) --> `:`, query_default(Default).

awk_string(String) --> `"`, awk_string_content(String), `"`.

awk_string_content([0'\\,H|T],R) -->
    [0'\\,H],
    !,
    awk_string_content(T,R).
awk_string_content([H|T],R) -->
    [H], { H\=0'\\, H\=0'" },
    !,
    awk_string_content(T,R).
awk_string_content(R,R) --> [].
awk_string_content(L) --> awk_string_content(L,[]).

awk_regex_content([0'\\,H|T],R) -->
    [0'\\,H],
    !,
    awk_regex_content(T,R).
awk_regex_content([H|T],R) -->
    [H], { H\=0'\\, H\=0'/ },
    !,
    awk_regex_content(T,R).
awk_regex_content(R,R) --> [].
awk_regex_content(L) --> awk_regex_content(L,[]).

awk_comment([0'\n|R],R) --> `\n`, !.
awk_comment([H|T],R) -->
    [H], {assertion(H \= 0'\n)},
    !,
    awk_comment(T,R).
% awk_comment(L) --> awk_comment(L,[]).

awk_code([0'"|T0],R,_,F) -->
    `"`, !, awk_string_content(T0,T1), `"`, { T1=[0'"|T2] }, awk_code(T2,R,expr,F).
awk_code([0'/|T0],R,Prev,F) -->
    { memberchk(Prev, [delim, op]) },
    `/`, !, awk_regex_content(T0,T1), `/`, { T1=[0'/|T2] }, awk_code(T2,R,expr,F).
awk_code([0'#|T],R,_,F) -->
    `#`, !, awk_comment(T,T1), awk_code(T1,R,op,F).
awk_code([H|T],R,Prev,F) -->
    [H], { code_type(H, white) }, !, awk_code(T,R,Prev,F).
awk_code([H|T],R,_,F) -->
    [H], { memberchk(H, `\n,;([{=`) }, !, awk_code(T,R,delim,F).
awk_code([H|T],R,_,F) -->
    [H], { memberchk(H, `!+-*/%^&|?:>~`) }, !, awk_code(T,R,op,F).
% awk_code([0'<|T],R,Prev,F) -->
%     `<`, !, { Prev \= delim }, awk_code(T,R,op,F).
awk_code([H|T],R,Prev,F) -->
    [H],
    { H \= 0'', H \= 0'@, (H \= 0'< ; Prev \= delim ) },
    !,
    awk_code(T,R,expr,F).
awk_code(R,R,L,L) --> [].
% awk_code(L) --> awk_code(L,[],delim,_).

query_awk([query(Query, Filters, Default)|T],delim) -->
    query_exp(Query, Filters, Default),
    query_awk(T, expr).
query_awk([code(Code)|T], Last) -->
    awk_code(Code, [], Last, Next),
    { Code \= [] },
    query_awk(T, Next).
query_awk([], _) --> [].
query_awk(L) --> query_awk(L,delim).

%%%

out_awk([code(Code)|T]) --> Code, out_awk(T).
out_awk([query(Query, Filters, Default)|T]) -->
    out_query(Query, Filters, Default), out_awk(T).
out_awk([]) --> [].

out_query(Query, Filters, none) -->
    out_filters(Filters, out_f_get(Query)).
out_query(Query, Filters, Default) -->
    `(find(`, out_query_content(Query), `)?`,
    out_filters(Filters, out_f_found),
    `:`, out_default(Default), `)`.

out_f_get(Query) -->
    `get(`, out_query_content(Query), `)`.

out_f_found --> `found`.

out_filters([H|T], Pred) -->
    H, `(`, out_filters(T, Pred), `)`.
out_filters([], Pred) --> call(Pred).

out_query_content([name(Name)|T]) --> `"`, Name, `"`, out_query_content(T).
out_query_content([expr(Expr)|T]) --> `(`, out_awk(Expr), `)`, out_query_content(T).
out_query_content([query(Query, Filters, Default)|T]) -->
    out_query(Query, Filters, Default), out_query_content(T).
out_query_content([]) --> [].

out_default([]) --> `""`.
out_default([H|T]) --> out_default_aux([H|T]).
out_default_aux([string(String)|T]) --> awk_string(String), out_default_aux(T).
out_default_aux([expr(Expr)|T]) --> `(`, out_awk(Expr), `)`, out_default_aux(T).
out_default_aux([query(Query, Filters, Default)|T]) -->
    out_query(Query, Filters, Default), out_default_aux(T).
out_default_aux([]) --> [].

%%%


parse_file(InFile) :-
    (  open(InFile, read, Fd, [encoding(utf8)]), read_stream_to_codes(Fd, Codes)
    -> true
    ;  throw(read_error(InFile))
    ),
    (  
       % phrase_from_file(query_awk(Awk), InFile)
       phrase(query_awk(Awk), Codes)
    -> true
    ;  throw(parsing_failed(InFile))
    ),
    (  phrase(out_awk(Awk), Out)
    -> true
    ;  throw(formatting_failed(InFile,Awk))
    ),
    current_stream(1, write, OutStream),
    set_stream(OutStream, encoding(utf8)),
    format('~s', [Out]).

main :- current_prolog_flag(argv, Argv), main(Argv).

main(Args) :-
    set_prolog_flag(verbose, false),
    (  (Args=[] -> InFile='/dev/stdin' ; Args=[InFile])
    -> catch(parse_file(InFile), E, (print_message(error, E), halt(1)))
    ;  write('usage: query.pl: [filename]\n'), halt(2)
    ),
    halt.

%    current_input(In), set_stream(In, tty(false)),
%    (phrase_from_stream(query_awk(Awk), In) -> true ; throw(parsing_failed)),
%    (phrase(out_awk(Awk), Out) -> true ; throw(formatting_failed(Awk))),
%    format('~s', [Out]).

prolog:message(read_error(File)) -->
    ['Unable to read file: ~w'-[File]].
prolog:message(parsing_failed(File)) -->
    ['Unable to parse AWK/query file: ~w'-[File]].
prolog:message(formatting_failed(File,_)) -->
    ['Failed generating AWK code from file: ~w'-[File]].