% Release 0.1 (2021/04/13) of xintsession.tex
% author: Jean-Francois Burnol
% License: LPPL 1.3c (author-maintained)
% Usage: execute etex or pdftex
%        then enter "xintsession" at the ** prompt
\input xintexpr.sty\relax%
\input xintbinhex.sty\relax%
\input polexpr.sty\relax% attention will crash if < 0.8
\let\PolToExprOneTerm\PolToExprOneTermStyleB%
%\let\PolToExprTimes\empty%
% we do assume standard catcodes on loading and use
% \XINT_restorecatcodes as a convenience facility.
% It stores \endlinechar but not \newlinechar
\edef\xsrestorecatcodes{\csname XINT_restorecatcodes\endcsname%
                        \newlinechar\the\newlinechar\relax}%
\endlinechar 13 %
\newlinechar 10
\catcode`_ 11
\catcode`& 11
\catcode`@ 11
\catcode`^ 11 
\catcode`" 7
\catcode0 12
\newwrite\xs_out
\edef\xs_name{\jobname-\expandafter\xint_gobble_ii\the\year
                      \expandafter\xint_gobble_i\the\numexpr100+\month\relax
                      \expandafter\xint_gobble_i\the\numexpr100+\day\relax
                      _%
                      \expandafter\xint_gobble_i\the\numexpr(\time+30)/60+99h%
                      \expandafter\xint_gobble_i\the\numexpr100+\time-60*((\time+30)/60-1).tex}%
\immediate\openout\xs_out=\xs_name\relax
\begingroup\catcode37 12
\gdef\xs_message#1{\message{#1}\immediate\write\xs_out{%#1}}\endgroup
{\catcode13\active\def
{""J}\catcode32\active\edef {\string }%
\catcode`?=0\catcode`\\=12\relax%
?xdef?xs_logo{%
                _____       _____
         ____  ____(_)________  /_________  _________________
         __  |/_/_  /__  __ \  __/  _ \_  |/_/__  __ \_  ___/
         __>  < _  / _  / / / /_ /  __/_>  < __  /_/ /  /
         /_/|_| /_/  /_/ /_/\__/ \___//_/|_| _  .___//_/
                                              /_/
  _____       _____                         __________
  ___(_)________  /__________________ ________  /___(_)__   ______
  __  /__  __ \  __/  _ \_  ___/  __ `/  ___/  __/_  /__ | / /  _ \
  _  / _  / / / /_ /  __/  /   / /_/ // /__ / /_ _  / __ |/ //  __/
  /_/  /_/ /_/\__/ \___//_/    \__,_/ \___/ \__/ /_/  _____/ \___/
}%
?immediate?write128{%

  xintsession 0.1
  Copyright (c) Jean-Francois Burnol, 2021.

  \jobname is ?jobname
  Transcript will go to log and to ?xs_name

  ===> HIT THE <RET> KEY NOW! <===
}}%
\def\xs_no{0}%
\XINT_expr_defvar_one{@___}{{0}}%
\XINT_expr_defvar_one{@__}{{0}}%
\XINT_expr_defvar_one{@_}{{0}}%
\def\xs_fetch{\let\xs_in\empty\xs_fetch_a}%
\begingroup
\catcode13\active%
\gdef\xs_fetch_a#1
{%
  \odef\xs_firstitem{\romannumeral0\xintfirstitem{#1.}}%
  \ifcat\relax\expandafter\noexpand\xs_firstitem%
    \expandafter\xs_cs%
  \else%
   \if &\expandafter\noexpand\xs_firstitem%
    \expandafter\expandafter\expandafter\xs_magic%
   \fi%
  \fi%
  \if;\expandafter\noexpand\romannumeral0\xintlastitem{#1}%
    \xs_fetch_b#1^%
  \else%
    \message{(Use ; to end multi-line input)}%
    \odef\xs_in{\xs_in#1}\expandafter\xs_fetch_a%
  \fi%
}%
\endgroup
\def\xs_cs\if;\expandafter\noexpand\romannumeral0\xintlastitem#1#2\fi
{%
   \xs_message{(executing \detokenize{#1} in background)}
   #1\xs_fetch_a
}%
\def\xs_firstfour#1#2#3#4#5\xint_bye{#1#2#3#4}%
\def\xs_magic\if;\expandafter\noexpand\romannumeral0\xintlastitem#1#2\fi
{%
% this gets rid of spaces and an inadvertent ; except for &fp which is too
% short, but this puts a constraint on magic words, maybe I will revisit
  \edef\xs_magic_in{\xs_firstfour#1\empty\empty\empty\empty\xint_bye}%
  \ifcsname xs_magic@\xs_magic_in\endcsname
      \csname xs_magic@\xs_magic_in\expandafter\endcsname
  \else
   \xs_message{(\detokenize\expandafter{\xint_gobble_i#1} is not a magic keyword:
            ignored)}%
  \fi\xs_fetch_a
}%
\def\xs_fetch_b#1;^%
{%
   \odef\xs_in{\xs_in#1}\expandafter\xs_fetch_c
}%
\def\xs_fetch_c
{%
   \if\relax\romannumeral`""@\detokenize\expandafter{\xs_in}\relax\expandafter\xs_pause\fi
   \expandafter\xs_parse_a\xs_in:=^%
}%
\def\xs_pause\expandafter\xs_parse_a\xs_in:=^{\xs_pause_a\xs_fetch_a}%
\def\xs_pause_a\xs_fetch_a
{%
   \xs_message{Need a break? OK, use \string\xintresume\space if you change your mind.}%
   \xs_resetcats
}%
\def\xs_bye\xs_fetch_a
{%
   \xs_message{Did I say something wrong?}%
   \xs_message{Session transcript written on \xs_name}%
%   \xs_resetcats
   \immediate\closeout\xs_out
% expeditive exit because I need to think about generating new output
% filename with a check that it is not same (a minute may be too coarse?).
% Add random postfix? or simply do not bother rather...
   \csname bye\endcsname
}%
\def\xs_parse_a#1:=#2%
{%
   \xint_gob_til_^#2\xs_calc ^%
   \def\xs_left{#1}\xs_parse_b#2%
}%
\def\xs_calc ^#1^%
{%
   \edef\xs_no{\the\numexpr1+\xs_no}%
   \xs_xintdefvar @_\xs_no=\xs_in;%
   \immediate\write\xs_out
   {\expandafter\string\xs_xintdefvar\space @_\xs_no:=%
                \detokenize\expandafter{\xs_in};}%
   \XINT_expr_defvar_one{@___}{\csname XINT_expr_varvalue_@__\endcsname}%
   \XINT_expr_defvar_one{@__}{\csname XINT_expr_varvalue_@_\endcsname}%
   \XINT_expr_defvar_one{@_}{\csname XINT_expr_varvalue_@_\xs_no\endcsname}%
   \xs_message{(@_\xs_no)\space\space\space\space\xs_xinteval{@_\xs_no}}%
   \xs_fetch
}%
\def\xs_parse_b #1:=^%
{%
   \def\xs_right{#1}\expandafter\xs_parse_c\xs_left(;%
}%
\def\xs_parse_c #1(#2%
{%
   \edef\xs_funcname{\expandafter\xint_zapspaces\detokenize{#1} \xint_gobble_i}%
   \xint_gob_til_sc#2\xs_defvar;\xs_deffunc
}%
\def\xs_deffunc #1;%
{%
% attention: \xs_xintdeffunc needs to see parentheses immediately
   \expandafter\xs_xintdeffunc\xs_left=\xs_right;%
   \immediate\write\xs_out
   {\expandafter\string\xs_xintdeffunc\space\detokenize\expandafter{\xs_left}:=%
                \detokenize\expandafter{\xs_right};}%
   \expandafter\ifx\xs_xintdeffunc\poldef
        \xs_message{\xs_funcname\space = \PolToExpr{\xs_funcname}}%
        \xs_message{Issue \string\PolGenFloatVariant{\xs_funcname} if fp evaluations
                 with \xs_funcname\space are intended}%
   \else\xs_message{Function \xs_funcname\space defined}%
   \fi\xs_fetch
}%
\def\xs_defvar;\xs_deffunc
{%
   \edef\xs_no{\the\numexpr1+\xs_no}%
   \xs_xintdefvar @_\xs_no=\xs_right;%
   \immediate\write\xs_out
   {\expandafter\string\xs_xintdefvar\space\detokenize\expandafter{\xs_left}:=%
                \detokenize\expandafter{\xs_right};}%
   \XINT_expr_defvar_one{@___}{\csname XINT_expr_varvalue_@__\endcsname}%
   \XINT_expr_defvar_one{@__}{\csname XINT_expr_varvalue_@_\endcsname}%
   \XINT_expr_defvar_one{@_}{\csname XINT_expr_varvalue_@_\xs_no\endcsname}%
   % this is to allow multiple assignments
   \expandafter\xs_xintdefvar\xs_left=@_\xs_no;%
   \xs_message{(@_\xs_no)\space\space\space\space\xs_xinteval{@_\xs_no}}%
   \xs_fetch
}%
\let\xs_magic@&pau\xs_pause_a
\let\xs_magic@&bye\xs_bye
\def\xs_magic@&fp
{%
   \xs_message{fp mode (\xinttheDigits\space digits)}%
   \def\xs_mode{fp}%
   \def\xs_xintdefvar{\xintdeffloatvar}%
   \def\xs_xintdeffunc{\xintdeffloatfunc}%
   \def\xs_xinteval{\xintfloateval}%
}%
\expandafter\def\csname xs_magic@&fp1\endcsname
{%
   \xintDigits*:=16;%
   \xs_message{fp16 mode (trig reloaded)}%
   \def\xs_mode{fp16}%
   \def\xs_xintdefvar{\xintdeffloatvar}%
   \def\xs_xintdeffunc{\xintdeffloatfunc}%
   \def\xs_xinteval{\xintfloateval}%
}%
\expandafter\def\csname xs_magic@&fp2\endcsname
{%
   \xintDigits*:=24;%
   \xs_message{fp24 mode (trig reloaded)}%
   \def\xs_mode{fp24}%
   \def\xs_xintdefvar{\xintdeffloatvar}%
   \def\xs_xintdeffunc{\xintdeffloatfunc}%
   \def\xs_xinteval{\xintfloateval}%
}%
\expandafter\def\csname xs_magic@&fp3\endcsname
{%
   \xintDigits*:=32;%
   \xs_message{fp32 mode (trig reloaded)}%
   \def\xs_mode{fp32}%
   \def\xs_xintdefvar{\xintdeffloatvar}%
   \def\xs_xintdeffunc{\xintdeffloatfunc}%
   \def\xs_xinteval{\xintfloateval}%
}%
\def\xs_magic@&exa
{%
   \xs_message{exact mode (floating point evaluations use \xinttheDigits\space digits)}%
   \def\xs_mode{exact}%
   \def\xs_xintdefvar{\xintdefvar}%
   \def\xs_xintdeffunc{\xintdeffunc}%
   \def\xs_xinteval{\xinteval}%
}%
\def\xs_magic@&int
{%
   \xs_message{int mode (integer only: / rounds, // is floored division, /: is modulo)}%
   \def\xs_mode{int}%
   \def\xs_xintdefvar{\xintdefiivar}%
   \def\xs_xintdeffunc{\xintdefiifunc}%
   \def\xs_xinteval{\xintiieval}%
}%
\def\xs_magic@&pol
{%
   \xs_message{pol mode (i.e. function definitions use \string\poldef)}%
   \def\xs_mode{pol}%
   \def\xs_xintdefvar{\xintdefvar}%
   \def\xs_xintdeffunc{\poldef}%
   \def\xs_xinteval{\xinteval}%
}%
\def\xs_magic@&mod{\xs_message{current mode: \xs_mode}}%
\def\xs_setcats
{%
    \newlinechar10\endlinechar13%
    \catcode13\active\catcode`@ 11\relax
}%
\def\xs_storecats{\edef\xs_resetcats
{%
    \newlinechar\the\newlinechar
    \endlinechar\the\endlinechar
    \catcode13=\the\catcode13
    \catcode`@\the\catcode`@\relax
}}%
\begingroup
\catcode13\active\catcode32\active%
\gdef\xintstart{\edef {\string }\xs_setcats\def
{""J}\message{\xs_logo
  Welcome to xintexpr interactive mode!

  - input is `;'-terminated and can extend over multiple lines,
  - definitions must be done using the `:=' syntax, not `='.

  Any line starting with a control sequence is executed in the
  background, interrupted numerical input resumes at next prompt.

  To pause, say either `;' or `&pause'.
  To quit, say `&bye'.
  For help say `&help'.
  Starting in}\xs_magic@&exa\let
=\xs_fetch}\gdef\xintresume{\edef {\string }\xs_storecats\xs_setcats%
\message{Welcome back to xintexpr interactive mode!}\let
=\xs_fetch}\gdef\xs_help\xs_fetch_a{\def
{""J}\edef {\string }\message{%
  - Terminate inputs using the `;' character.

  - Input can extend over multiple lines (<RET> terminated).

  - An empty input (`;' terminated) or the magic word `&pause' puts
    the session on hold and switches to normal TeX interaction.
    Use \string\xintresume to resume the session.

  - The numerical engine can work in various modes:
    * exact mode means using \string\xintexpr context,
    * fp mode uses \string\xintfloatexpr,
    * int mode uses integer-only \string\xintiiexpr,
    * pol mode makes function definitions using \string\poldef.
    To activate a mode enter `&<mode name>' at the prompt

  - Lines starting with any control sequence are handed over to
    TeX, and numerical input resumes on next line.

  - To change the floating point precision enter at the prompt:
    \string\xintDigits*:= <some number>;
    then at next prompt switch to fp mode using `&fp'.
    This can be done in the midst of a computation!

(1/5) HIT <RET> KEY
}\let
=\xs_help_b}\gdef\xs_help_b{\def
{""J}\message{%
  - Specific toggles `&fp16', `&fp24', `&fp32' are already predefined.

  - Issue `&mode' if you forgot in what mode you are currently in.

  - Definitions (variables or functions) are done using `:='.

    IMPORTANT: currently, using a `=' by oversight will cause
    breakage because xintsession thinks it is then a numerical
    evaluation, not a definition, but there is no `=' operator in
    xintexpr syntax.

    Perhaps in future it will be allowed to make definitions using
    `=' and xintsession will check if it is not misinterpreting a
    `=' from `<=', `>=', `!=', `==' or `seq()' syntax but this
    currently was considered to add too much overhead for the
    benefit.
    
  - Parentheses present before the `:=' trigger a function
    definition, else a variable definition is done.

(2/5) HIT <RET> KEY
}\let
=\xs_help_c}\gdef\xs_help_c{\def
{""J}\message{%
  - Functions defined in one mode are not available for usage in other
    modes without further steps; check xintexpr doc.

  - Polynomial definitions (pol mode) are function definitions
    (see polexpr documentation).  To use a polynomial also as a
    numerical function when back in `fp' mode, issue
    \string\PolGenFloatVariant{<polname>} at the prompt.

  - Variables defined in any mode can be used in any mode except:
    * polynomials can be used as variables only in the pol and the
      exact modes,
    * integer only mode requires... integer only variables!
      One can use num() wrapper see xintexpr documentation.

  - Whenever a variable is defined, or a computation is performed,
    the result is assigned a label @_<integer>. These automatic
    variables can then be used in subsequent computations.

  - In the case of a multi-variable assignment (see xintexpr doc)
    the automatic variable @_<integer> holds the whole thing, which
    may be a nutple or an ople, in addition to individual variables.

(3/5) HIT <RET> KEY
}\let
=\xs_help_d}\gdef\xs_help_d{\def
{""J}\message{%
  - The special variables @_, @__, and @___ hold the last, next
    to last and next to next to last evaluation results.

  Known issues:

  - The [P] optional argument of \string\xintfloateval can not
    be used because it is not recognized by \string\xintdeffloatvar,
    one can use the float() function with its second argument.

  - Sometimes one wishes to use \string\xintexpr...\string\relax wrapper
    inside fp mode; this is possible but can not be first in current
    input line as it would trigger control sequence execution.  Start
    with 0+ or use parentheses.

  - The output format of the exact and fp mode is not yet decided
    at upstream xintexpr; in particular trailing zeros are not
    trimmed and floating point numbers are printed in diverging
    ways in exact vs fp modes.  Besides, the exact mode does
    not do a systematic reduction to lowest terms.  Use the
    reduce() function (or preduce()).

  - By default, the ** and ^ operators operate only with integer
    exponents in exact mode and with half-integers in fp mode.
    Fractional powers are available via the pow(x,a) function.
(4/5) HIT <RET> KEY
}\let
=\xs_help_e}\gdef\xs_help_e{\def
{""J}\message{%
  Known issues (cont.):

  - Refer to xintexpr \string\poormanloghack documentation on
    how to let fractional powers be available with ** or ^,
    and check polexpr documentation for related information.

  - A session transcript file is created which can be executed by
    eTeX to redo all calculations; however @_, @__, and @__
    will not be defined in this context so need to be manually
    replaced by the suitable @_<integer>.

  Future:

  - a plot() function will be added at some point to export
    coordinates into a tex file for usage either with PSTricks or
    TikZ/pgf.

  Caveat:

  Misformed xintexpr input can drop you into TeX error mode.
  In that case, try escaping using `S'.  It does work sometimes.

  Use `&bye' to terminate the session and the TeX run.
    
(5/5) You are back to the xintexpr interactive session! (mode: \xs_mode)
}\let
=\xs_fetch\xs_fetch_a}\endgroup
\let\xs_magic@&hel\xs_help
\xsrestorecatcodes
\csname xs_storecats\endcsname
\catcode13\active\def
{\xintstart
}\endinput%
