1/*
    2 * Part of pljulia
    3 * Copyright Samer Abdallah 2017
    4 *
    5 * This program is free software; you can redistribute it and/or
    6 * modify it under the terms of the GNU General Public License
    7 * as published by the Free Software Foundation; either version 2
    8 * of the License, or (at your option) any later version.
    9 *
   10 * This program is distributed in the hope that it will be useful,
   11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
   12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   13 * GNU General Public License for more details.
   14 *
   15 * You should have received a copy of the GNU General Public
   16 * License along with this library; if not, write to the Free Software
   17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
   18 */
   19
   20:- module(dcg_julia,
   21      [  term_jlstring/2   % (+Expr, -String)  ~Prolog term to Julia string
   22      ,  op(200,xfx,::)    % type annotations
   23      ,  op(150,fx,:)      % symbols
   24      ,  op(160,yf,'`')    % postfix ctranspose operator
   25      ,  op(800,xfy,:>:)
   26      ,  op(210,xfy,.^)    % broadcasting exponentiation
   27      ,  op(410,yfx,.*)    % broadcasting times
   28      ,  op(410,yfx,./)    % broadcasting division
   29      ,  op(410,xfy,.\)    % reverse broadcasting division
   30      ,  op(400,xfy,\)     % reverse matrix division
   31      ,  op(100,yfx,'`')   % dot, as in modules and fields
   32      ,  op(750,xfy,\\)    % lambda abdstraction
   33      ,  op(200,yf,[])     % array indexing
   34      ,  op(160,fx,.)      % broadcasting op
   35      ,  op(200,yfx,@)     % function application
   36      ,  op(200,yfx,.@)    % broadcasting function application
   37      ,  op(300,yf,...)    % splicing
   38      ,  op(700,xfy,=>)    % dictionary key-value pair
   39      ]).   40
   41
   42:- multifile dcg_julia:pl2jl_hook/2.

Julia DCG

TODO

@ for macros? ; for keyword arguments

REVIEW \\ for lambda

Julia expression syntax

The expression syntax adopted by this module allows Prolog terms to represent or denote Julia expressions. Let T be the domain of recognised Prolog terms (denoted by type expr), and M be the domain of Julia expressions written in Julia syntax. Then V : T->M is the valuation function which maps Prolog term X to Julia expression V[X]. These are some of the constructs it recognises:

X:>:Y           % |--> V[X]; V[Y]  (sequential evaluation returning second value)
X=Y             % |--> V[X]=V[Y] (assignment, X must denote a valid left-value)
if(X,Y)         % |--> if V[X] then V[Y] end
if(X,Y,Z)       % |--> if V[X] then V[Y] else V[Z] end

+X              % |--> +(V[X])
-X              % |--> -(V[X])
X+Y             % |--> +(V[X],V[Y])
X-Y             % |--> -(V[X],V[Y])
X^Y             % |--> ^(V[X],V[Y])
X*Y             % |--> *(V[X],V[Y])
X/Y             % |--> /(V[X],V[Y])
X\Y             % |--> \(V[X],V[Y])
X.^Y            % |--> .^(V[X],V[Y])
X.*Y            % |--> .*(V[X],V[Y])
X./Y            % |--> ./(V[X],V[Y])
X.\Y            % |--> .\(V[X],V[Y])
X rdiv Y        % |--> //(V[X],V[Y])
X:Y:Z           % |--> colon(V[X],V[Y],V[Z])
X:Z             % |--> colon(V[X],V[Z])
X>Z             % |--> >(V[X],V[Y])
X>=Z            % |--> >=(V[X],V[Y])
X<Z             % |--> <(V[X],V[Y])
X=<Z            % |--> <=(V[X],V[Y])
X==Z            % |--> ==(V[X],V[Y])
[X1,X2,...]     % |--> [ V[X1], V[X2], ... ]

:X              % |--> :V[X] (symbol or abstract syntax tree)
'Inf'           % |--> Inf (positive infinity)
'NaN'           % |--> NaN (not a number)
X`              % |--> ctranpose(V[X]) (conjugate transpose, V[X]')
X`Y             % |--> V[X].V[q(Y)]
X\\Y            % |--> (V[X]) -> V[Y])
q(X)            % wrap V[X] in single quotes (escaping internal quotes)
qq(X)           % wrap V[X] in double quotes (escaping internal double quotes)
arr(Lists)         % multidimensional array from nested lists.
arr(Dims,Lists)    % multidimensional array from nested lists.
int64(Dims,Lists)  % multidimensional array of Int64
float64(Dims,Lists)% multidimensional array of Int64

