IOConvertImpl.mesa
Copyright Ó 1985, 1986, 1987, 1990, 1991, 1992 by Xerox Corporation. All rights reserved.
MBrown on January 16, 1984 2:22 pm PST
Russ Atkinson (RRA) April 26, 1985 6:02:16 pm PST
Demers, November 6, 1990 5:40 pm PST
Doug Wyatt, August 21, 1991 5:50 pm PDT
Willie-sue, March 6, 1990 1:12 pm PST
Wes Irish, June 12, 1987 5:06:02 pm PDT
Carl Hauser, May 11, 1988 4:01:48 pm PDT
JKF August 26, 1988 10:05:42 am PDT
Christian Jacobi, July 25, 1990 8:25 pm PDT
Michael Plass, October 18, 1991 11:58 am PDT
DIRECTORY
Arpa USING [Address, nullAddress],
Ascii USING [caseOffset, Digit, Lower],
Atom USING [GetPName],
AtomPrivate USING [UnsafeMakeAtom],
Basics USING [LowHalf, UnsafeBlock],
BasicTime USING [DayOfWeek, GetZoneAndDST, GMT, minutesPerHour, MonthOfYear, Unpack, Unpacked, UnpackZ, unspecifiedZone, Zone, ZoneAndDST],
Convert USING [ErrorType, NetFormat, RealPrecision, TimePrecision],
DReal,
IO USING [card, char, DEL, Error, ErrorCode, GetIndex, GetTime, GetUnpackedTime, NUL, PutChar, PutFLR, PutFR, RIS, SP, STREAM],
PreDebug USING [RegisterErrorExplainer],
Real USING [DefaultSinglePrecision, Exception, ExceptionFlags, Extended, MaxSinglePrecision, NumberType, PairToReal, RealException, RealToPair],
RefText USING [Append, AppendChar, AppendRope, AppendTextRope, ObtainScratch, ReleaseScratch, ReserveChars, TrustTextRopeAsText],
Rope USING [ActionType, Concat, Equal, Fetch, FetchType, FromProc, FromRefText, Index, Length, Map, MapType, MaxLen, MoveType, ROPE, Substr, Text],
RuntimeError USING [BoundsFault],
XNS USING [Address, broadcastHost, broadcastNet, GetThisHost, Host, Net, Socket, unknownAddress, unknownNet, unknownSocket];
Parsing Cedar literals
CardFromDecimalLiteral:
PUBLIC
PROC [r:
ROPE, start:
INT]
RETURNS [
CARD] = {
Accepts whitespaceChar...num?((d|D)?num)?whitespaceChar
Note: does not accept a plus or minus sign
rLength: INT = r.Length[];
digitSeen: BOOL ¬ FALSE;
num: CARD ¬ 0;
decBase: CARD = 10D;
FOR i:
INT
IN [start..rLength)
DO
c: CHAR;
SELECT (c ¬ r.Fetch[i])
FROM
IN ['0..'9] => {
IF num >
CARD[(
LCLAST-(decBase-1))]/decBase
AND
num > (
LCLAST-
LOOPHOLE[(c.
ORD-'0.
ORD),
CARDINAL])/decBase
THEN
ERROR Error[$overflow, i];
num ¬ num * decBase + CARD[c.ORD-'0.ORD];
digitSeen ¬ TRUE;
};
'D, 'd => {
IF NOT digitSeen THEN ERROR Error[$syntax, i];
IF i < rLength THEN num ¬ ParseScaleFactor[decBase, num, r, i];
RETURN [num];
};
IN [IO.NUL..IO.SP] => IF digitSeen THEN EXIT;
ENDCASE => ERROR Error[$syntax, i];
ENDLOOP;
IF NOT digitSeen THEN ERROR Error[$empty, rLength-1];
RETURN [num];
};
LCLAST: CARD = LAST[CARD];
ParseScaleFactor:
PROC [base:
NAT, accum:
CARD, r:
ROPE, index:
INT]
RETURNS [
CARD] = {
bound: CARD = LCLAST/CARD[base];
decBase: CARD = 10D;
scale: CARDINAL ¬ 0;
c: CHAR;
FOR i:
INT
IN [index+1..r.Length[])
DO
IF (c ¬ r.Fetch[i])
IN ['0..'9]
THEN {
IF scale > CARD[(LCLAST-(decBase-1))]/decBase THEN ERROR Error[$overflow, i];
scale ¬ scale * 10 + CARD[c.ORD-'0.ORD];
}
ELSE IF c IN [IO.NUL..IO.SP] THEN EXIT
ELSE ERROR Error[$syntax, i];
ENDLOOP;
UNTIL scale = 0
DO
IF accum > bound THEN ERROR Error[$overflow, r.Length[]-1];
accum ¬ accum * base; scale ¬ scale - 1;
ENDLOOP;
RETURN [accum];
};
CardFromOctalLiteral:
PUBLIC
PROC [r:
ROPE, start:
INT]
RETURNS [
CARD] = {
Accepts whitespaceChar...num(b|B)?num?whitespaceChar
Note: does not accept a plus or minus sign, or a string of digits without radix char 'b
rLength: INT = r.Length[];
digitSeen: BOOL ¬ FALSE;
num: CARD ¬ 0;
FOR i:
INT
IN [start..rLength)
DO
c: CHAR;
octalBase: CARD = 10B;
SELECT (c ¬ r.Fetch[i])
FROM
IN ['0..'7] => {
IF num >
CARD[(
LCLAST-(octalBase-1))]/octalBase
AND
num > (
LCLAST-
LOOPHOLE[(c.
ORD-'0.
ORD),
CARDINAL])/octalBase
THEN
ERROR Error[$overflow, i];
num ¬ num * octalBase + CARD[c.ORD-'0.ORD];
digitSeen ¬ TRUE;
};
'B, 'b => {
IF NOT digitSeen THEN ERROR Error[$syntax, i];
IF i < rLength THEN num ¬ ParseScaleFactor[octalBase, num, r, i];
RETURN [num];
};
IN [IO.NUL..IO.SP] => IF digitSeen THEN ERROR Error[$syntax, i];
ENDCASE => ERROR Error[$syntax, i];
ENDLOOP;
ERROR Error[$empty, rLength-1];
};
CardFromHexLiteral:
PUBLIC PROC [r:
ROPE, start:
INT]
RETURNS [
CARD] = {
Accepts whitespaceChar...num(h|H)?num?whitespaceChar
Note: does not accept a plus or minus sign, or a string of digits without radix char 'h
rLength: INT = r.Length[];
digitSeen: BOOL ¬ FALSE;
num: CARD ¬ 0;
hexBase: CARD ¬ 10H;
FOR i:
INT
IN [start..rLength)
DO
c: CHAR;
SELECT (c ¬ Ascii.Lower[r.Fetch[i]])
FROM
IN ['0..'9] => {
IF num >
CARD[(
LCLAST-(hexBase-1))]/hexBase
AND
num > (
LCLAST-
LOOPHOLE[(c.
ORD-'0.
ORD),
CARDINAL])/hexBase
THEN
ERROR Error[$overflow, i];
num ¬ num * hexBase + CARD[c.ORD-'0.ORD];
digitSeen ¬ TRUE;
};
IN ['a..'f] => {
IF num >
CARD[(
LCLAST-(hexBase-1))]/hexBase
AND
num > (
LCLAST-
LOOPHOLE[c.
ORD-(('a).
ORD-10),
CARDINAL])/hexBase
THEN
ERROR Error[$overflow, i];
num ¬ num * hexBase + CARD[c.ORD-(('a).ORD-10)];
digitSeen ¬ TRUE;
};
'h => {
IF NOT digitSeen THEN ERROR Error[$syntax, i];
IF i < rLength THEN num ¬ ParseScaleFactor[hexBase, num, r, i];
RETURN [num];
};
IN [IO.NUL..IO.SP] => IF digitSeen THEN ERROR Error[$syntax, i];
ENDCASE => ERROR Error[$syntax, i];
ENDLOOP;
ERROR Error[$empty, rLength-1];
};
CardFromWholeNumberLiteral:
PUBLIC PROC [r:
ROPE, start:
INT]
RETURNS [
CARD] = {
rLength: INT = r.Length[];
radixChar: CHAR = FindRadix[r, start];
RETURN [(
SELECT radixChar
FROM
'd => CardFromDecimalLiteral,
'b => CardFromOctalLiteral,
'h => CardFromHexLiteral
ENDCASE => ERROR)[r, start]]
};
FindRadix:
PROC [r:
ROPE, start:
INT]
RETURNS [
CHAR] ~ {
rLength: INT = r.Length[];
radixChar: CHAR ¬ 'd;
FOR i:
INT
IN [start .. rLength)
DO
c: CHAR;
SELECT (c ¬ Ascii.Lower[r.Fetch[i]])
FROM
'b, 'd, 'h => radixChar ¬ c;
ENDCASE;
ENDLOOP;
RETURN [radixChar]
};
maxDINT: DCARD ~ 7FFFFFFFFFFFFFFFH; -- DINT.LAST
DIntFromRope:
PUBLIC
PROC [r:
ROPE]
RETURNS [
DINT] = {
rLength: INT = r.Length[];
start: INT ¬ 0;
neg: BOOL ¬ FALSE;
mag: DCARD ¬ 0;
WHILE start < rLength AND r.Fetch[start] IN['\000..' ] DO start ¬ start + 1 ENDLOOP;
IF start >= rLength THEN ERROR Error[$syntax, start];
SELECT r.Fetch[start]
FROM
'- => {neg ¬ TRUE; start ¬ start+1};
'+ => {start ¬ start+1};
ENDCASE;
mag ¬ DCardFromWholeNumberLiteral[r, start];
IF mag > maxDINT + ORD[neg] THEN Error[$overflow, rLength];
RETURN [IF neg THEN -mag ELSE mag];
};
DCardFromWholeNumberLiteral:
PUBLIC PROC [r:
ROPE, start:
INT]
RETURNS [
DCARD] = {
bug: should check for overflow.
rLength: INT = r.Length[];
radixChar: CHAR = FindRadix[r, start];
radix: CARD = SELECT radixChar FROM 'b => 8, 'h => 16, ENDCASE => 10;
value: DCARD ¬ 0;
WHILE start < rLength AND r.Fetch[start] IN['\000..' ] DO start ¬ start + 1 ENDLOOP;
IF start >= rLength THEN ERROR Error[$syntax, start];
FOR i:
INT
IN [start..rLength)
DO
c: CHAR = Ascii.Lower[r.Fetch[i]];
prev: DCARD ¬ value;
digit: CARD ¬ 0;
IF c = radixChar
THEN {
IF i+1 < rLength
THEN {
scale: CARD = CardFromWholeNumberLiteral[r, i+1];
value ¬ value * (radix**scale);
IF value < prev THEN Error[$overflow, i];
};
EXIT;
};
SELECT c
FROM
IN ['0..'9] => digit ¬ (c-'0);
IN ['a..'f] => digit ¬ (c-'a+10);
ENDCASE => Error[$syntax, i];
IF digit >= radix THEN Error[$syntax, i];
value ¬ (value * radix) + digit;
IF value < prev THEN Error[$overflow, i];
ENDLOOP;
RETURN [value]
};
RealFromLiteral:
PUBLIC
PROC [r:
ROPE, start:
INT]
RETURNS [
REAL] ~ {
Accepts both whitespaceChar...num exponent?whitespaceChar and whitespaceChar...?num.num?exponent?whitespaceChar, where exponent is (E|e)?(+|-)num
state: {begin, int, dot, frac, estart, esign, exp, error} ¬ begin;
index: INT ¬ start; -- index in rope of current char
fr: INT ¬ 0; -- mantissa
exp, adj: INTEGER ¬ 0; -- exponent and adjustment
eNeg: BOOL ¬ FALSE;
mDigits, eDigits: NAT ¬ 0; -- significant digits
MDigit:
PROC[c:
CHAR] ~ { d: [0..10) ~ c-'0;
IF state=frac THEN adj ¬ adj-1;
IF mDigits=0 AND d=0 THEN NULL -- leading zero
ELSE IF mDigits<9 THEN { fr ¬ fr*10+d; mDigits ¬ mDigits+1 }
ELSE { adj ¬ adj+1; IF mDigits=9 AND d>5 THEN fr ¬ fr+1; }; -- round if 10th digit >5
};
EDigit:
PROC[c:
CHAR] ~ { d: [0..10) ~ c-'0;
IF eDigits=0 AND d=0 THEN NULL -- leading zero
ELSE IF eDigits<3 THEN { exp ¬ exp*10+d; eDigits ¬ eDigits+1 }
ELSE ERROR Error[reason: $overflow, index: index];
};
action: Rope.ActionType ~ {
SELECT state
FROM
begin =>
SELECT c
FROM
IN['\000..' ] => NULL; -- leading white space
IN['0..'9] => { state ¬ int; MDigit[c] };
'. => state ¬ dot;
ENDCASE => GOTO Fail;
int =>
SELECT c
FROM
-- ?( + | - ) 0..9 | int 0..9
IN['0..'9] => MDigit[c];
'. => state ¬ frac;
'e, 'E => state ¬ estart;
IN['\000..' ] => RETURN[TRUE];
ENDCASE => GOTO Fail;
dot =>
SELECT c
FROM
-- ?( + | - ) .
IN['0..'9] => { state ¬ frac; MDigit[c] };
ENDCASE => GOTO Fail;
frac =>
SELECT c
FROM
-- int . | ?( + | - ) . 0..9 | frac 0..9
IN['0..'9] => MDigit[c];
'e, 'E => state ¬ estart;
IN['\000..' ] => RETURN[TRUE];
ENDCASE => GOTO Fail;
estart =>
SELECT c
FROM
-- ( int | frac ) ( E | e )
IN['0..'9] => { state ¬ exp; EDigit[c] };
'+, '- => { eNeg ¬ (c='-); state ¬ esign };
ENDCASE => GOTO Fail;
esign =>
SELECT c
FROM
-- ( int | frac ) ( E | e ) ( + | - )
IN['0..'9] => { state ¬ exp; EDigit[c] };
ENDCASE => GOTO Fail;
exp =>
SELECT c
FROM
-- ( int | frac ) ( E | e ) ?( + | - ) 0..9 | exp 0..9
IN['0..'9] => EDigit[c];
IN['\000..' ] => RETURN[TRUE];
ENDCASE => GOTO Fail;
ENDCASE => GOTO Fail;
index ¬ index+1;
EXITS Fail => { state ¬ error; RETURN[TRUE] };
};
[] ¬ Rope.Map[base: r, start: start, action: action];
SELECT state
FROM
int, frac, exp => NULL; -- ok
begin => ERROR Error[reason: $empty, index: index];
ENDCASE => ERROR Error[reason: $syntax, index: index];
IF eNeg THEN exp ¬ -exp;
RETURN[Real.PairToReal[fr: fr, exp10: exp+adj ! Real.RealException => RESUME]];
};
RopeFromLiteral:
PUBLIC
PROC [r:
ROPE, start:
INT]
RETURNS [
ROPE] = {
new: ROPE;
index: INT ¬ start + 1;
end: INT ¬ r.Length[]-1;
resultChars: INT ¬ 0;
escaped: BOOL ¬ FALSE;
getProc:
PROC
RETURNS [c:
CHAR] = {
IF index > end THEN RETURN ['\000];
c ¬ r.Fetch[index]; index ¬ index + 1;
SELECT c
FROM
'\\ => {
[c, index] ¬ ParseEscapeCode[r, index];
escaped ¬ TRUE };
'\" => {
IF r.Fetch[index] # '\" THEN ERROR Error[$syntax, index]; index ¬ index + 1;
escaped ¬ TRUE };
ENDCASE;
resultChars ¬ resultChars + 1;
};
IF r.Fetch[start] # '\" THEN ERROR Error[$syntax, start];
SELECT r.Fetch[end]
FROM
'l, 'L => {
end ¬ end - 2;
IF r.Fetch[end+1] # '\" THEN ERROR Error[$syntax, end];
};
'\" => end ¬ end - 1;
ENDCASE => ERROR Error[$syntax, end];
new ¬ Rope.FromProc[end - index + 1, getProc];
IF escaped THEN new ¬ new.Substr[0, resultChars];
RETURN [new];
};
CharFromLiteral:
PUBLIC
PROC [r:
ROPE, start:
INT]
RETURNS [
CHAR] = {
ENABLE RuntimeError.BoundsFault => ERROR Error[$syntax, start];
c: CHAR;
SELECT (c ¬ r.Fetch[start])
FROM
'\' => {
-- preferred form: ' extendedChar
IF (c ¬ r.Fetch[start ¬ start+1]) = '\\
THEN RETURN[ParseEscapeCode[r, start+1].c]
ELSE RETURN[c];
};
IN ['0..'7] => {
-- obsolete form: digit
!...C
num: INTEGER ¬ c.ORD-'0.ORD;
DO
SELECT (c ¬ r.Fetch[start ¬ start+1])
FROM
IN ['0..'7] => {
num ¬ num*10B + c.ORD-'0.ORD;
IF num > 377B THEN ERROR Error[$overflow, start];
};
'C, 'c => RETURN [VAL[num]];
ENDCASE => Error[$syntax, start];
ENDLOOP;
};
ENDCASE => ERROR Error[$syntax, start];
};
ParseEscapeCode:
PROC [r:
ROPE, start:
INT]
RETURNS [c:
CHAR, newIndex:
INT] = {
ENABLE RuntimeError.BoundsFault => ERROR Error[$syntax, start];
c ¬ r.Fetch[start];
SELECT Ascii.Lower[c]
FROM
'n => c ¬ '\n;
'r => c ¬ '\r;
't => c ¬ '\t;
'b => c ¬ '\b;
'f => c ¬ '\f;
'l => c ¬ '\l;
'\' => c ¬ '\';
'\" => c ¬ '\";
'\\ => c ¬ '\\;
IN ['0..'3] => {
num: INTEGER ¬ c.ORD-'0.ORD;
FOR i:
NAT
IN [1..2]
DO
SELECT (c ¬ r.Fetch[start+i])
FROM
IN ['0..'7] => num ¬ num*10B + c.ORD-'0.ORD;
ENDCASE => ERROR Error[$syntax, start+i];
ENDLOOP;
c ¬ VAL[num];
start ¬ start + 2;
};
ENDCASE => ERROR Error[$syntax, start];
start ¬ start + 1;
RETURN[c, start];
};
Printing
AppendInt:
PUBLIC
PROC [to:
REF
TEXT, from:
INT, base: Base, showRadix:
BOOL]
RETURNS [
REF
TEXT] = {
RETURN[AppendWholeNumber[to, LOOPHOLE[from.ABS], base, from<0, showRadix]];
};
AppendCard:
PUBLIC
PROC [to:
REF
TEXT, from:
CARD, base: Base ¬ 10, showRadix:
BOOL ¬
FALSE]
RETURNS [
REF
TEXT] = {
RETURN[AppendWholeNumber[to, from, base, FALSE, showRadix]];
};
AppendWholeNumber:
PROC [
to:
REF
TEXT, from:
CARD, base:
NAT, negative:
BOOL, showRadix:
BOOL]
RETURNS [
REF
TEXT] = {
stack: ARRAY [0..31] OF CHAR;
nChars: NAT ¬ 0;
IF showRadix
THEN {
SELECT base
FROM
10 => NULL;
8, 16 => { stack[0] ¬ IF base = 8 THEN 'B ELSE 'H; nChars ¬ 1 };
ENDCASE => ERROR Error[$invalidBase, 0];
}
ELSE IF base NOT IN [2..36] THEN ERROR Error[$invalidBase, 0];
DO
digit: NAT ¬ from MOD CARD[base];
stack[nChars] ¬ VAL[IF digit < 10 THEN '0.ORD + digit ELSE 'A.ORD + (digit-10)];
nChars ¬ nChars + 1;
IF from < CARD[base] THEN EXIT;
from ¬ from / CARD[base];
ENDLOOP;
IF negative
THEN {
stack[nChars] ¬ '-;
nChars ¬ nChars + 1;
};
to ¬ RefText.ReserveChars[to, nChars];
{
newLengthMinusOne: NAT = to.length + nChars - 1;
FOR i:
NAT
DECREASING
IN [0 .. nChars)
DO
to[newLengthMinusOne-i] ¬ stack[i];
ENDLOOP;
to.length ¬ newLengthMinusOne + 1;
};
RETURN[to];
};
AppendDCard:
PUBLIC
PROC [to:
REF
TEXT, from:
DCARD,
base: Base ¬ 10, showRadix:
BOOL ¬
TRUE
]
RETURNS [
REF
TEXT] ~ {
IF from <=
CARD.
LAST
THEN {RETURN [AppendCard[to, from, base, showRadix]]}
ELSE {
cbase: CARD ¬ base;
digit: CARD ¬ from MOD cbase;
to ¬ AppendDCard[to, from/cbase, base, FALSE];
RETURN [AppendCard[to, digit, base, showRadix]];
};
};
AppendReal:
PUBLIC
PROC [to:
REF
TEXT, from:
REAL, precision: RealPrecision, useE:
BOOL ¬
FALSE]
RETURNS [
REF
TEXT] =
TRUSTED {
IF useE
THEN RETURN [AppendE[to, from, precision]]
ELSE RETURN [AppendG[to, from, precision]];
};
<<
AppendDReal:
PUBLIC
PROC [to:
REF
TEXT, from:
DREAL, precision: [0..17], useE:
BOOL ¬
FALSE]
RETURNS [
REF
TEXT] ~
TRUSTED {
BUG: Ignores useE
gconvert:
UNSAFE
PROC [value:
DREAL, ndigit:
INT, trailing:
INT, buf:
POINTER
TO
CHAR] ~
UNCHECKED
MACHINE
CODE {
"+#define gconvert←help(v,n,t,b) (void)gconvert(*((double*) (&(v))),n,t,b)\n";
".gconvert←help"
};
fieldwidth: INT ¬ 7+precision;
i: NAT ¬ to.length;
hasDot: BOOL ¬ FALSE;
to ¬ RefText.ReserveChars[to, fieldwidth+6];
gconvert[value: from, ndigit: precision, trailing: 0, buf: LOOPHOLE[to, POINTER TO CHAR]+SIZE[TEXT[0]]+to.length];
UNTIL to[i]=0C DO IF to[i] = '. THEN hasDot ¬ TRUE; i ¬ i + 1 ENDLOOP;
to.length ¬ i;
IF NOT hasDot THEN to ¬ RefText.Append[to, ".0"];
RETURN [to]
};>>
AppendDecimalDigits:
PROC [to:
REF
TEXT, c:
CARD, nDigits: [0..9]]
RETURNS [
REF
TEXT] ~ {
to ¬ RefText.ReserveChars[to, nDigits];
FOR j:
NAT
DECREASING
IN [to.length..to.length+nDigits)
DO
to[j] ¬ '0 + (c MOD 10);
c ¬ c / 10;
ENDLOOP;
to.length ¬ to.length+nDigits;
RETURN [to]
};
AppendDReal:
PUBLIC
PROC [to:
REF
TEXT, from:
DREAL, precision: DRealPrecision, useE:
BOOL ¬
FALSE]
RETURNS [
REF
TEXT] ~
TRUSTED {
BUG: Ignores useE
type: Real.NumberType;
d: DINT;
exp10: INTEGER;
precision ¬ MAX[precision, 1];
[type, d, exp10] ¬ DReal.RealToPair[from, precision];
SELECT type
FROM
zero => { to ¬ RefText.Append[to, "0.0"] };
infinity => {
to ¬ RefText.AppendChar[to, IF from < 0.0 THEN '- ELSE '+];
to ¬ RefText.Append[to, "Inf"];
};
nan => {
to ¬ RefText.Append[to, "NaN"];
};
ENDCASE => {
IF d < 0 THEN { to ¬ RefText.AppendChar[to, '-]; d ¬ - d };
to ¬ RefText.Append[to, "0."];
IF precision > 9
THEN {
to ¬ AppendDecimalDigits[to, d / 1d9, precision-9];
to ¬ AppendDecimalDigits[to, d MOD 1d9, 9];
}
ELSE {
to ¬ AppendDecimalDigits[to, d, precision];
};
to ¬ RefText.AppendChar[to, 'e];
to ¬ AppendInt[to, exp10 + precision, 10, FALSE];
};
RETURN [to]
};
AppendTime:
PUBLIC
PROC [to:
REF
TEXT, from: BasicTime.
GMT, start, end: TimePrecision, includeDayOfWeek, useAMPM, includeZone:
BOOL]
RETURNS [
REF
TEXT] = {
RETURN[AppendUnpackedTime[
to, BasicTime.Unpack[from], start, end, includeDayOfWeek, useAMPM, includeZone]];
};
AppendUnpackedTime:
PUBLIC
PROC [to:
REF
TEXT, from: BasicTime.Unpacked, start, end: TimePrecision
, includeDayOfWeek, useAMPM, includeZone:
BOOL]
RETURNS [
REF
TEXT] = {
ok: PACKED ARRAY TimePrecision OF BOOL ¬ ALL[FALSE];
FOR p: TimePrecision IN TimePrecision[start..end] DO ok[p] ¬ TRUE ENDLOOP;
IF includeDayOfWeek
THEN {
to ¬ RefText.AppendTextRope[to, Day[from.weekday]];
IF ok # ALL[FALSE] THEN to ¬ RefText.AppendTextRope[to, ", "]
};
IF ok[months]
THEN {
to ¬ RefText.AppendTextRope[to, Month[from.month]];
IF ok[days] OR ok[years] OR ok[hours] THEN to ¬ RefText.AppendChar[to, ' ]
};
IF ok[days]
THEN {
to ¬ AppendCard[to, from.day, 10, FALSE];
IF ok[years] OR ok[hours] THEN to ¬ RefText.AppendTextRope[to, ", "]
};
IF ok[years]
THEN {
to ¬ AppendCard[to, from.year, 10, FALSE];
IF ok[hours] THEN to ¬ RefText.AppendChar[to, ' ]
};
IF ok[hours]
THEN {
to ¬ AppendCard[
to, IF useAMPM AND from.hour>12 THEN from.hour - 12 ELSE from.hour, 10, FALSE];
IF ok[minutes]
THEN {
to ¬ RefText.AppendChar[to, ':];
to ¬ Append2[to, from.minute];
IF ok[seconds]
THEN {
to ¬ RefText.AppendChar[to, ':];
to ¬ Append2[to, from.second];
};
};
IF useAMPM
THEN {
to ¬ RefText.AppendTextRope[to, IF from.hour >= 12 THEN " pm" ELSE " am"];
};
IF includeZone
THEN {
to ¬ RefText.AppendChar[to, ' ];
to ¬ AppendZone[to, from.zone, (from.dst=yes)];
};
};
RETURN[to];
};
AppendTimeRFC822:
PUBLIC
PROC [to:
REF
TEXT, from: BasicTime.
GMT, includeSeconds:
BOOL ¬
TRUE, includeDayOfWeek:
BOOL ¬
FALSE, useUT:
BOOL ¬
FALSE]
RETURNS [
REF
TEXT] = {
utz: BasicTime.ZoneAndDST ~ [zone: 0, beginDST: 366, endDST: 0];
z: BasicTime.ZoneAndDST ~ IF useUT THEN utz ELSE BasicTime.GetZoneAndDST[];
RETURN [AppendUnpackedTimeRFC822[to: to, from: BasicTime.UnpackZ[from, z], includeSeconds: includeSeconds, includeDayOfWeek: includeDayOfWeek]];
};
AppendUnpackedTimeRFC822:
PROC [to:
REF
TEXT, from: BasicTime.Unpacked, includeSeconds:
BOOL ¬
TRUE, includeDayOfWeek:
BOOL ¬
FALSE]
RETURNS [
REF
TEXT] = {
IF includeDayOfWeek
THEN {
to ¬ RefText.AppendTextRope[to: to, from: Day[from.weekday], len: 3];
to ¬ RefText.AppendTextRope[to, ", "];
};
to ¬ Append2[to, from.day];
to ¬ RefText.AppendChar[to, ' ];
to ¬ RefText.AppendTextRope[to: to, from: Month[from.month], len: 3];
to ¬ RefText.AppendChar[to, ' ];
to ¬ Append2[to, from.year MOD 100];
to ¬ RefText.AppendChar[to, ' ];
to ¬ Append2[to, from.hour];
to ¬ RefText.AppendChar[to, ':];
to ¬ Append2[to, from.minute];
IF includeSeconds
THEN {
to ¬ RefText.AppendChar[to, ':];
to ¬ Append2[to, from.second];
};
to ¬ RefText.AppendChar[to, ' ];
to ¬ AppendZone[to, from.zone, (from.dst=yes), TRUE];
RETURN[to];
};
MonthArray: TYPE ~ ARRAY BasicTime.MonthOfYear[January .. December] OF Rope.Text;
Month: REF MonthArray ~ NEW [MonthArray ¬ ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]];
DayArray: TYPE ~ ARRAY BasicTime.DayOfWeek[Monday .. Sunday] OF Rope.Text;
Day: REF DayArray ~ NEW [DayArray ¬ ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]];
Append2:
PROC [to:
REF
TEXT, from: [0..100)]
RETURNS [
REF
TEXT] ~ {
to ¬ RefText.AppendChar[to, '0+(from/10)];
to ¬ RefText.AppendChar[to, '0+(from MOD 10)];
RETURN [to];
};
AppendZone:
PROC [to:
REF
TEXT, zone: BasicTime.Zone, dst:
BOOL, useUT:
BOOL ¬
FALSE]
RETURNS [
REF
TEXT] ~ {
zoneRope: ROPE ¬ NIL;
metZone: BasicTime.Zone = -1*BasicTime.minutesPerHour;
jstZone: BasicTime.Zone = -9*BasicTime.minutesPerHour;
nZ: BasicTime.Zone ¬ zone;
SELECT zone
FROM
jstZone => zoneRope ¬ "JST";
metZone => zoneRope ¬ "MET";
0 => zoneRope ¬ IF useUT THEN "UT" ELSE IF dst THEN "BST" ELSE "GMT";
NAT[5*BasicTime.minutesPerHour] => zoneRope ¬ IF dst THEN "EDT" ELSE "EST";
NAT[6*BasicTime.minutesPerHour] => zoneRope ¬ IF dst THEN "CDT" ELSE "CST";
NAT[7*BasicTime.minutesPerHour] => zoneRope ¬ IF dst THEN "MDT" ELSE "MST";
NAT[8*BasicTime.minutesPerHour] => zoneRope ¬ IF dst THEN "PDT" ELSE "PST";
ENDCASE;
IF ( dst AND ( zone >= -720+BasicTime.minutesPerHour ) ) THEN
nZ ¬ zone - BasicTime.minutesPerHour;
IF zoneRope =
NIL
THEN zoneRope ¬
IO.PutFR["%g%02d%02d",
IO.char[IF zone < 0 THEN '- ELSE '+],
IO.card[zone.ABS/BasicTime.minutesPerHour],
IO.card[zone.ABS MOD BasicTime.minutesPerHour]
];
to ¬ RefText.AppendRope[to, zoneRope];
RETURN [to];
};
AppendBool:
PUBLIC PROC [to:
REF
TEXT, from:
BOOL]
RETURNS [
REF
TEXT] = {
RETURN [RefText.AppendTextRope[to, RopeFromBool[from]]];
};
AppendAtom:
PUBLIC PROC [to:
REF
TEXT, from:
ATOM, quote:
BOOL]
RETURNS [
REF
TEXT] = {
pName: Rope.Text = IF from = NIL THEN "<NIL>" ELSE Atom.GetPName[from];
IF quote
THEN {
CheckID[pName];
to ¬ RefText.AppendChar[to, '$];
};
RETURN [RefText.AppendTextRope[to, pName]];
};
CheckID:
PUBLIC PROC [id: Rope.Text] = {
FOR i:
INT
IN [0..id.Length[])
DO
SELECT id.Fetch[i]
FROM
IN ['0..'9] => IF i = 0 THEN ERROR Error[$unprintableAtom, 0];
IN ['A..'Z], IN ['a..'z] => NULL;
ENDCASE => ERROR Error[$unprintableAtom, 0];
ENDLOOP;
};
AppendRope:
PUBLIC
PROC [to:
REF
TEXT, from:
ROPE, quote:
BOOL ¬
TRUE]
RETURNS [
REF
TEXT] = {
IF quote THEN to ¬ RefText.AppendChar[to, '\"];
FOR i:
INT
IN [0 .. from.Length[])
DO
to ¬ AppendChar[to, Rope.Fetch[from, i], FALSE];
ENDLOOP;
IF quote THEN to ¬ RefText.AppendChar[to, '\"];
RETURN [to];
};
AppendChar:
PUBLIC
PROC [to:
REF
TEXT, from:
CHAR, quote:
BOOL ¬
TRUE]
RETURNS [
REF
TEXT] = {
IF quote THEN to ¬ RefText.AppendChar[to, '\'];
RETURN [
IF from
IN [
IO.
SP ..
IO.
DEL)
AND from # '\\
AND from # '\"
AND from # '\'
THEN
RefText.AppendChar[to, from]
ELSE AppendExtendedChar[to, from]
];
};
AppendExtendedChar:
PROC [to:
REF
TEXT, c:
CHAR]
RETURNS [
REF
TEXT] = {
IF c
IN [
IO.
SP ..
IO.
DEL)
THEN {
IF c = '\\ OR c = '\" OR c = '\' THEN to ¬ RefText.AppendChar[to, '\\];
to ¬ RefText.AppendChar[to, c];
}
ELSE {
to ¬ RefText.AppendChar[to, '\\];
SELECT c
FROM
'\n => to ¬ RefText.AppendChar[to, 'n];
'\t => to ¬ RefText.AppendChar[to, 't];
'\b => to ¬ RefText.AppendChar[to, 'b];
'\f => to ¬ RefText.AppendChar[to, 'f];
'\l => to ¬ RefText.AppendChar[to, 'l];
ENDCASE => {
to ¬ RefText.AppendChar[to, VAL['0.ORD + (c.ORD / 64)]];
to ¬ RefText.AppendChar[to, VAL['0.ORD + (c.ORD MOD 64 / 8)]];
to ¬ RefText.AppendChar[to, VAL['0.ORD + (c.ORD MOD 8)]]
} ;
};
RETURN [to];
};
RopeFromInt:
PUBLIC
PROC [from:
INT, base: Base, showRadix:
BOOL]
RETURNS [Rope.Text] = {
scratch: REF TEXT = RefText.ObtainScratch[100];
result: Rope.Text = Rope.FromRefText[AppendInt[scratch, from, base, showRadix]];
RefText.ReleaseScratch[scratch];
RETURN [result];
};
RopeFromCard:
PUBLIC PROC [from:
CARD, base: Base, showRadix:
BOOL]
RETURNS [Rope.Text] = {
scratch: REF TEXT = RefText.ObtainScratch[100];
result: Rope.Text = Rope.FromRefText[AppendCard[scratch, from, base, showRadix]];
RefText.ReleaseScratch[scratch];
RETURN [result];
};
RopeFromReal:
PUBLIC PROC [from:
REAL, precision: RealPrecision, useE: BOOL]
RETURNS [Rope.Text] = {
scratch: REF TEXT = RefText.ObtainScratch[100];
result: Rope.Text = Rope.FromRefText[AppendReal[scratch, from, precision, useE]];
RefText.ReleaseScratch[scratch];
RETURN [result];
};
RopeFromDReal:
PUBLIC PROC [from:
DREAL, precision: DRealPrecision, useE: BOOL]
RETURNS [Rope.Text] = {
scratch: REF TEXT = RefText.ObtainScratch[100];
result: Rope.Text = Rope.FromRefText[AppendDReal[scratch, from, precision, useE]];
RefText.ReleaseScratch[scratch];
RETURN [result];
};
RopeFromTime:
PUBLIC
PROC [from: BasicTime.
GMT, start, end: TimePrecision, includeDayOfWeek, useAMPM, includeZone:
BOOL]
RETURNS [Rope.Text] = {
scratch: REF TEXT = RefText.ObtainScratch[100];
result: Rope.Text = Rope.FromRefText[AppendUnpackedTime[scratch,
BasicTime.Unpack[from], start, end, includeDayOfWeek, useAMPM, includeZone]];
RefText.ReleaseScratch[scratch];
RETURN [result];
};
RopeFromUnpackedTime:
PUBLIC
PROC [from: BasicTime.Unpacked, start, end: TimePrecision, includeDayOfWeek, useAMPM, includeZone:
BOOL]
RETURNS [Rope.Text] = {
scratch: REF TEXT = RefText.ObtainScratch[100];
result: Rope.Text = Rope.FromRefText[AppendUnpackedTime[scratch,
from, start, end, includeDayOfWeek, useAMPM, includeZone]];
RefText.ReleaseScratch[scratch];
RETURN [result];
};
RopeFromTimeRFC822:
PUBLIC
PROC [from: BasicTime.
GMT, includeSeconds:
BOOL ¬
TRUE,
includeDayOfWeek:
BOOL ¬
FALSE, useUT:
BOOL ¬
FALSE]
RETURNS [Rope.Text] = {
scratch: REF TEXT = RefText.ObtainScratch[100];
result: Rope.Text = Rope.FromRefText[AppendTimeRFC822[to: scratch, from: from, includeSeconds: includeSeconds, includeDayOfWeek: includeDayOfWeek, useUT: useUT]];
RefText.ReleaseScratch[scratch];
RETURN [result];
};
RopeFromBool:
PUBLIC
PROC [from:
BOOL]
RETURNS [Rope.Text] = {
RETURN [IF from THEN "TRUE" ELSE "FALSE"];
};
RopeFromAtom:
PUBLIC
PROC [from:
ATOM, quote:
BOOL]
RETURNS [
ROPE] = {
pName: Rope.Text = IF from = NIL THEN "<NIL>" ELSE Atom.GetPName[from];
IF NOT quote THEN RETURN [pName];
CheckID[pName];
RETURN [Rope.Concat["$", pName]];
};
RopeFromRope:
PUBLIC
PROC [from:
ROPE, quote:
BOOL]
RETURNS [Rope.Text] = {
scratch: REF TEXT = RefText.ObtainScratch[from.Length[]+10];
result: Rope.Text = Rope.FromRefText[AppendRope[scratch, from, quote]];
RefText.ReleaseScratch[scratch];
RETURN [result];
};
RopeFromChar:
PUBLIC
PROC [from:
CHAR, quote:
BOOL]
RETURNS [Rope.Text] = {
scratch: REF TEXT = RefText.ObtainScratch[5];
result: Rope.Text = Rope.FromRefText[AppendChar[scratch, from, quote]];
RefText.ReleaseScratch[scratch];
RETURN [result];
};
Real formatting
bufferSize: NAT = Real.MaxSinglePrecision*2 + 8;
FtoRope:
PUBLIC
PROC [r:
REAL, afterDot: [0..Real.MaxSinglePrecision]]
RETURNS [rope:
ROPE] = {
scratch: REF TEXT ¬ RefText.ObtainScratch[bufferSize];
rope ¬ Rope.FromRefText[AppendF[scratch, r, afterDot]];
RefText.ReleaseScratch[scratch];
};
AppendF:
PUBLIC
PROC [buffer:
REF
TEXT, r:
REAL, afterDot: [0..Real.MaxSinglePrecision]]
RETURNS [
REF
TEXT] = {
nType: Real.NumberType;
{normal, zero, infinity, nan}
fract: INT;
exp10: INTEGER;
places: INTEGER ¬ afterDot;
[nType, fract, exp10] ¬ Real.RealToPair[r, Real.MaxSinglePrecision];
SELECT nType
FROM
normal => {
fudge: REAL ¬ Real.PairToReal[5, -places-1];
abs: REAL ¬ ABS[r];
IF abs < fudge
THEN {
The answer is small enough to be considered 0.
buffer ¬ RefText.AppendChar[buffer, '0];
GO TO restZero};
IF fract < 0
THEN {
buffer ¬ RefText.AppendChar[buffer, '-];
fract ¬ -fract;
};
{
This is the more normal case. The trick here is to get the proper # of digits after the decimal point.
temp: REF TEXT ¬ RefText.ObtainScratch[afterDot+Real.MaxSinglePrecision+4];
tpos: NAT ¬ 0;
card: CARD ¬ fract;
error: {up, none, down} ¬ none;
First, adjust the number to be "aligned" with the buffer
DO
SELECT places+exp10
FROM
< 0 => {
card has too much precision, so eliminate one digit
thisDigit: [0..10) ¬ LowHalf[card MOD 10];
card ¬ card/10;
exp10 ¬ exp10 + 1;
SELECT thisDigit
FROM
= 0 => {};
> 5 => {card ¬ card + 1; error ¬ up};
= 5 => IF error = up THEN error ¬ down ELSE {card ¬ card + 1; error ¬ up};
ENDCASE => error ¬ down;
};
> 0 => {
fract has too little precision, so accum a '0
temp[tpos] ¬ '0;
tpos ¬ tpos + 1;
places ¬ places - 1;
IF places = 0 THEN {temp[tpos] ¬ '.; tpos ¬ tpos + 1};
};
ENDCASE => EXIT;
ENDLOOP;
Next, put chars into the temp buffer in reverse order (the convenient order when peeling them off of a number).
DO
temp[tpos] ¬ '0 + LowHalf[card MOD 10];
tpos ¬ tpos + 1;
card ¬ card / 10;
exp10 ¬ exp10 + 1;
places ¬ places - 1;
IF places = 0 THEN {temp[tpos] ¬ '.; tpos ¬ tpos + 1};
IF card = 0 AND exp10 > 0 THEN EXIT;
ENDLOOP;
Finally, append the temporary buffer onto the output buffer
WHILE tpos > 0
DO
buffer ¬ RefText.AppendChar[buffer, temp[tpos ¬ tpos - 1]];
ENDLOOP;
RefText.ReleaseScratch[temp];
};
};
zero => {
buffer ¬ RefText.AppendChar[buffer, '0];
GO TO restZero;
};
infinity =>
buffer ¬ RefText.AppendRope[buffer, IF fract < 0 THEN "-Inf" ELSE "+Inf"];
nan => {
buffer ¬ RefText.AppendChar[buffer, '*];
buffer ¬ RefText.AppendChar[buffer, '.];
buffer ¬ AppendChars[buffer, afterDot, '*];
};
ENDCASE => ERROR;
RETURN [buffer];
EXITS
restZero => {
IF afterDot > 0
THEN {
buffer ¬ RefText.AppendChar[buffer, '.];
buffer ¬ AppendChars[buffer, afterDot, '0]};
RETURN [buffer];
};
};
EtoRope:
PUBLIC
PROC [r:
REAL, afterDot: [0..Real.MaxSinglePrecision]]
RETURNS [rope:
ROPE] = {
scratch: REF TEXT ¬ RefText.ObtainScratch[bufferSize];
rope ¬ Rope.FromRefText[AppendE[scratch, r, afterDot]];
RefText.ReleaseScratch[scratch];
};
AppendE:
PUBLIC
PROC [buffer:
REF
TEXT, r:
REAL, precision: [0..Real.MaxSinglePrecision]]
RETURNS [
REF
TEXT] = {
nType: Real.NumberType;
{normal, zero, infinity, nan}
fract: INT;
exp10: INTEGER;
IF precision < 1 THEN precision ¬ 1;
[nType, fract, exp10] ¬ Real.RealToPair[r, precision];
SELECT nType
FROM
normal => {
abs: REAL ¬ ABS[r];
lag: CARD ¬ 1;
card: CARD ¬ ABS[fract];
pos: NAT ¬ 0;
IF r < 0.0 THEN buffer ¬ RefText.AppendChar[buffer, '-];
pos ¬ buffer.length;
buffer ¬ RefText.AppendChar[buffer, '.];
buffer ¬ AppendWholeNumber[buffer, card, 10, FALSE, FALSE];
buffer[pos] ¬ buffer[pos+1];
buffer[pos+1] ¬ '.;
buffer ¬ RefText.AppendChar[buffer, 'e];
exp10 ¬ exp10 + precision - 1;
buffer ¬ RefText.AppendChar[buffer, IF exp10 < 0 THEN '- ELSE '+];
buffer ¬ AppendWholeNumber[buffer, ABS[exp10], 10, FALSE, FALSE];
};
ENDCASE => buffer ¬ AppendF[buffer, r, 1];
RETURN [buffer];
};
GtoRope:
PUBLIC PROC [r:
REAL, precision: [0..Real.MaxSinglePrecision]]
RETURNS [rope:
ROPE] = {
scratch: REF TEXT ¬ RefText.ObtainScratch[bufferSize];
rope ¬ Rope.FromRefText[AppendG[scratch, r, precision]];
RefText.ReleaseScratch[scratch];
};
AppendG:
PUBLIC PROC [buffer:
REF
TEXT, r:
REAL, precision: [0..Real.MaxSinglePrecision]]
RETURNS [
REF
TEXT] = {
nType: Real.NumberType;
{normal, zero, infinity, nan}
fract: INT;
exp10: INTEGER;
IF precision < 1 THEN precision ¬ 1;
[nType, fract, exp10] ¬ Real.RealToPair[r, precision];
SELECT nType
FROM
normal => {
places: NAT ¬ precision;
IF fract < 0
THEN {
buffer ¬ RefText.AppendChar[buffer, '-];
fract ¬ ABS[fract];
r ¬ ABS[r];
};
IF exp10 > 0
THEN {
Too large for F format, so use E format
buffer ¬ AppendE[buffer, r, precision];
GO TO done};
Remove extra 0s to determine whether we can use F format
WHILE LowHalf[fract
MOD 10] = 0
DO
places ¬ places - 1;
exp10 ¬ exp10 + 1;
fract ¬ fract / 10;
ENDLOOP;
IF exp10 >= 0
THEN {
Can use F format, and only show 1 0 (to show that it's real)
buffer ¬ AppendF[buffer, r, 1];
GO TO done};
IF -exp10 <= precision
THEN {
Can use F format, but use places determined by the exponent
buffer ¬ AppendF[buffer, r, -exp10];
GO TO done};
Sigh, default to using E format
buffer ¬ AppendE[buffer, r, MAX[places, 2]];
EXITS done => {};
};
ENDCASE => buffer ¬ AppendF[buffer, r, 1];
RETURN [buffer];
};
PutChars:
PROC [st:
STREAM, chars:
NAT, char:
CHAR] = {
THROUGH [0..chars) DO IO.PutChar[st, char]; ENDLOOP;
};
AppendChars:
PROC [text:
REF
TEXT, chars:
NAT, char:
CHAR]
RETURNS [
REF
TEXT] = {
THROUGH [0..chars) DO text ¬ RefText.AppendChar[text, char]; ENDLOOP;
RETURN [text];
};
XNS Network Address Formatting
XNSFieldType:
TYPE = { default, broadcast, octal, decimal, hex, notANumber };
The order in which these possibilities are specified is important: receiving more information (i.e. looking at more characters of the field) may cause a computed XNSFieldType to increase (e.g. octal->decimal->hex) but cannot cause it to decrease. See GetXNSFieldType[...].
XNSAddressFromRope:
PUBLIC
PROC [r:
ROPE]
RETURNS [
XNS.Address] ~ {
nameLen: INT ~ Rope.Length[r];
answer: XNS.Address ¬ XNS.unknownAddress;
netPart, hostPart, socketPart: ROPE ¬ NIL;
netType, hostType, socketType: XNSFieldType ¬ default;
Split name into pieces.
BEGIN
firstDot, secondDot: INT;
firstDot ¬ Rope.Index[s1~r, s2~"."];
IF firstDot = nameLen
THEN
firstDot ¬ Rope.Index[s1~r, s2~"#"];
IF firstDot = nameLen
THEN {
One-part name.
hostPart ¬ r; hostType ¬ GetXNSFieldType[hostPart];
GOTO DoneSplit };
secondDot ¬ Rope.Index[s1~r, pos1~firstDot+1, s2~"."];
IF secondDot = nameLen
THEN
secondDot ¬ Rope.Index[s1~r, pos1~firstDot+1, s2~"#"];
IF secondDot = nameLen
THEN {
Two-part name.
part1: ROPE ~ Rope.Substr[base~r, len~firstDot];
part2: ROPE ~ Rope.Substr[base~r, start~firstDot+1];
type1: XNSFieldType ~ GetXNSFieldType[part1];
type2: XNSFieldType ~ GetXNSFieldType[part2];
SELECT
TRUE
FROM
(type1 = notANumber)
AND (type2 # notANumber) => {
hostPart ¬ part1; hostType ¬ type1;
socketPart ¬ part2; socketType ¬ type2 };
(type1 # notANumber)
AND (type2 = notANumber) => {
netPart ¬ part1; netType ¬ type1;
hostPart ¬ part2; hostType ¬ type2 };
ENDCASE => ERROR Error[reason~syntax, index~0];
GOTO DoneSplit };
{
Three-part name.
netPart ¬ Rope.Substr[base~r, len~firstDot];
netType ¬ GetXNSFieldType[netPart];
hostPart ¬ Rope.Substr[base~r, start~firstDot+1, len~secondDot-firstDot-1];
hostType ¬ GetXNSFieldType[hostPart];
socketPart ¬ Rope.Substr[base~r, start~secondDot+1];
socketType ¬ GetXNSFieldType[socketPart];
GOTO DoneSplit };
END;
SELECT hostType
FROM
default => ERROR Error[reason~syntax, index~0];
broadcast => answer.host ¬ XNS.broadcastHost;
octal, decimal, hex => answer.host ¬ CvtXNSHost[hostPart, hostType];
notANumber => {
IF Rope.Equal[hostPart, "ME"]
THEN answer.host ¬ XNS.GetThisHost[]
ELSE ERROR Error[reason~syntax, index~0];
};
ENDCASE => ERROR;
SELECT netType
FROM
default => answer.net ¬ DefaultXNSNet[];
broadcast => answer.net ¬ XNS.broadcastNet;
octal, decimal, hex => answer.net ¬ CvtXNSNet[netPart, netType];
notANumber => ERROR Error[reason~syntax, index~0];
ENDCASE => ERROR;
SELECT socketType
FROM
default => answer.socket ¬ XNS.unknownSocket;
broadcast => ERROR Error[reason~syntax, index~0];
octal, decimal, hex => answer.socket ¬ CvtXNSSocket[socketPart, socketType];
notANumber => ERROR Error[reason~syntax, index~0];
ENDCASE => ERROR;
RETURN [answer];
};
XNSNetFromRope:
PUBLIC PROC [r:
ROPE]
RETURNS [
XNS.Net] ~ {
type: XNSFieldType ~ GetXNSFieldType[r];
SELECT type
FROM
default => RETURN [DefaultXNSNet[]];
broadcast => RETURN [XNS.broadcastNet];
octal, decimal, hex => RETURN [CvtXNSNet[r, type]];
notANumber => ERROR Error[syntax, 0];
ENDCASE => ERROR;
};
XNSHostFromRope:
PUBLIC
PROC [r:
ROPE]
RETURNS [
XNS.Host] ~ {
type: XNSFieldType ~ GetXNSFieldType[r];
SELECT type
FROM
default => RETURN [XNS.GetThisHost[]];
broadcast => RETURN [XNS.broadcastHost];
octal, decimal, hex => RETURN [CvtXNSHost[r, type]];
notANumber => {
IF Rope.Equal[r, "ME"] THEN RETURN [XNS.GetThisHost[]];
ERROR Error[syntax, 0] };
ENDCASE => ERROR;
};
XNSSocketFromRope:
PUBLIC PROC [r:
ROPE]
RETURNS [
XNS.Socket] ~ {
type: XNSFieldType ~ GetXNSFieldType[r];
SELECT type
FROM
default => RETURN [XNS.unknownSocket];
broadcast => ERROR Error[syntax, 0];
octal, decimal, hex => RETURN [CvtXNSSocket[r, type]];
notANumber => ERROR Error[syntax, 0];
ENDCASE => ERROR;
};
DefaultXNSNet:
PROC
RETURNS [default:
XNS.Net ¬
XNS.unknownNet] ~ {
We define the default net to be the one at the head of the network chain. It may not be a good idea to block the caller of DefaultNet for so long, but it shouldn't happen (in theory).
<<netH: CommDriver.Network;
oneSecond: Process.Ticks ~ Process.SecondsToTicks[1];
FOR i:
NAT
IN [1..5]
DO
IF ((netH ¬ CommDriver.GetNetworkChain[]) #
NIL)
AND ((default ¬ netH.xns.net) #
XNS.unknownNet)
THEN EXIT;
Process.Pause[oneSecond];
ENDLOOP;>>
ERROR Error[empty, 0]; -- Not yet implemented - waiting on CommDriver.GetNetworkChain
};
bnSize: NAT ~ 6;
BigNum:
TYPE ~
ARRAY[0..bnSize)
OF
CARDINAL;
A BigNum a represents sum over i ( a[i] * 256**(5-i) ); that is, it's a base 256 number with the most significant part in a[0] and the least significant in a[5]. WARNING: Call a BigNum "normalized" if all the elements are in the range [0..256). The BigNum multiplication routine, MulAdd, always produces normalized results, but the division routine, DivRem, while mathematically correct, would leave its result unnormalized except for the explicit renormalization code. For legal network numbers, it can be proved that BigNum elements won't exceed LAST[LONG CARDINAL], so to speed thing up you could change the declaration of BigNum and delete the renormalization code. Yecch.
CvtXNSHost:
PROC [rope:
ROPE, type: XNSFieldType]
RETURNS [XNS.Host] ~ {
n: BigNum ~ CvtXNS[rope, type];
RETURN [ [a~n[0], b~n[1], c~n[2], d~n[3], e~n[4], f~n[5]] ] };
CvtXNSNet:
PROC [rope:
ROPE, type: XNSFieldType]
RETURNS [XNS.Net] ~ {
n: BigNum ~ CvtXNS[rope, type];
RETURN [ [hi~[hi~n[2], lo~n[3]], lo~[hi~n[4], lo~n[5]]] ] };
CvtXNSSocket:
PROC [rope:
ROPE, type: XNSFieldType]
RETURNS [XNS.Socket] ~ {
n: BigNum ~ CvtXNS[rope, type];
RETURN [ [hi~n[4], lo~n[5]] ] };
CvtXNS:
PROC [rope:
ROPE, type: XNSFieldType]
RETURNS [BigNum] ~ {
n: BigNum ¬ ALL [0];
base: CARDINAL;
len: CARDINAL;
c: CHAR;
MulAdd:
PROC [increment:
CARDINAL] ~ {
FOR i:
CARDINAL
DECREASING
IN [0..bnSize)
DO
temp: CARDINAL ¬ n[i] * base + increment;
n[i] ¬ temp MOD 100H;
increment ¬ temp / 100H;
ENDLOOP;
};
base ¬ SELECT type FROM octal => 8, decimal => 10, ENDCASE => 16;
IF (len ¬ Rope.Length[rope]) = 0 THEN ERROR;
c ¬ Rope.Fetch[rope, len-1];
SELECT
TRUE
FROM
(type = octal) AND ((c = 'B) OR (c = 'b)) => len ¬ len - 1;
(type = hex) AND ((c = 'H) OR (c = 'h)) => len ¬ len - 1;
ENDCASE => NULL;
FOR i:
CARDINAL
IN [0..len)
DO
SELECT (c ¬ Rope.Fetch[rope,i])
FROM
IN ['0..'9] => MulAdd[c - '0];
IN ['A..'F] => MulAdd[(c - 'A) + 10];
IN ['a..'f] => MulAdd[(c - 'a) + 10];
ENDCASE => NULL;
ENDLOOP;
RETURN [n];
};
GetXNSFieldType:
PROC [rope:
ROPE]
RETURNS [type: XNSFieldType] ~ {
limit: CARDINAL;
IF (limit ¬ Rope.Length[rope]) = 0 THEN RETURN [default];
limit ¬ limit - 1;
IF Rope.Equal[rope, "*"] THEN RETURN [broadcast];
type ¬ (
SELECT Rope.Fetch[rope, 0]
FROM
IN ['0 .. '7] => octal,
IN ['8 .. '9] => decimal,
ENDCASE => notANumber);
FOR i:
CARDINAL
IN [1 .. limit)
WHILE type < notANumber
DO
SELECT Rope.Fetch[rope, i]
FROM
IN ['0 .. '7] => type ¬ MAX[type, octal];
IN ['8 .. '9] => type ¬ MAX[type, decimal];
IN ['A .. 'F], IN ['a .. 'f] => type ¬ MAX[type, hex];
'- => type ¬ (IF type <= decimal THEN decimal ELSE notANumber);
ENDCASE => type ¬ notANumber;
ENDLOOP;
IF limit > 0
THEN
SELECT Rope.Fetch[rope, limit]
FROM
IN ['0 .. '9], 'D, 'd => type ¬ MAX[type, decimal];
'B, 'b => type ¬ MAX[type, octal];
IN ['A .. 'F], IN ['a .. 'f], 'H, 'h => type ¬ MAX[type, hex];
ENDCASE => type ¬ notANumber;
};
RopeFromXNSAddress:
PUBLIC
PROC [address:
XNS.Address,
format: NetFormat]
RETURNS [rope:
ROPE] ~ {
scratch: REF TEXT ¬ RefText.ObtainScratch[40];
rope ¬ Rope.FromRefText[scratch ¬ AppendXNSAddress[scratch, address, format]];
RefText.ReleaseScratch[scratch];
};
RopeFromXNSNet:
PUBLIC
PROC [net:
XNS.Net,
format: NetFormat]
RETURNS [rope:
ROPE] ~ {
scratch: REF TEXT ¬ RefText.ObtainScratch[20];
rope ¬ Rope.FromRefText[scratch ¬ AppendXNSNet[scratch, net, format]];
RefText.ReleaseScratch[scratch];
};
RopeFromXNSHost:
PUBLIC
PROC [host:
XNS.Host,
format: NetFormat]
RETURNS [rope:
ROPE] ~ {
scratch: REF TEXT ¬ RefText.ObtainScratch[20];
rope ¬ Rope.FromRefText[scratch ¬ AppendXNSHost[scratch, host, format]];
RefText.ReleaseScratch[scratch];
};
RopeFromXNSSocket:
PUBLIC
PROC [socket:
XNS.Socket,
format: NetFormat]
RETURNS [rope:
ROPE] ~ {
scratch: REF TEXT ¬ RefText.ObtainScratch[20];
rope ¬ Rope.FromRefText[scratch ¬ AppendXNSSocket[scratch, socket, format]];
RefText.ReleaseScratch[scratch];
};
AppendXNSAddress:
PUBLIC
PROC [to:
REF
TEXT, address:
XNS.Address,
format: NetFormat]
RETURNS [
REF
TEXT] ~ {
Returns a ROPE of the form "1.12.123".
An unknownSocket is expanded as the null string, e.g. "1.12." (no trailing 0).
IF address.net =
XNS.broadcastNet
THEN to ¬ RefText.AppendChar[to, '* ]
ELSE to ¬ FmtXNS[to, [0, 0, address.net.hi.hi, address.net.hi.lo, address.net.lo.hi, address.net.lo.lo], format];
to ¬ RefText.AppendChar[to, '. ];
IF address.host =
XNS.broadcastHost
THEN to ¬ RefText.AppendChar[to, '* ]
ELSE to ¬ FmtXNS[to, [address.host.a, address.host.b, address.host.c, address.host.d, address.host.e, address.host.f], format];
to ¬ RefText.AppendChar[to, '. ];
IF address.socket #
XNS.unknownSocket
THEN
to ¬ FmtXNS[to, [0, 0, 0, 0, address.socket.hi, address.socket.lo], format];
RETURN [to] };
AppendXNSNet:
PUBLIC PROC [to:
REF
TEXT, net:
XNS.Net,
format: NetFormat]
RETURNS [
REF
TEXT] ~ {
IF net =
XNS.broadcastNet
THEN to ¬ RefText.AppendChar[to, '* ]
ELSE to ¬ FmtXNS[to, [0, 0, net.hi.hi, net.hi.lo, net.lo.hi, net.lo.lo], format];
RETURN [to] };
AppendXNSHost:
PUBLIC PROC [to:
REF
TEXT, host:
XNS.Host,
format: NetFormat]
RETURNS [
REF
TEXT] ~ {
IF host =
XNS.broadcastHost
THEN to ¬ RefText.AppendChar[to, '* ]
ELSE to ¬ FmtXNS[to, [host.a, host.b, host.c, host.d, host.e, host.f], format];
RETURN [to] };
AppendXNSSocket:
PUBLIC PROC [to:
REF
TEXT, socket:
XNS.Socket,
format: NetFormat]
RETURNS [
REF
TEXT] ~ {
to ¬ FmtXNS[to, [0, 0, 0, 0, socket.hi, socket.lo], format];
RETURN [to] };
maxDigits: NAT ~ 24;
repChar: ARRAY [0 .. 16) OF CHAR ~ ['0, '1, '2, '3, '4, '5, '6, '7, '8, '9, 'A, 'B, 'C, 'D, 'E, 'F];
FmtXNS:
PROC [to:
REF
TEXT, n: BigNum, type: NetFormat]
RETURNS [
REF
TEXT] ~ {
text: REF TEXT;
base, rem: CARDINAL ¬ 0;
i: NAT;
isZero: BOOL;
DivRem:
PROC ~ {
[n, rem, isZero] ← [n/base, n MOD base, (n/base = 0)]
temp, carry: CARDINAL;
rem ¬ 0; isZero ¬ TRUE;
FOR j:
CARDINAL
IN [0 .. bnSize)
DO
temp ¬ n[j] + rem*0100H;
IF (n[j] ¬ temp / base) # 0 THEN isZero ¬ FALSE;
rem ¬ temp MOD base;
ENDLOOP;
n ← renormalize[n]:
carry ¬ 0;
FOR j:
NAT
DECREASING
IN [0 .. bnSize)
DO
temp ¬ n[j] + carry;
n[j] ¬ temp MOD 0100H;
carry ¬ temp / 0100H;
ENDLOOP;
};
text ¬ RefText.ObtainScratch[maxDigits];
text.length ¬ text.maxLength;
i ¬ text.length;
SELECT type
FROM
productSoftware => {
untilDash: NAT ¬ 3;
nDashes: NAT ¬ 0;
base ¬ 10;
isZero ¬ FALSE;
WHILE (
NOT isZero)
OR (nDashes = 0)
DO
[n, rem, isZero] ← [n/base, n MOD base, (n/base = 0)]
DivRem[];
IF untilDash = 0
THEN {
text[i ¬ i - 1] ¬ '-;
untilDash ¬ 3;
nDashes ¬ nDashes + 1 };
text[i ¬ i - 1] ¬ repChar[rem];
untilDash ¬ untilDash - 1;
ENDLOOP;
};
octal => {
base ¬ 8;
text[i ¬ i - 1] ¬ 'B;
isZero ¬ FALSE;
WHILE
NOT isZero
DO
[n, rem, isZero] ← [n/base, n MOD base, (n/base = 0)]
DivRem[];
text[i ¬ i - 1] ¬ repChar[rem];
ENDLOOP;
};
hex => {
base ¬ 16;
text[i ¬ i - 1] ¬ 'H;
isZero ¬ FALSE;
WHILE (
NOT isZero)
OR (rem >= 10)
DO
[n, rem, isZero] ← [n/base, n MOD base, (n/base = 0)]
DivRem[];
text[i ¬ i - 1] ¬ repChar[rem];
ENDLOOP;
};
ENDCASE => ERROR;
WHILE i < text.length
DO
to ¬ RefText.AppendChar[to, text[i]];
i ¬ i + 1;
ENDLOOP;
RefText.ReleaseScratch[text];
RETURN [to] };