
% Lexical analyser for Lang.

% $Id: lex.pl,v 2.4 1994/05/05 16:11:47 dbc Exp dbc $

% $Log: lex.pl,v $
% Revision 2.4  1994/05/05  16:11:47  dbc
% Remove `rep' lexeme.
%
% Revision 2.3  1994/04/26  15:48:23  dbc
% Move `member' and `printString' to `utils.pl'.
%
% Revision 2.1  1994/04/12  13:18:29  dbc
% Eliminate `in' keyword.  Add `expression' and `is' keywords.
% Add `|' symbol.
%
% Revision 1.1  1994/04/11  10:39:42  dbc
% Initial revision
%

% Output tokens are...
%
%   name(<name>)        <name> built from lower case + digits; initial letter
%   opName(<opName>)    <opName> built from "+-*/\^<>:=~.%|"
%   literal(<literal>)  <literal> built from digits
%   variable
%   function
%   expression
%   align
%   with
%   distribute
%   is
%   forall
%   end
%   rightBrack
%   leftBrack
%   rightParen
%   leftParen
%   rightBrace
%   leftBrace
%   comma
%   semicolon

lex(F, Ts) :- readFile(F, L), getTokens(L, Ts).

readFile(F, L) :- see(F), getText(L), seen.

% `getText' gets text from the input, stripping comments and replacing
% tabs and newlines by spaces.

getText(L) :- get0(X), getRest(X, L).

getRest(X, [])       :- isEOF(X), !.
getRest(X, [Sp | L]) :- isCR(X),  !, isSpace(Sp), getText(L).
getRest(X, [Sp | L]) :- isTAB(X), !, isSpace(Sp), getText(L).
getRest(X,  L)       :- isBeginComment(X), !, skpText(L).
getRest(X, [X  | L]) :- getText(L).

skpText(L) :- get0(X), skpRest(X, L).

skpRest(X, [])       :- isEOF(X), !.
skpRest(X, [Sp | L]) :- isCR(X),  !, isSpace(Sp), getText(L).
skpRest(_,  L)       :- skpText(L).

% Lexical analyser

getTokens(List, L) :-
    isWhiteSpace(List, Tail),
    getTokensTail(Tail, L).

getTokensTail([], []).
getTokensTail(List, [T | L]) :-
    getToken(List, InterTail, T),
    isWhiteSpace(InterTail, Tail),
    getTokensTail(Tail, L).

getToken([X | Tail], Tail, T) :- isSpecial(X, T), !.
getToken(List, Tail, T) :-
    isName(List, Tail, S), !,
    name(N, S),
    tokeniseName(N, T).
getToken(List, Tail, T) :-
    isOpName(List, Tail, S), !,
    tokeniseOpName(S, T).
getToken(List, Tail, T) :-
    isLiteral(List, Tail, S), !,
    name(N, S),
    tokeniseLiteral(N, T).
getToken([X | _], _, _) :-
    printString("Illegal character: "),
    printString([X]),
    abort.



isWhiteSpace([X | List], Tail) :-
    isSpace(X), !,
    isWhiteSpace(List, Tail).
isWhiteSpace(List, List).


isName([X | List], Tail, [X | I]) :-
    isAlpha(X), isNameTail(List, Tail, I).

isNameTail([X | List], Tail, [X | I]) :-
    (isAlpha(X) ; isDigit(X)), !, isNameTail(List, Tail, I).
isNameTail(List, List, []).


isOpName([X | List], Tail, [X | I]) :-
    isSymbol(X), isOpNameTail(List, Tail, I).

isOpNameTail([X | List], Tail, [X | I]) :-
    isSymbol(X), !, isOpNameTail(List, Tail, I).
isOpNameTail(List, List, []).


isLiteral([X | List], Tail, [X | I]) :-
    isDigit(X), isLiteralTail(List, Tail, I).

isLiteralTail([X | List], Tail, [X | I]) :-
    isDigit(X), !, isLiteralTail(List, Tail, I).
isLiteralTail(List, List, []).


tokeniseName(N, N) :-
    member(N, [variable, align, with, distribute, function, expression, is,
               forall, end]), !.
tokeniseName(N, name(N)).

tokeniseOpName(S, opName(S)).

tokeniseLiteral(N, literal(N)).


% Lexicon of recognised characters...

% Misc lexical garbage.

isCR(10).
isEOF(-1).
isTAB(9).
isBeginComment(33).  % '!'

% Space

isSpace(32).

% Special delimiting characters.

isSpecial(44,  comma).       % ','
isSpecial(59,  semicolon).   % ';'
isSpecial(91,  leftBrack).   % '['
isSpecial(93,  rightBrack).  % ']'
isSpecial(40,  leftParen).   % '('
isSpecial(41,  rightParen).  % ')'
isSpecial(123, leftBrace).   % '{'
isSpecial(125, rightBrace).  % '}'

% Alphabetic characters (lower case only).

isAlpha(X) :- 97 =< X, X =< 122.  % 'a'-'z'
isAlpha(95).                      % '_'

% Digits.

isDigit(X) :- 48 =< X, X =< 57.   % '0'-'9'

% ``Symbols''---allowed components of operator names.

isSymbol(X) :- member(X, "+-*/\^<>:=~.%|").

