#!/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]].