Things to bypass default formatting

noeval(_)          % triggers a failure when processed
\(P)               % escape and call phrase P directly to generate string
$(X)               % calls pl2jl_hook/2, denotes V[Y] where pljl_hook(X,Y).
'$VAR'(N)          % gets formatted as p_N where N is assumed to be atomic.

All other Prolog atoms are written using write/1. Prolog dictionaries are written as Julia dictionaries. Dictionary keys can be atoms (written as Julia symbols) or small integers (written as Julia integers). Other Prolog terms are assumed to be calls to functions named according to the head functor. Thus V[ <head>( <arg1>, <arg2>, ...) ] = <head>(V[<arg1>, V[<arg2>], ...).

To be done
- Expression language: arr(Vals,Shape,InnerFunctor) - allows efficient representation of arrays of arbitrary things. Will require more strict nested list form. */
  128:- use_module(library(dcg_core)).  129:- use_module(library(dcg_codes)).  130
  131:- set_prolog_flag(back_quotes,symbol_char).  132:- set_prolog_flag(double_quotes,codes).
 pl2jl_hook(+X:term, -Y:expr) is nondet
Clauses of pl2jl_hook/2 allow for extensions to the expression language such that V[$X] = V[Y] if pl2jl_hook(X,Y).
 expr(+X:expr)// is nondet
Convert Julia expression as a Prolog term to string representation.
  142expr(A:>:B)      --> !, "(", expr(A), ";", expr(B), ")".
  143expr(A=B)        --> !, "(", expr(A), " = ", expr(B), ")".
  144expr(A::B)       --> !, "(", expr(A), ")::", expr(B). % FIXME: think about syntax for Julia types
  145expr(if(A,B))    --> !, "if ",expr(A), " ", expr(B), " end".
  146expr(if(A,B,C))  --> !, "if ",expr(A), " ", expr(B), " else ", expr(C), " end".
  147expr(using(P))   --> !, "using ", atm(P).
  148
  149expr(\X)         --> !, phrase(X).
  150expr($X)         --> !, {pl2jl_hook(X,Y)}, expr(Y).
  151expr(q(X))       --> !, q(expr(X)).
  152expr(qq(X))      --> !, qq(expr(X)).
  153expr(noeval(_))  --> !, {fail}. % causes evaluation to fail.
  154
  155expr(A>=B)--> !, ">=",args(A,B).
  156expr(A=<B)--> !, "<=",args(A,B).
  157expr(A\=B)--> !, "!=",args(A,B).
  158expr(\+A) --> !, "!",args(A).
  159expr(A:B:C) --> !, expr(colon(A,B,C)).
  160expr(A:B) --> !, expr(colon(A,B)).
  161expr(rdiv(A,B)) --> !, "//", args(A,B).
  162expr(<<(A,B)) --> !, "∘", args(A,B). % function composition
  163
  164expr([])     --> !, "[]".
  165expr([X|Xs]) --> !, "[", seqmap_with_sep(",",expr,[X|Xs]), "]".
  166
  167expr(:B) --> !, ":", {atomic(B)} -> atm(B); ":(", expr(B), ")".
  168expr(A`B) --> !, expr(A), ".", atm(B).
  169expr(B`)  --> !, "ctranspose", args(B).
  170expr(B...) --> !, expr(B), "...".
  171expr(A\\B) --> !, { term_variables(A,V), varnames(V) },
  172   "((", arglist(A), ") -> ", expr(B), ")".
  173
  174expr([](Xs,'`')) --> !, "[", slist(Xs), "]".
  175expr([](Is,X))   --> !, expr(X), "[", clist(Is), "]".
  176expr(A@B)        --> !, expr(A), arglist(B).
  177expr(A.@B)       --> !, expr(.A), arglist(B).
  178expr(.A)         --> !, "(", expr(A), ")", ".".
  179expr(#())        --> !, "()".
  180expr(#(A))       --> !, "(", expr(A), ",)".
  181expr(#(A,B))     --> !, arglist([A,B]).
  182
  183expr(int64([_],L))   --> !, "Int64[", clist(L), "]".
  184expr(float64([_],L)) --> !, "Float64[", clist(L), "]".
  185expr(int64(S,L))   --> !, "reshape(Int64[", flatten(S,L), "],reverse(", arglist(S), "))".
  186expr(float64(S,L)) --> !, "reshape(Float64[", flatten(S,L), "],reverse(", arglist(S), "))".
  187
  188expr(arr($X))    --> !, { pl2jl_hook(X,L) }, expr(arr(L)).
  189expr(arr(L))     --> !, { array_dims(L,D) }, array(D,L).
  190expr(arr(D,L))   --> !, array(D,L).
  191expr(arr(D,L,P)) --> !, array(D,P,L).
  192expr('$VAR'(N))  --> !, "p_", atm(N).
  193
  194% these are the catch-all clauses which will deal with identifiers literals function calls, and dicts
  195expr(A) --> {string(A)}, !, qq(str(A)).
  196expr(A) --> {atomic(A)}, !, atm(A).
  197expr(F) --> {compound_name_arity(F,H,0)}, !, atm(H), "()".
  198expr(A) --> {is_dict(A)}, !, {dict_pairs(A,_,Ps), maplist(pair_to_jl,Ps,Ps1)}, "Dict", arglist(Ps1).
  199expr(F) --> {F=..[H|AX]}, ({H='#'} -> []; atm(H)), arglist(AX).
  200
  201expr_with(Lambda,Y) --> {copy_term(Lambda,Y\\PY)}, expr(PY).
  202pair_to_jl(K-V, KK=>V) :- atom(K) -> KK= :K; KK=K.
  203
  204
  205% dimensions implicit in nested list representation
  206array_dims([X|_],M) :- !, array_dims(X,N), M is N+1.
  207array_dims(_,0).
  208
  209flatten([], X) --> expr(X).
  210flatten([_|D], L) --> seqmap_with_sep(",", flatten(D), L).
 array(+Dims:natural, +Id:ml_eng, +Array)// is det
Format nested lists as a multidimensional array. Dims is the number of dimensions of the resulting array and should equal the nesting level of Array, ie if Array=[1,2,3], Dims=1; if Array=[[1,2],[3,4]], Dims=2, etc.
  218array(0,X) --> !, expr(X).
  219array(1,L) --> !, "[", seqmap_with_sep(";",expr,L), "]".
  220array(2,L) --> !, "[", seqmap_with_sep(" ",array(1),L), "]".
  221array(N,L) --> {succ(M,N)}, "cat(", atm(N), ",", seqmap_with_sep(",",array(M),L), ")".
  222
  223array(0,P,X) --> !, expr_with(P,X).
  224array(1,P,L) --> !, "[", seqmap_with_sep(";",expr_with(P),L), "]".
  225array(2,P,L) --> !, "[", seqmap_with_sep(" ",array(1,P),L), "]".
  226array(N,P,L) --> {succ(M,N)}, "cat(", atm(N), ",", seqmap_with_sep(",",array(M,P),L), ")".
 clist(+Id:ml_eng, +Items:list(expr))// is det
Format list of Julia expressions in a comma separated list.
  230clist([]) --> [].
  231clist([L1|LX])  --> expr(L1), seqmap(do_then_call(",",expr),LX).
  232
  233slist([]) --> [].
  234slist([L1|LX])  --> expr(L1), seqmap(do_then_call(" ",expr),LX).
 arglist(+Id:ml_eng, +Args:list(expr))// is det
DCG rule to format a list of Julia expressions as function arguments including parentheses.
  239arglist(X) --> "(", clist(X), ")".
 args(+Id:ml_eng, +A1:expr, +A2:expr)// is det
 args(+Id:ml_eng, +A1:expr)// is det
DCG rule to format one or two Julia expressions as function arguments including parentheses.
  246args(X,Y) --> "(", expr(X), ",", expr(Y), ")".
  247args(X) --> "(", expr(X), ")".
 atm(+A:atom)// is det
DCG rule to format an atom using write/1.
  251atm(A,C,T) :- format(codes(C,T),'~w',[A]).
  252
  253varnames(L) :- varnames(1,L).
  254varnames(_,[]).
  255varnames(N,[TN|Rest]) :-
  256   atom_concat(p_,N,TN), succ(N,M),
  257   varnames(M,Rest).
 term_jlstring(+X:expr, -Y:list(code)) is det
Convert term representing Julia expression to a list of character codes.
  262term_jlstring(Term,String) :- phrase(expr(Term),String), !