-- Copyright (c) 1991-2002, The Numerical Algorithms Group Ltd.
-- All rights reserved.
-- Copyright (C) 2007-2014, Gabriel Dos Reis.
-- All rights reserved.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions are
-- met:
--
-- - Redistributions of source code must retain the above copyright
-- notice, this list of conditions and the following disclaimer.
--
-- - Redistributions in binary form must reproduce the above copyright
-- notice, this list of conditions and the following disclaimer in
-- the documentation and/or other materials provided with the
-- distribution.
--
-- - Neither the name of The Numerical Algorithms Group Ltd. nor the
-- names of its contributors may be used to endorse or promote products
-- derived from this software without specific prior written permission.
--
-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
-- IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-- TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
-- PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
-- OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-- EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-- PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-- PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-- LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-- NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--
import tokens
import includer
namespace BOOTTRAN
module scanner
shoeTAB == abstractChar 9
dqUnit s==
a := [s]
[a,:a]
dqAppend(x,y)==
x = nil => y
y = nil => x
x.rest.rest := first y
x.rest := rest y
x
dqConcat ld==
ld = nil => nil
rest ld = nil => first ld
dqAppend(first ld,dqConcat rest ld)
dqToList s ==
s = nil => nil
first s
--%
structure %Lexer ==
Record(line: %String, pos: %Maybe %Short) with
lexerLineString == (.line)
lexerCurrentPosition == (.pos)
makeLexer() ==
mk%Lexer(nil,nil)
++ Return true if the lexer wants a fresh input line.
macro lexerRefresh? lex ==
lexerCurrentPosition lex = nil
macro lexerLineLength lex ==
#lexerLineString lex
++ Make the lexer ready to process a new input line.
lexerSetLine!(lex,line) ==
lexerLineString(lex) := line
lexerCurrentPosition(lex) := 0
++ Adjust the current position to the next non-blank character.
lexerSkipBlank! lex ==
lexerCurrentPosition(lex) :=
firstNonblankPosition(lexerLineString lex,lexerCurrentPosition lex)
++ Move the current position by a given amount
lexerAdvancePosition!(lex,n == 1) ==
lexerCurrentPosition(lex) := lexerCurrentPosition lex + n
++ Move the current position to end of line.
lexerSkipToEnd! lex ==
lexerCurrentPosition(lex) := lexerLineLength lex
++ Set the current position at a given index.
lexerPosition!(lex,k) ==
lexerCurrentPosition(lex) := k
++ Return the amount of space characters need to complete a tab
++ to its next logical stop.
lexerCharCountToCompleteTab lex ==
7 - (lexerCurrentPosition lex rem 8)
++ Access the character the current position.
macro lexerCurrentChar lex ==
stringChar(lexerLineString lex,lexerCurrentPosition lex)
++ Access the character at a given position.
macro lexerCharacterAt(lex,k) ==
stringChar(lexerLineString lex,k)
++ Return the position of next character `c', or end of line.
lexerCharPosition(lex,c) ==
charPosition(c,lexerLineString lex,lexerCurrentPosition lex)
++ Return true if the current position is at end of line.
lexerEol? lex ==
lexerCurrentPosition lex >= lexerLineLength lex
--%
lexerReadLisp lex ==
shoeReadLispString(lexerLineString lex,lexerCurrentPosition lex)
shoeNextLine(lex,s) ==
bStreamNull s => false
$linepos := s
[$f,:$r] := s
lexerSetLine!(lex,sourceLineString $f)
lexerSkipBlank! lex
lexerRefresh? lex => true
lexerCurrentChar lex = shoeTAB =>
a := makeString(lexerCharCountToCompleteTab lex,char " ")
lexerCurrentChar(lex) := char " "
lexerLineString(lex) := strconc(a,lexerLineString lex)
s1 := [makeSourceLine(lexerLineString lex,sourceLineNumber $f),:$r]
shoeNextLine(lex,s1)
true
shoeLineToks s ==
$f: local := nil
$r: local := nil
$floatok: local := true
$linepos: local := s
lex := makeLexer()
not shoeNextLine(lex,s) => [nil,:nil]
lexerRefresh? lex => shoeLineToks $r
lexerCharacterAt(lex,0) = char ")" =>
command := shoeLine? lexerLineString lex =>
dq := dqUnit makeToken($linepos,shoeLeafLine command,0)
[[dq],:$r]
command := shoeLisp? lexerLineString lex => shoeLispToken(lex,$r,command)
shoeLineToks $r
toks := []
while not lexerEol? lex repeat
toks := dqAppend(toks,shoeToken lex)
toks = nil => shoeLineToks $r
[[toks],:$r]
shoeLispToken(lex,s,string)==
if #string = 0 or stringChar(string,0) = char ";" then
string := '""
ln := lexerLineString lex
linepos := $linepos
[r,:st] := shoeAccumulateLines(lex,s,string)
dq := dqUnit makeToken(linepos,shoeLeafLisp st,0)
[[dq],:r]
shoeAccumulateLines(lex,s,string)==
not shoeNextLine(lex,s) => [s,:string]
lexerRefresh? lex => shoeAccumulateLines(lex,$r,string)
lexerLineLength lex = 0 => shoeAccumulateLines(lex,$r,string)
lexerCharacterAt(lex,0) = char ")" =>
command := shoeLisp? lexerLineString lex
command and #command > 0 =>
stringChar(command,0) = char ";" =>
shoeAccumulateLines(lex,$r,string)
a := findChar(char ";",command) =>
shoeAccumulateLines(lex,$r,
strconc(string,subString(command,0,a-1)))
shoeAccumulateLines(lex,$r,strconc(string,command))
shoeAccumulateLines(lex,$r,string)
[s,:string]
-- returns true if token t is closing `parenthesis'.
shoeCloser t ==
shoeKeyWord t in '(CPAREN CBRACK)
shoeToken lex ==
linepos := $linepos
n := lexerCurrentPosition lex
ch := lexerCurrentChar lex
b :=
shoeStartsComment lex =>
shoeComment lex
[]
shoeStartsNegComment lex =>
shoeNegComment lex
[]
ch = char "!" => shoeLispEscape lex
shoePunctuation codePoint ch => shoePunct lex
shoeStartsId ch => shoeWord(lex,false)
ch = char " " =>
shoeSpace lex
[]
ch = char "_"" => shoeString lex
digit? ch => shoeNumber lex
ch = char "__" => shoeEscape lex
ch = shoeTAB =>
lexerAdvancePosition! lex
[]
ch = char "&" => shoeInert lex
shoeError lex
b = nil => nil
dqUnit makeToken(linepos,b,n)
-- to pair badge and badgee
shoeLeafId x ==
["ID",makeSymbol x]
shoeLeafKey x==
["KEY",shoeKeyWord x]
shoeLeafInteger x==
["INTEGER",shoeIntValue x]
shoeLeafFloat(a,w,e)==
b:=shoeIntValue strconc(a,w)
c:= double b * EXPT(double 10, e-#w)
["FLOAT",c]
shoeLeafString x ==
["STRING",x]
shoeLeafLisp x ==
["LISP",x]
shoeLeafLispExp x ==
["LISPEXP",x]
shoeLeafLine x ==
["LINE",x]
shoeLeafComment x ==
["COMMENT", x]
shoeLeafNegComment x==
["NEGCOMMENT", x]
shoeLeafError x ==
["ERROR",x]
shoeLeafSpaces x ==
["SPACES",x]
shoeLispEscape lex ==
lexerAdvancePosition! lex
lexerEol? lex =>
SoftShoeError([$linepos,:lexerCurrentPosition lex],'"lisp escape error")
shoeLeafError lexerCurrentChar lex
a := lexerReadLisp lex
a = nil =>
SoftShoeError([$linepos,:lexerCurrentPosition lex],'"lisp escape error")
shoeLeafError lexerCurrentChar lex
[exp,n] := a
n = nil =>
lexerSkipToEnd! lex
shoeLeafLispExp exp
lexerPosition!(lex,n)
shoeLeafLispExp exp
shoeEscape lex ==
lexerAdvancePosition! lex
shoeEsc lex => shoeWord(lex,true)
nil
shoeEsc lex ==
lexerEol? lex =>
shoeNextLine(lex,$r) =>
while lexerRefresh? lex repeat
shoeNextLine(lex,$r)
shoeEsc lex
false
false
n1 := firstNonblankPosition(lexerLineString lex,lexerCurrentPosition lex)
n1 = nil =>
shoeNextLine(lex,$r)
while lexerRefresh? lex repeat
shoeNextLine(lex,$r)
shoeEsc lex
false
true
shoeStartsComment lex ==
not lexerEol? lex =>
lexerCurrentChar lex = char "+" =>
www := lexerCurrentPosition lex + 1
www >= lexerLineLength lex => false
lexerCharacterAt(lex,www) = char "+"
false
false
shoeStartsNegComment lex ==
not lexerEol? lex =>
lexerCurrentChar lex = char "-" =>
www := lexerCurrentPosition lex + 1
www >= lexerLineLength lex => false
lexerCharacterAt(lex,www) = char "-"
false
false
shoeNegComment lex ==
n := lexerCurrentPosition lex
lexerSkipToEnd! lex
shoeLeafNegComment subString(lexerLineString lex,n)
shoeComment lex ==
n := lexerCurrentPosition lex
lexerSkipToEnd! lex
shoeLeafComment subString(lexerLineString lex,n)
shoePunct lex ==
sss := shoeMatch lex
lexerAdvancePosition!(lex,#sss)
shoeKeyTr(lex,sss)
shoeKeyTr(lex,w) ==
shoeKeyWord w = "DOT" =>
$floatok => shoePossFloat(lex,w)
shoeLeafKey w
$floatok := not shoeCloser w
shoeLeafKey w
shoePossFloat(lex,w)==
lexerEol? lex or not digit? lexerCurrentChar lex => shoeLeafKey w
w := shoeInteger lex
shoeExponent(lex,'"0",w)
shoeSpace lex ==
n := lexerCurrentPosition lex
lexerSkipBlank! lex
$floatok := true
lexerRefresh? lex =>
shoeLeafSpaces 0
lexerSkipToEnd! lex
shoeLeafSpaces(lexerCurrentPosition lex - n)
shoeString lex ==
lexerAdvancePosition! lex
$floatok := false
shoeLeafString shoeS lex
shoeS lex ==
lexerEol? lex =>
SoftShoeError([$linepos,:lexerCurrentPosition lex],'"quote added")
'""
n := lexerCurrentPosition lex
strsym := lexerCharPosition(lex,char "_"")
escsym := lexerCharPosition(lex,char "__")
mn := MIN(strsym,escsym)
mn = lexerLineLength lex =>
lexerSkipToEnd! lex
SoftShoeError([$linepos,:lexerCurrentPosition lex],'"quote added")
subString(lexerLineString lex,n)
mn = strsym =>
lexerPosition!(lex,mn + 1)
subString(lexerLineString lex,n,mn-n)
str := subString(lexerLineString lex,n,mn-n)
lexerPosition!(lex,mn + 1)
a := shoeEsc lex
b :=
a =>
str := strconc(str,charString lexerCurrentChar lex)
lexerAdvancePosition! lex
shoeS lex
shoeS lex
strconc(str,b)
shoeIdEnd lex ==
n := lexerCurrentPosition lex
while n < lexerLineLength lex and shoeIdChar lexerCharacterAt(lex,n) repeat
n := n + 1
n
shoeW(lex,b) ==
n1 := lexerCurrentPosition lex
lexerAdvancePosition! lex
l := lexerLineLength lex
endid := shoeIdEnd lex
endid = l or lexerCharacterAt(lex,endid) ~= char "__" =>
lexerPosition!(lex,endid)
[b,subString(lexerLineString lex,n1,endid-n1)]
str := subString(lexerLineString lex,n1,endid-n1)
lexerPosition!(lex,endid + 1)
a := shoeEsc lex
bb :=
a => shoeW(lex,true)
[b,'""] -- escape finds space or newline
[bb.0 or b,strconc(str,bb.1)]
shoeWord(lex,esp) ==
aaa := shoeW(lex,false)
w:=aaa.1
$floatok:=false
esp or aaa.0 => shoeLeafId w
shoeKeyWordP w =>
$floatok:=true
shoeLeafKey w
shoeLeafId w
shoeInert lex ==
lexerAdvancePosition! lex
['INERT,second shoeW(lex,false)]
shoeInteger lex ==
shoeInteger1(lex,false)
shoeInteger1(lex,zro) ==
n := lexerCurrentPosition lex
while not lexerEol? lex and digit? lexerCurrentChar lex repeat
lexerAdvancePosition! lex
lexerEol? lex or lexerCurrentChar lex ~= char "__" =>
n = lexerCurrentPosition lex and zro => '"0"
subString(lexerLineString lex,n,lexerCurrentPosition lex - n)
str := subString(lexerLineString lex,n,lexerCurrentPosition lex - n)
lexerAdvancePosition! lex
a := shoeEsc lex
bb := shoeInteger1(lex,zro)
strconc(str,bb)
shoeIntValue(s) ==
ns := #s
ival := 0
for i in 0..ns-1 repeat
d := digit? stringChar(s,i)
ival := 10*ival + d
ival
shoeNumber lex ==
a := shoeInteger lex
lexerEol? lex => shoeLeafInteger a
$floatok and lexerCurrentChar lex = char "." =>
n := lexerCurrentPosition lex
lexerAdvancePosition! lex
not lexerEol? lex and lexerCurrentChar lex = char "." =>
lexerPosition!(lex,n)
shoeLeafInteger a
w := shoeInteger1(lex,true)
shoeExponent(lex,a,w)
shoeLeafInteger a
shoeExponent(lex,a,w)==
lexerEol? lex => shoeLeafFloat(a,w,0)
n := lexerCurrentPosition lex
c := lexerCurrentChar lex
c = char "E" or c = char "e" =>
lexerAdvancePosition! lex
lexerEol? lex =>
lexerPosition!(lex,n)
shoeLeafFloat(a,w,0)
digit? lexerCurrentChar lex =>
e := shoeInteger lex
e := shoeIntValue e
shoeLeafFloat(a,w,e)
c1 := lexerCurrentChar lex
c1 = char "+" or c1 = char "-" =>
lexerAdvancePosition! lex
lexerEol? lex =>
lexerPosition!(lex,n)
shoeLeafFloat(a,w,0)
digit? lexerCurrentChar lex =>
e := shoeInteger lex
e := shoeIntValue e
shoeLeafFloat(a,w,(c1 = char "-" => MINUS e; e))
lexerPosition!(lex,n)
shoeLeafFloat(a,w,0)
-- FIXME: Missing alternative.
shoeLeafFloat(a,w,0)
shoeError lex ==
n := lexerCurrentPosition lex
lexerAdvancePosition! lex
SoftShoeError([$linepos,:n],
strconc( '"The character whose number is ",
toString codePoint lexerCharacterAt(lex,n),
'" is not a Boot character"))
shoeLeafError lexerCharacterAt(lex,n)
shoeKeyWord st ==
tableValue(shoeKeyTable,st)
shoeKeyWordP st ==
tableValue(shoeKeyTable,st) ~= nil
shoeMatch lex ==
shoeSubStringMatch(lexerLineString lex,shoeDict,lexerCurrentPosition lex)
shoeSubStringMatch(l,d,i) ==
h := codePoint stringChar(l, i)
u := d.h
ll := #l
done := false
s1 := '""
for j in 0.. #u - 1 while not done repeat
s := u.j
ls := #s
done :=
ls + i > ll => false
eql := true
for k in 1..ls-1 while eql repeat
eql := stringChar(s,k) = stringChar(l,k+i)
eql =>
s1 := s
true
false
s1
shoePunctuation c ==
shoePun.c = 1