%
%   BaSiX (with the emphasis on SICK!) by Andrew Marc Greene
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Andrew's Affiliations
%
%   Copyright (C) 1990 by Andrew Marc Greene
%   <amgreene@mit.edu>
%   MIT Project Athena
%   Student Information Processing Board
%   All rights reserved.
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  BaSiX's Beginnings
%
\def\flageol{\catcode13=13}
\def\endflageol{\catcode13=5}
\def\struncat{\catcode`\$=12}
\def\strcat{\catcode`\$=11}
\flageol\let\eol
\endflageol
\newif\ifresult
%\newcount\xa\newcount\xb
\def\iw{\immediate\write16}
\def\empty{}
\def\gobble#1{}
\def\spc{ }
\def\itstrue{tt}
\def\itsfalse{tf}
\def\isnull#1{\resultfalse
\expandafter\ifx\csname empty#1\endcsname\empty\resulttrue\fi}
\newcount\matha\newcount\mathb
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Character-string Calls
%
\newcount\strtmp
\def\ascii#1{\strtmp`#1}
\def\chr#1{\begingroup\uccode65=#1\uppercase{\gdef\tmp{A}}\endgroup}
\def\strlen#1{\strtmp-2% don't count " " \iw tokens
  \expandafter\if\stringP #1\let\next\strIter\strIter #1\iw\fi}
\def\strIter#1{\ifx\iw#1\let\next\relax\else\advance\strtmp by 1\relax
  \fi\next}
\def\Flen{\expandafter\strlen\expandafter{\Pa}\return{\number\strtmp}}
\strcat
\def\F$chr${\expandafter\chr\expandafter{\Pa}\return{\tmp}}
\struncat
% first char only:
% \def\Fasc{\expandafter\asc\expandafter{\Pa}\return{\number\strtmp}}
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Debugging Definitions
%
\def\debug{\tracingmacros=2}
\def\diw#1{}
\def\Cdebug{\let\diw\iw\tracingmacros=2 \endeval}
\def\Cnodebug{\def\diw##1{}\endeval}
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Expression Evaluation
%
\def\expression{\let\afterexpression\afterscan\math}
%
%  (math is a misnomer and should -> expr)
%
\newcount\parens\newcount\mathParams
\def\math{\parens=0\mathParams96\mathInit\matheval}
\def\mathRecurse{\advance\parens by 1\relax\mathParams96\mathInit\matheval}
\def\mathInit{\begingroup
  \let\mathAcc\empty
  \let\mathOpRel\empty
  \let\mathOpAdd\empty
  \let\mathOpMul\empty
  \let\mathlhRef\empty}
%
\def\matheval{\after\mathbranch\scan}
%
\def\mathbranch{\diw{EXPRESS:\expandafter\noexpand\word:}
\let\next\matherr
\ifx\empty\word\let\next\mathHardEnd\else                   % Expr. end?
\expandafter\if\numberP\word\let\next\mathLiteral\diw{@@}\fi  % Number?
\expandafter\if\stringP\word\let\next\mathLiteral\fi        % String literal?
\expandafter\if\identifierP\word\let\next\mathIdentifier\fi % Identifier?
\expandafter\if\stringvarP\word\let\next\mathIdentifier\fi  % :-(
\expandafter\if\macroP\word\let\next\mathMacro\fi           % Macro?
\ifx\word\Oleft\let\next\mathRecurse\fi                     % Open paren?
\ifx\word\Oright\let\next\mathEndRecurse\fi                 % Close paren?
\ifx\word\Ocomma\let\next\mathComma\fi                      % Comma?
%
%  Operator?
%
\ifx\word\Oplus\let\next\mathOp\diw{!+}\fi
\ifx\word\Ominus\let\next\mathOp\diw{!-}\fi
\ifx\word\Otimes\let\next\mathOp\diw{!*}\fi
\ifx\word\Odiv\let\next\mathOp\diw{!/}\fi
\ifx\word\Olt\let\next\mathOp\diw{!<}\fi
\ifx\word\Oeq\let\next\mathOp\diw{!=}\fi
\ifx\word\Ogt\let\next\mathOp\diw{!>}\fi
%
\fi\next}
%
\def\Oleft{(}\def\Oright{)}\def\Ocomma{,}
\def\Oplus{+}\def\Ominus{-}\def\Otimes{*}\def\Odiv{/}
\def\Olt{<}\def\Oeq{=}\def\Ogt{>}
%
% There's got to be a better way to do the above....
%
\def\mathLiteral{\diw{MLIT}\ifx\empty\mathAcc\diw{ACCUMUL:\word:}
\expandafter\def\expandafter\mathAcc\expandafter
{\expandafter\expandafter\expandafter\empty\word}
\else
\diw{ACC has :\mathAcc: and word is :\word:}
\errmessage{Syntax Error: Two values with no operator}\fi\matheval}
%
%  Operator stuff:  (Need to add string support / error checking)
%
\def\mathAdd{\advance\matha by \mathb}
\def\mathSub{\advance\matha by -\mathb}
\def\mathMul{\multiply\matha by \mathb}
\def\mathDiv{\divide\matha by \mathb}
\def\mathEQ{\ifnum\matha=\mathb\matha-1\else\matha0\fi}
\def\mathGT{\ifnum\matha>\mathb\matha-1\else\matha0\fi}
\def\mathLT{\ifnum\matha<\mathb\matha-1\else\matha0\fi}
%
\def\mathFlushRel{\mathFlushAdd\ifx\empty\mathOpRel\else
  \matha=\mathlhRel\relax\mathb=\mathAcc\relax\mathOpRel
  \edef\mathAcc{\number\matha}\let\mathOpRel\empty\fi}
%
\def\mathFlushAdd{\mathFlushMul\ifx\empty\mathOpAdd\else
  \matha=\mathlhAdd\relax\mathb=\mathAcc\relax\mathOpAdd
  \edef\mathAcc{\number\matha}\let\mathOpAdd\empty\fi}
%
\def\mathFlushMul{\mathFlushRef\ifx\empty\mathOpMul\else
  \matha=\mathlhMul\relax\mathb=\mathAcc\relax\mathOpMul
  \edef\mathAcc{\number\matha}\let\mathOpMul\empty\fi}
%
\def\mathFlushRef{\ifx\empty\mathlhRef\else
  \mathParam
  \mathlhRef\let\mathlhRef\empty\fi}
%
\def\mathOp{%
\if\word+
  \mathFlushAdd\let\mathlhAdd\mathAcc\let\mathOpAdd\mathAdd\fi
\if\word-
  \mathFlushAdd\let\mathlhAdd\mathAcc\let\mathOpAdd\mathSub\fi
\if\word*
  \mathFlushMul\let\mathlhMul\mathAcc\let\mathOpMul\mathMul\fi
\if\word/
  \mathFlushMul\let\mathlhMul\mathAcc\let\mathOpMul\mathDiv\fi
\if\word=
  \mathFlushRel\let\mathlhRel\mathAcc\let\mathOpRel\mathEQ\fi
\if\word>
  \mathFlushRel\let\mathlhRel\mathAcc\let\mathOpRel\mathGT\fi
\if\word<
  \mathFlushRel\let\mathlhRel\mathAcc\let\mathOpRel\mathLT\fi
\let\mathAcc\empty
\matheval}
%
\def\mathIdentifier{%
\expandafter\ifx\csname C\word\endcsname\relax
\expandafter\ifx\csname F\word\endcsname\relax
\expandafter\ifx\csname V\word\endcsname\relax
\let\next\matherr\diw{LOSING:\word:}
\else\let\next\mathVariable\fi
\else\let\next\mathFunction\fi
\else\let\next\mathCommand\fi\next}
%
\def\mathVariable{\expandafter\edef\expandafter\word\expandafter
{\csname V\word\endcsname}\mathbranch}
\def\mathCommand{\expandafter\mathHardEnd\word}
\def\mathFunction{\expandafter\let\expandafter\mathlhRef
\csname F\word\endcsname\matheval}
%
\def\mathParam{\advance\mathParams by 1\relax\chr\mathParams
  \diw{PARAM:\tmp:\mathAcc:}
  \expandafter\edef\csname P\tmp\endcsname{\mathAcc}}
\def\mathComma{\mathEnd\mathParam\mathInit\matheval}
\def\mathEndRecurse{\mathEnd\advance\parens by -1\matheval}
\def\mathEnd{\diw{MATHEND: ACC=\mathAcc:}\mathFlushRel
  \xdef\mathtemp{\mathAcc}\endgroup\edef\mathAcc{\mathtemp}}
\def\mathHardEnd{\ifnum\parens>0\errmessage{Insufficient closeparens.}\relax
  \let\next\endeval\else\let\next\mathFinal\fi\next}
\def\mathFinal{\mathEnd\let\value\mathAcc\endexpression}
\def\matherr{\errmessage{Syntax error: Unknown symbol \word}}
\def\endexpression{\afterexpression}
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Linked List
%
\def\gotofirstline{\edef\lpointer{\csname L0\endcsname}}
\def\foreachline#1{\ifnum\lpointer<99999\edef\word{\lpointer}#1%
\edef\lpointer{\csname L\word\endcsname}\foreachline{#1}\fi}
%
%  gotopast{#1}  where #1 is a line number, will set \lpointer to
%                the least value such that L(lpointer)>#1
%
\def\gotopast#1{\def\lpointer{0}\def\target{#1}\gotopastloop}
%
\def\gotopastloop{\edef\tmp{\csname L\lpointer\endcsname}%
\ifnum\tmp<\target%
\edef\lpointer{\csname L\lpointer\endcsname}%
\let\next=\gotopastloop\else\let\next=\relax\fi
\next}
%
\flageol\def\addLineToLinkedList#1#2
{\def#1{#2}\diw{Just stored #2 in \noexpand #1}%
% now put it into linked list...
\expandafter\ifx\csname L\word\endcsname\relax% if it isn't already there,
\gotopast{\word}% \def\lpointer{what-should-point-to-word}
\expandafter\edef\csname L\word\endcsname{\csname L\lpointer\endcsname}%
\expandafter\edef\csname L\lpointer\endcsname{\word}%
\fi\endeval
}\endflageol
\expandafter\def\csname L0\endcsname{99999}
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Program Parser
%
\def\evalline{%\iw{EVALLINE :\word:}%
\csname C\word\endcsname}  %error-checking?  :-)
\def\evalerror{\errmessage{Unkonwn command.  Sorry.}}
%
%  \mandatory takes one argument and checks to see if the next
%  non-whitespace token matches it.  If not, an error is generated.
%
\def\mandatory#1{\def\tmp{#1}\mandatest}
\def\mandatest#1{\def\tmpp{#1}\ifx\tmp\tmpp\let\next\afterscan\else
\let\next\manderror\fi\next}
\def\manderror{\errmessage{\tmpp\spc read when \tmp\spc expected.}%
\afterscan}
%
%  \parseline gets the first WORD of the next line.  If it's a line
%  number, \scanandstoreline is called; otherwise the line is executed.
%
\def\parseline{\after\firsttest\scan}
\def\firsttest{\expandafter\if\numberP\word
\let\next\grabandstoreline\else\let\next\evalline\fi\next}
\def\grabandstoreline{\diw{Grabbing line \word.}%
\expandafter\addLineToLinkedList\csname/\word\endcsname}
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Syntactic Scanner
%
%  The \scan routine reads the next WORD and then calls \afterscan.
%
%  As syntactic sugar, one can write \after\foo to set \afterscan to
%  \foo.
%
%  Here are the rules governing WORD.  Initial whitespace is
%  discarded.  The word is the next single character, unless that
%  character is one of the following:
%
%   A-Z or a-z:  [A-Z,a-z][A-Z,a-z,0-9]*\$?
%   0-9:         [0-9]+
%   ":           "[^"]*"
%   <,=,>:       [<=>][<=>]?  (one or two; not the same if two)
%
%   Note that the string literal ignores spaces but may be abnormally
%   terminated by an end-of-line.  (I wasn't sure how to express that
%   as a regexp).
%
%
\newif\ifscan  %  shall we continue scanning?
%
\def\scan{\def\word{}\futurelet\q\scanFirst}
%
\def\scanFirst{%  Checks the first character to determine type.
\let\next\scanIter
\expandafter\if\spc\noexpand\q         %  Space -- ignore it
  \let\next\scanSpace\else
\if\eol\noexpand\q                     %  End of line -- no word here
  \let\next\scanEnd\else
\ifcat A\noexpand\q                    %  Then we have an identifier
  \let\scanTest\scanIdentifier\else 
\expandafter\if\digit\q
  \let\scanTest\scanNumericConstant\else
\if"\noexpand\q
  \let\scanTest\scanStringConstant\else
\expandafter\if\relationP\q
  \let\scanTest\scanRelation
\else
  \let\scanTest\scanfalse
\fi\fi\fi\fi\fi\fi\next}
%
\def\scanIter#1{\expandafter\def\expandafter\word\expandafter{\word #1}
  \futurelet\q\scanContinueP}
\def\scanContinueP{\scanTest\ifscan\let\next\scanIter
  \else\let\next\scanEnd\fi\next}
%
\def\scanSpace#1{\scan}% If the first char is a space, gobble it and try again.
\def\scanIdentifier{\ifcat A\noexpand\q\scantrue\else
\expandafter\if\digit\q\scantrue
\else\if$\noexpand\q\scantrue
  \expandafter\def\expandafter\word\expandafter{\expandafter$\word}
  \let\scanTest\scanfalse\else
\scanfalse\fi\fi\fi}
\def\scanEndString{\scanfalse}
\def\scanNumericConstant{\expandafter\if\digit\q\scantrue\else\scanfalse\fi}
\def\scanStringConstant{\scantrue\if"\q\let\scanTest\scanfalse\fi}
\def\scanRelation{\if<\q\scantrue\else\if>\q\scantrue\else\if=\q\scantrue
\else\scanfalse\fi\fi\fi}
%
\def\scanEnd#1{\relax\diw{SCANNED:\word:}
\afterscan #1}% dumps trailing spaces.
\def\after{\let\afterscan}%
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Type Tests (Predicates for type determination)
%
\def\relationP#1{tf}  % for now, only single-char relations
\def\identifierP#1{\expandafter\identifierTest #1\\}
\def\identifierTest#1#2\\{\ifcat A#1\itstrue\else\itsfalse\fi}
\def\stringvarP#1{\expandafter\stringvarTest #1\\}
\def\stringvarTest#1#2\\{\if$#1\itstrue\else\itsfalse\fi}
\def\stringP#1{\expandafter\stringTest #1\\}
\def\stringTest#1#2\\{\if #1"\itstrue\else\itsfalse\fi}
\def\numberP#1{\expandafter\numberTest #1\\}
\def\numberTest#1#2\\{\expandafter\if\digit #1\itstrue\else\itsfalse\fi}
\def\macroP#1{\expandafter\macroTest #1\\}
\def\macroTest#1#2\\{\expandafter\ifx #1\relax\itstrue\else\itsfalse\fi}
%
%  \digit tests its single-token argument and returns tt if true, 
%  tf otherwise.
%
%
\def\digit#1{%
\if0\noexpand#1\itstrue\else
\if1\noexpand#1\itstrue\else
\if2\noexpand#1\itstrue\else
\if3\noexpand#1\itstrue\else
\if4\noexpand#1\itstrue\else
\if5\noexpand#1\itstrue\else
\if6\noexpand#1\itstrue\else
\if7\noexpand#1\itstrue\else
\if8\noexpand#1\itstrue\else
\if9\noexpand#1\itstrue\else\itsfalse
\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi
% 9  8  7  6  5  4  3  2  1  0  
}
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  User Utilities.  These are the commands that are called by the
%  user.  We could really use a better section name.  :-)
%
%  List (one line or all lines, for now)
%
\def\Clist{\after\listmain\scan}
\def\listmain{\isnull{\word}\ifresult\let\next\listalllines
\else\let\next\listoneline\fi\next}
\def\listline{\iw{\word\spc\csname/\word\endcsname}}
\def\listalllines{\gotofirstline\foreachline{\listline}\endeval}
\def\listoneline{\listline\endeval}
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Different degrees of ``stop execution''
%
\def\Csystem{\end}       % exits to the system
\def\Cexit{}% \endflageol} % exits to TeX
\flageol%
\def\Cstop#1
{\iw{Stopped in \lineno.}\cleanstop}%
\def\cleanstop{\diw{CLEANSTOP}\let\endeval\enduserline\endeval
}\endflageol
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  The command ``rem'' introduces a remark
%
\def\Crem{\endeval}%
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  The ``let'' command allows variable assignments
%
\def\Clet{\after\letgetequals\scan}
\def\letgetequals{\after\letgetvalue\mandatory{=}}
\def\letgetvalue{\after\letdoit\expression}
\def\letdoit{\expandafter\edef\csname V\word\endcsname{\value}%
\endeval}
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  The ``print'' command takes a [list of] expression[s] and displays
%  it [them].
%
\def\Cprint{\after\printit\expression}
\def\printit{\iw{\value}\endeval}
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  The ``if'' command takes an expression, the word ``then,'' and
%  another command.  If the expression is non-zero, the command is
%  executed; otherwise it is ignored.
%
\def\Cif{\after\getift\expression}
\def\Cthen{\errmessage{Syntax error: THEN without IF}}
\def\getift{\after\getifh\mandatory t}
\def\getifh{\after\getife\mandatory h}
\def\getife{\after\getifn\mandatory e}
\def\getifn{\after\consequent\mandatory n}
\def\consequent{\ifnum\value=0\let\next=\endeval\else\let\next=\evalconsq\fi
\next}
\def\evalconsq{\after\evalline\scan}
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Functions
%
%  Functions may read the counter \mathParams to find out the number
%  of the top parameter.  Parameters are in Pa Pb Pc etc.
%
\def\return{\expandafter\def\expandafter\mathAcc\expandafter}
%
\def\Finc{\matha=\Pa \advance\matha by 1
\return{\number\matha}}
%
\def\Fmin{\ifnum\Pa<\Pb\return{\Pa}\else\return{\Pb}\fi}
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Program execution control
%
\def\Crun{\let\endeval\endincrline\def\lineno{0}\endeval}
\def\Cgoto{\let\endeval\endgotoline\after\gotomain\scan}
\def\gotomain{\edef\lineno{\word}\endeval}
%
\flageol%
\def\execline{%\message{Executing line \lineno...}%
\edef\theline{\csname/\lineno\endcsname}%
%\message{THE LINE\theline}%
\let\endeval\endincrline\after\evalline\expandafter\scan\theline
}\endflageol
%
%  Different varieties of what to do at the end of a command:
%
%  get new line from user  (enduserline)
%  get next line in order  (endincrline)
%  get line in \lineno     (endgotoline)
%  keep parsing current line (endcolonline tbi)
%
\flageol%
\def\endincrline#1
{\diw{ENDINCRLINE}\edef\lineno{\csname L\lineno\endcsname}\execnextline}
%
\def\endgotoline#1
{\diw{ENDGOTOLINE}\let\endeval\execincrline\execnextline}%
%
\def\execnextline{\diw{Ready to execute line \lineno...}%
\ifnum\lineno<99999\let\next\execline\else\let\next\cleanstop\fi\next}%
%
\def\enduserline #1
{\diw{ENDUSERLINE}\parseline}\endflageol
%
\let\endeval\enduserline
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%   Start your engines!
%
\iw{This is BaSiX, v0.3, emphasis on the SICK!  by amgreene@mit.edu}
\flageol
\catcode32=12
\endeval
