MakeDoParsersImpl.Mesa
Copyright Ó 1991, 1992 by Xerox Corporation. All rights reserved.
Last tweaked by Mike Spreitzer on November 30, 1992 2:54 pm PST
Willie-s, May 13, 1992 12:32 pm PDT
Michael Plass, November 27, 1991 10:33 am PST
DIRECTORY BasicTime, FS, IO, IOClasses, MakeDo, MakeDoParsers, RefTab, Rope, SymTab;
MakeDoParsersImpl:
CEDAR
MONITOR
IMPORTS FS, IO, IOClasses, MakeDo, RefTab, Rope, SymTab
EXPORTS MakeDoParsers
=
BEGIN OPEN MakeDo, MakeDoParsers;
GotoSyntaxError: ERROR = CODE;
parses: RefTab.Ref ~ RefTab.Create[];
EnumerateWithSuffix:
PUBLIC
PROC [table: SymTab.Ref, suffix:
ROPE, consume:
PROC [
ROPE]] ~ {
Pass:
PROC [key:
ROPE, val:
REF
ANY]
RETURNS [quit:
BOOL ¬
FALSE]
--SymTab.EachPairAction-- ~ {
consume[key.Concat[suffix]];
RETURN [FALSE]};
IF table.Pairs[Pass] THEN ERROR;
RETURN};
FlushCache:
PUBLIC
ENTRY
PROC ~ {
ENABLE UNWIND => NULL;
parses.Erase[];
RETURN};
GetParseData:
PUBLIC
ENTRY
PROC [sourceNode: Node, sourceType: SourceType]
RETURNS [ParseData] ~ {
ENABLE UNWIND => NULL;
RETURN InnerGetParseData[sourceNode, sourceType]};
InnerGetParseData:
INTERNAL
PROC [sourceNode: Node, sourceType: SourceType]
RETURNS [pd: ParseData] ~ {
NoteDep: PROC [fileName: ROPE] = {NoteConfigDep[fileName, FALSE]};
NoteCDep: PROC [fileName: ROPE] = {NoteConfigDep[fileName, TRUE]};
NoteConfigDep:
PROC [fileName:
ROPE, isStaticRequest:
BOOL] = {
new: BOOL ~ SymTab.Store[IF isStaticRequest THEN pd.refdFiles ELSE pd.refdModules, fileName, $T];
IF new AND isStaticRequest THEN pd.refdFileSeq ← CONS[fileName, pd.refdFileSeq];
RETURN};
cur: Time;
exists: BOOL;
sourceName: ROPE;
pd ¬ NARROW[parses.Fetch[sourceNode].val];
IF pd=
NIL
THEN {
pd ¬
NEW [ParseDataRep ¬ [
source: sourceNode,
sourceType: sourceType,
refdModules: SymTab.Create[case: FALSE],
refdFiles: SymTab.Create[case: FALSE]
]];
IF NOT parses.Insert[sourceNode, pd] THEN ERROR};
IF sourceType # pd.sourceType THEN ERROR;
cur ¬ InnerGetCreated[sourceNode];
IF pd.stamp=cur THEN RETURN [IF pd.stamp#notExistTime THEN pd ELSE NIL];
pd.resultType ¬ Unknown;
pd.refdModules.Erase[];
pd.refdFiles.Erase[];
pd.refdFileSeq ← NIL;
pd.stamp ¬ cur;
IF pd.stamp=notExistTime THEN RETURN [NIL];
sourceName ¬ sourceNode.PublicPartsOfNode[].name;
SELECT pd.sourceType
FROM
Unknown => pd.resultType ¬ Unknown;
Mesa => {isDefs:
BOOL;
[exists, isDefs] ¬ EnumerateMesaDependancies[sourceName, NIL, NoteDep];
pd.resultType ¬ IF isDefs THEN MobOnly ELSE MobAndC};
Config => {
exists ¬ EnumerateConfigDependancies[sourceName, NIL, NoteConfigDep];
pd.resultType ¬ MobAndC};
PlainC => {
exists ¬ EnumerateCFileDependancies[sourceName, NoteCDep];
pd.resultType ¬ O};
Scheme => {
exists ¬ TRUE;
[] ¬ FS.FileInfo[sourceName !FS.Error => {exists ¬ FALSE; CONTINUE}];
pd.resultType ¬ SxCAndO};
Cluster => {
exists ¬ EnumerateClusterDependancies[sourceName, NoteCDep];
pd.resultType ¬ SxCAndO};
ENDCASE => ERROR;
IF NOT exists THEN {pd.resultType ¬ Unknown; RETURN [NIL]};
RETURN [pd]};
ClusterBreak:
PROC [char:
CHAR]
RETURNS [
IO.CharClass] ~ {
SELECT char
FROM
<= ' => RETURN [sepr];
'(, '), ';, '" => RETURN [break];
ENDCASE => RETURN [other]};
EnumerateClusterDependancies:
PROC [sourceName:
ROPE, consume:
PROC [fileName:
ROPE]]
RETURNS [exists:
BOOLEAN] ~ {
source: IO.STREAM ¬ NIL;
Token: TYPE ~ RECORD [k: {n, q} ¬ n, r: ROPE];
Equal:
PROC [t: Token, r:
ROPE]
RETURNS [
BOOL]
~ INLINE {RETURN [t.k=n AND t.r.Equal[r, FALSE]]};
GetToken:
PROC
RETURNS [Token] ~ {
DO
--until we've passed the comments
c: CHAR;
DO
--pass whitespace
c ¬ source.GetChar[];
IF NOT c IN [0C .. ' ] THEN EXIT;
ENDLOOP;
SELECT c
FROM
'( => RETURN [[n, "("]];
') => RETURN [[n, ")"]];
'; => [] ¬ source.GetLineRope[];
'" => {source.Backup[c]; RETURN [[q, source.GetRopeLiteral[]]]};
ENDCASE => {source.Backup[c]; RETURN [[n, source.GetTokenRope[ClusterBreak].token]]};
ENDLOOP;
};
ParseCluster:
PROC ~ {
op: Token ~ GetToken[];
decl: Token ~ GetToken[];
IF NOT (Equal[op, "("] AND Equal[decl, "declare-cluster"]) THEN GotoSyntaxError;
DO
next: Token ~ GetToken[];
SELECT
TRUE
FROM
Equal[next, ")"] => EXIT;
Equal[next, "("] => {
kind: Token ~ GetToken[];
xs:
RECORD [pre, post:
ROPE] ~
SELECT
TRUE
FROM
Equal[kind, "passive"] => [NIL, ".o"],
Equal[kind, "cedar"] => ["sun4/", ".c2c.o"],
Equal[kind, "cedar-sun4"] => ["sun4/", ".c2c.o"],
Equal[kind, "cedar-sun4o3"] => ["sun4-o3/", ".c2c.o"],
ENDCASE => ERROR GotoSyntaxError[];
DO
elt: Token ~ GetToken[];
SELECT
TRUE
FROM
elt.k=q => consume[Rope.Cat[xs.pre, elt.r, xs.post]];
Equal[elt, ")"] => EXIT;
ENDCASE => GotoSyntaxError;
ENDLOOP;
};
next.k=q => consume[next.r.Concat[".sx.o"]];
ENDCASE => GotoSyntaxError;
ENDLOOP;
RETURN};
source ¬ FS.StreamOpen[fileName: sourceName, accessOptions: $read !FS.Error => {source ¬ NIL; CONTINUE}];
exists ¬ source # NIL;
IF NOT exists THEN RETURN;
[] ¬ source.GetIndex[];
ParseCluster[!
GotoSyntaxError => {
SIGNAL Warning[IO.PutFR["Syntax error in %g; parse aborted at %g", IO.rope[sourceName], [integer[source.GetIndex[]]]]];
CONTINUE};
IO.EndOfStream => {
SIGNAL Warning[IO.PutFR["Premature end-of-file in %g; parse aborted at %g", IO.rope[sourceName], [integer[source.GetIndex[]]]]];
CONTINUE}];
source.Close[!IO.Error => CONTINUE];
RETURN};
EnumerateMesaDependancies:
PROC [sourceName, symTail:
ROPE, consume:
PROC [fileName:
ROPE]]
RETURNS [exists, isDefs:
BOOLEAN] =
BEGIN
Consume:
PROC [fileName:
ROPE] ~ {
consume[Rope.Concat[fileName, symTail]]; -- use .MOB --
RETURN;
};
NextToken:
PROC
RETURNS [
ROPE] =
{RETURN [source.GetTokenRope[Break].token]};
ParseDirectory:
PROC
RETURNS [next:
ROPE] =
BEGIN
firstClause: BOOL ¬ TRUE;
ParseClause:
PROC
RETURNS [next:
ROPE] =
BEGIN
fileName: ROPE ¬ "?";
next ¬ NextToken[];
IF firstClause
THEN
{firstClause ¬ FALSE; IF next.Equal[";"] THEN RETURN};
IF NOT Letter[next.Fetch[0]] THEN ERROR GotoSyntaxError;
fileName ¬ next;
next ¬ NextToken[];
IF next.Equal[":"]
THEN
BEGIN
next ¬ NextToken[];
IF next.Equal["TYPE"]
THEN {
next ¬ NextToken[];
IF Letter[next.Fetch[0]] AND NOT next.Equal["USING"] THEN next ¬ NextToken[];
}
ELSE
IF next.Equal["FROM"]
THEN {
fileName ¬ source.GetRopeLiteral[];
next ¬ NextToken[];
}
ELSE ERROR GotoSyntaxError;
END;
IF next.Equal["USING"]
THEN
BEGIN
IF NOT (next ¬ NextToken[]).Equal["["] THEN ERROR GotoSyntaxError;
WHILE NOT (next ¬ NextToken[]).Equal["]"] DO NULL ENDLOOP;
next ¬ NextToken[];
END;
Consume[fileName];
END;
IF (next ¬ NextToken[]).Equal["DIRECTORY"]
THEN
BEGIN
DO
IF (next ¬ ParseClause[]).Equal[";"] THEN EXIT;
IF NOT next.Equal[","] THEN ERROR GotoSyntaxError;
ENDLOOP;
next ¬ NextToken[];
END;
IF NOT Letter[next.Fetch[0]] THEN ERROR GotoSyntaxError;
END;
ParseMesaHead:
PROC
RETURNS [next:
ROPE] ~ {
next ¬ NextToken[];
IF NOT next.Equal[":"] THEN ERROR GotoSyntaxError;
next ¬ NextToken[];
IF next.Equal["CEDAR"]
THEN {
next ¬ NextToken[];
IF next.Equal["SAFE"]
OR next.Equal["UNSAFE"]
THEN next ¬ NextToken[]};
SELECT
TRUE
FROM
next.Equal["DEFINITIONS"] => isDefs ¬ TRUE;
next.Equal["PROGRAM"] => isDefs ¬ FALSE;
next.Equal["MONITOR"] => isDefs ¬ FALSE;
ENDCASE => ERROR GotoSyntaxError;
};
ParseDirectoryAndHead:
PROC ~ {
[] ¬ ParseDirectory[];
[] ¬ ParseMesaHead[];
RETURN};
source: IO.STREAM ¬ NIL;
source ¬ FS.StreamOpen[fileName: sourceName, accessOptions: $read !FS.Error => {source ¬ NIL; CONTINUE}];
exists ¬ source # NIL;
isDefs ¬ TRUE;
IF NOT exists THEN RETURN;
source ¬ IOClasses.CreateCommentFilterStream[source];
[] ¬ source.GetIndex[];
ParseDirectoryAndHead[!
GotoSyntaxError => {
SIGNAL Warning[IO.PutFR["Syntax error in %g; parse aborted at %g", IO.rope[sourceName], [integer[source.GetIndex[]]]]];
CONTINUE};
IO.EndOfStream => {
SIGNAL Warning[IO.PutFR["Premature end-of-file in %g; parse aborted at %g", IO.rope[sourceName], [integer[source.GetIndex[]]]]];
CONTINUE}];
source.Close[!IO.Error => CONTINUE];
RETURN;
END;
EnumerateConfigDependancies:
PROC [sourceName, symTail:
ROPE, consume:
PROC [fileName:
ROPE, isStaticRequest:
BOOL]]
RETURNS [exists:
BOOLEAN] = {
locals: LIST OF LIST OF ROPE ¬ NIL;
AddDef: PROC [name: ROPE] = {locals.first ¬ CONS[name, locals.first]};
MaybeConsume:
PROC [moduleName:
ROPE] = {
FOR l:
LIST
OF
LIST
OF
ROPE ¬ locals, l.rest
WHILE l #
NIL
DO
FOR m:
LIST
OF
ROPE ¬ l.first, m.rest
WHILE m #
NIL
DO
IF m.first.Equal[moduleName] THEN RETURN;
ENDLOOP;
ENDLOOP;
Consume[moduleName, FALSE]};
Consume:
PROC [fileName:
ROPE, isStatic:
BOOL] ~ {
consume[Rope.Concat[fileName, symTail], isStatic]; -- use .MOB --
RETURN;
};
ConsumeSimple:
PROC [fileName:
ROPE, isStatic:
BOOL] ~ {
consume[fileName, isStatic]; -- use fileName directly --
NULL;
};
NextToken: PROC RETURNS [ROPE] = {RETURN [source.GetTokenRope[Break].token]};
ParseConfigDescription:
PROC = {
next: ROPE ¬ ParseCDirectory[];
next ¬ ParseCPacking[next];
next ¬ ParseConfiguration[next];
IF NOT next.Equal["."] THEN ERROR GotoSyntaxError;
};
ParseConfiguration:
PROC [first:
ROPE]
RETURNS [next:
ROPE] = {
IF NOT Letter[first.Fetch[0]] THEN ERROR GotoSyntaxError;
IF NOT (next ¬ NextToken[]).Equal[":"] THEN ERROR GotoSyntaxError;
IF NOT IsConfig[next ¬ NextToken[]] THEN ERROR GotoSyntaxError;
next ¬ ParseConfigurationRemains[];
};
ParseConfigurationRemains:
PROC
RETURNS [next:
ROPE] = {
next ¬ ParseCHeadRemains[];
IF NOT (next.Equal["="] OR next.Equal["~"]) THEN ERROR GotoSyntaxError;
ParseCBody[];
next ¬ NextToken[];
};
ParseCBody:
PROC = {
next: ROPE ¬ NextToken[];
curly: BOOLEAN;
IF next.Equal["BEGIN"] THEN curly ¬ FALSE
ELSE IF next.Equal["{"] THEN curly ¬ TRUE
ELSE ERROR GotoSyntaxError;
locals ¬ CONS[NIL, locals];
next ¬ NextToken[];
DO
semiSeen: BOOL ¬ FALSE;
next ¬ ParseCStatement[next];
WHILE next.Equal[";"]
DO
next ¬ NextToken[];
semiSeen ¬ TRUE;
ENDLOOP;
IF next.Equal[IF curly THEN "}" ELSE "END"] THEN EXIT;
IF NOT semiSeen THEN ERROR GotoSyntaxError;
ENDLOOP;
locals ¬ locals.rest;
};
ParseCStatement:
PROC [first:
ROPE]
RETURNS [next:
ROPE] = {
next ¬ first;
IF next.Equal["["]
THEN
BEGIN
next ¬ ParseItemList[FALSE];
IF NOT next.Equal["]"] THEN ERROR GotoSyntaxError;
next ¬ NextToken[];
IF NOT next.Equal["←"] THEN ERROR GotoSyntaxError;
next ¬ ParseCExpression[];
END
ELSE
BEGIN
lhs, rhs: ROPE;
named: BOOL ¬ FALSE;
lhs ¬ rhs ¬ next;
IF named ¬ (next ¬ NextToken[]).Equal[":"]
THEN
BEGIN
IF IsConfig[rhs ¬ NextToken[]]
THEN {
AddDef[lhs];
next ¬ ParseConfigurationRemains[];
RETURN};
next ¬ NextToken[];
END;
IF next.Equal["←"]
THEN
BEGIN
AddDef[lhs];
next ¬ ParseCExpression[];
END
ELSE
BEGIN
IF named THEN AddDef[lhs];
MaybeConsume[rhs];
IF next.Equal["["]
THEN
BEGIN
IF NOT (next ¬ NextToken[]).Equal["]"] THEN next ¬ EatIDList[next, FALSE];
IF NOT next.Equal["]"] THEN ERROR GotoSyntaxError;
next ¬ ParseCLinks[];
END
ELSE
IF next.Equal["LINKS"]
THEN {
SIGNAL Warning[IO.PutFR["[] missing before %g in %g", IO.int[source.GetIndex[]], IO.rope[sourceName]]];
next ¬ ParseCLinks[next];
};
END;
END;
};
ParseCExpression:
PROC
RETURNS [next:
ROPE] = {
next ¬ ParseCRightSide[];
WHILE next.Equal["PLUS"]
DO
next ¬ ParseCRightSide[];
ENDLOOP;
WHILE next.Equal["THEN"]
DO
next ¬ ParseCRightSide[];
ENDLOOP;
};
ParseCRightSide:
PROC
RETURNS [next:
ROPE] = {
next ¬ ParseItem[NIL, TRUE];
IF next.Equal["["]
THEN
BEGIN
IF NOT (next ¬ NextToken[]).Equal["]"] THEN next ¬ EatIDList[next, FALSE];
IF NOT next.Equal["]"] THEN ERROR GotoSyntaxError;
next ¬ ParseCLinks[];
END;
};
ParseCHeadRemains:
PROC
RETURNS [next:
ROPE] = {
next ¬ ParseCLinks[];
next ¬ ParseImports[next];
next ¬ ParseCExports[next];
next ¬ ParseStatics[next];
next ¬ ParseDynamics[next];
next ¬ ParseControlClause[next];
};
ParseCLinks:
PROC [first:
ROPE ¬
NIL]
RETURNS [next:
ROPE] = {
next ¬ IF first # NIL THEN first ELSE NextToken[];
IF NOT next.Equal["LINKS"] THEN RETURN;
IF NOT (next ¬ NextToken[]).Equal[":"] THEN ERROR GotoSyntaxError;
next ¬ NextToken[];
IF NOT (next.Equal["CODE"] OR next.Equal["FRAME"]) THEN ERROR GotoSyntaxError;
next ¬ NextToken[];
};
ParseImports:
PROC [first:
ROPE]
RETURNS [next:
ROPE] = {
IF NOT first.Equal["IMPORTS"] THEN RETURN [first];
next ¬ ParseItemList[FALSE];
};
ParseCExports:
PROC [first:
ROPE]
RETURNS [next:
ROPE] = {
IF NOT first.Equal["EXPORTS"] THEN RETURN [first];
next ¬ ParseItemList[FALSE];
};
ParseStatics:
PROC [first:
ROPE]
RETURNS [next:
ROPE] = {
IF NOT first.Equal["STATIC"] THEN RETURN [first];
IF NOT (next ¬ NextToken[]).Equal["REQUESTS"] THEN ERROR GotoSyntaxError;
next ¬ ParseStringList[TRUE];
};
ParseDynamics:
PROC [first:
ROPE]
RETURNS [next:
ROPE] = {
IF NOT first.Equal["DYNAMIC"] THEN RETURN [first];
IF NOT (next ¬ NextToken[]).Equal["REQUESTS"] THEN ERROR GotoSyntaxError;
next ¬ ParseStringList[FALSE];
};
ParseControlClause:
PROC [first:
ROPE]
RETURNS [next:
ROPE] = {
IF NOT first.Equal["CONTROL"] THEN RETURN [first];
next ¬ EatIDList[NIL, FALSE];
};
ParseItemList:
PROC [notice:
BOOLEAN]
RETURNS [next:
ROPE] = {
DO
next ¬ ParseItem[NIL, notice];
IF NOT next.Equal[","] THEN EXIT;
ENDLOOP;
};
ParseItem:
PROC [first:
ROPE, notice:
BOOLEAN]
RETURNS [next:
ROPE] = {
lhs, rhs: ROPE;
named: BOOL ¬ FALSE;
lhs ¬ rhs ¬ IF first = NIL THEN NextToken[] ELSE first;
IF named ¬ (next ¬ NextToken[]).Equal[":"]
THEN
BEGIN
rhs ¬ NextToken[];
next ¬ NextToken[];
END;
IF notice
THEN {
IF named THEN AddDef[lhs];
MaybeConsume[rhs]};
};
ParseStringList:
PROC [notice:
BOOLEAN]
RETURNS [next:
ROPE] = {
DO
next ¬ ParseString[notice];
IF NOT next.Equal[","] THEN EXIT;
ENDLOOP;
};
ParseString:
PROC [notice:
BOOLEAN]
RETURNS [next:
ROPE] = {
string: ROPE;
string ¬ source.GetRopeLiteral[ !IO.Error => ERROR GotoSyntaxError];
next ¬ NextToken[];
IF notice THEN ConsumeSimple[string, TRUE];
};
ParseCDirectory:
PROC
RETURNS [next:
ROPE] = {
firstClause: BOOL ¬ TRUE;
ParseClause:
PROC
RETURNS [next:
ROPE] = {
moduleName: ROPE ¬ "?";
fileName: ROPE ¬ "?";
next ¬ NextToken[];
IF firstClause
THEN
{firstClause ¬ FALSE; IF next.Equal[";"] THEN RETURN};
IF NOT Letter[next.Fetch[0]] THEN ERROR GotoSyntaxError;
moduleName ¬ fileName ¬ next;
next ¬ NextToken[];
IF next.Equal[":"]
THEN
BEGIN
next ¬ NextToken[];
IF next.Equal["TYPE"]
THEN {
next ¬ NextToken[];
IF Letter[next.Fetch[0]] THEN next ¬ NextToken[];
}
ELSE
IF next.Equal["FROM"]
THEN {
fileName ¬ source.GetRopeLiteral[];
next ¬ NextToken[];
}
ELSE ERROR GotoSyntaxError;
END;
Consume[fileName, FALSE];
AddDef[moduleName];
};
locals ¬ CONS[NIL, locals];
IF NOT (next ¬ NextToken[]).Equal["DIRECTORY"] THEN RETURN;
DO
IF (next ¬ ParseClause[]).Equal[";"] THEN EXIT;
IF NOT next.Equal[","] THEN ERROR GotoSyntaxError;
ENDLOOP;
next ¬ NextToken[];
};
ParseCPacking:
PROC [first:
ROPE]
RETURNS [next:
ROPE] = {
next ¬ first;
WHILE next.Equal["PACK"]
DO
next ¬ EatIDList[NIL, FALSE];
IF NOT next.Equal[";"] THEN ERROR GotoSyntaxError;
next ¬ NextToken[];
ENDLOOP;
};
EatIDList:
PROC [first:
ROPE, notice:
BOOLEAN]
RETURNS [next:
ROPE] = {
next ¬ IF first = NIL THEN NextToken[] ELSE first;
DO
IF NOT Letter[next.Fetch[0]] THEN ERROR GotoSyntaxError;
IF notice THEN Consume[next, FALSE];
next ¬ NextToken[];
IF NOT next.Equal[","] THEN EXIT;
next ¬ NextToken[];
ENDLOOP;
};
source: IO.STREAM ¬ NIL;
source ¬ FS.StreamOpen[fileName: sourceName, accessOptions: $read !FS.Error => {source ¬ NIL; CONTINUE}];
exists ¬ source # NIL;
IF NOT exists THEN RETURN;
source ¬ IOClasses.CreateCommentFilterStream[source];
[] ¬ source.GetIndex[];
ParseConfigDescription[!
GotoSyntaxError => {
SIGNAL Warning[IO.PutFR["Syntax error in %g; parse aborted at %g", [rope[sourceName]], [integer[source.GetIndex[]]]]];
source.Close[];
GOTO Done
};
IO.EndOfStream => {
SIGNAL Warning[IO.PutFR["Premature end-of-file in %g; parse aborted at %g", IO.rope[sourceName], [integer[source.GetIndex[]]]]];
source.Close[];
GOTO Done
};
];
};
EnumerateCFileDependancies:
PROC [sourceName:
ROPE, consume:
PROC [fileName:
ROPE]]
RETURNS [exists:
BOOLEAN] ~ {
The following does NOT take into account all the /lib/cpp tricks that can actually be done.
Actually, maybe the correct way to do this is to send the file to /lib/cpp on a Unix system...
Actually, this would not work because files may not exist yet and /lib/cpp will complain.
Which takes us to comparing the real differences between the binding models of C and Mesa, but this is not the moment to discuss them
ParseCFile:
PROC = {
currentLine ← source.GetLineRope;
IF currentLine.... THEN currentLine ← currentLine.Cat[source.GetLineRope ! IO.EndOfStream => ERROR GotoSyntaxError];
--- repeat
if currentLine.Fetch[0] = SharpSign then
SkipOverBlanks
SkipToBlank
if inbetween is "include"
SkipOverBlanks - if not found here is SyntaxError --
GetSeparatorCharacter - too
SkipToSeparatorCharater - too
CONSUME;
REPEAT ALL
IncludeSep: ROPE ~ """<";
WhiteSpace: ROPE ~ " "; -- that is a tab and a blank --
WhiteSpaceOrSep: ROPE ~ "\t ""<";
SharpSign: CHAR ~ '#;
IncludeString: ROPE ~ "include";
Consume:
PROC [string:
ROPE, sepChar:
CHAR] ~ {
IF sepChar # '" THEN RETURN;
IF string.SkipTo[0, "/"] # string.Length[] THEN RETURN;
consume[string]; -- use name directly; it contains the extension --
};
WHILE
NOT
IO.EndOf[source]
DO
We exit the loop by finding the end of the source stream
currentLine: ROPE;
lineLength: INT;
currentLine ¬ IO.GetLineRope[source];
IF currentLine = NIL THEN LOOP;
lineLength ¬ currentLine.Length[];
WHILE currentLine.Fetch[lineLength-1] = '\\
DO
currentLine ¬ Rope.Concat[currentLine, IO.GetLineRope[source ! IO.EndOfStream => ERROR GotoSyntaxError]];
lineLength ¬ currentLine.Length[];
ENDLOOP;
IF currentLine.Fetch[0] =
SharpSign
THEN {
tokStart, tokEnd: INT;
tokStart ¬ currentLine.SkipOver[1, WhiteSpace];
IF tokStart = lineLength THEN LOOP;
tokEnd ¬ currentLine.SkipTo[tokStart+1, WhiteSpaceOrSep];
IF NOT Rope.Equal[currentLine.Substr[tokStart, tokEnd-tokStart], IncludeString] THEN LOOP;
BEGIN
ENABLE {IO.EndOfStream => ERROR GotoSyntaxError};
matchSep: ROPE;
sepChar: CHAR;
tokStart ¬ currentLine.SkipTo[tokEnd, IncludeSep];
sepChar ¬ currentLine.Fetch[tokStart];
matchSep ¬ IF sepChar = '" THEN """" ELSE ">";
tokEnd ¬ currentLine.SkipTo[tokStart+1, matchSep];
Consume[currentLine.Substr[tokStart+1, tokEnd-(1+tokStart)], sepChar];
END;
};
ENDLOOP;
};
source: IO.STREAM ¬ NIL;
source ¬ FS.StreamOpen[fileName: sourceName, accessOptions: $read !FS.Error => {source ¬ NIL; CONTINUE}];
exists ¬ source # NIL;
IF NOT exists THEN RETURN;
{
ParseCFile[!
GotoSyntaxError =>
{
SIGNAL Warning[
IO.PutFR1["Syntax error in %g", [rope[sourceName]]]];
There should be an indication of where the syntax error lies.
GOTO Done
};
IO.EndOfStream => {
SIGNAL Warning[IO.PutFR["Premature end-of-file in %g; parse aborted at %g", IO.rope[sourceName], [integer[source.GetIndex[]]]]];
GOTO Done
};
];
};
source.Close[];
};
IsConfig:
PROC [word:
ROPE]
RETURNS [is:
BOOLEAN] =
{is ¬ word.Equal["CONFIGURATION"] OR word.Equal["CONFIG"]};
Break:
PROC [char:
CHAR]
RETURNS [
IO.CharClass]
--IO.BreakProc-- = {
RETURN [
SELECT char
FROM
IN ['a .. 'z], IN ['A .. 'Z], IN ['0 .. '9] => other,
IO.SP, '\n, '\r, '\l, IO.TAB, IO.LF, IO.FF => sepr,
ENDCASE => break]};
Letter:
PROC [c:
CHAR]
RETURNS [letter:
BOOLEAN] =
INLINE
{letter ¬ (c IN ['a .. 'z]) OR (c IN ['A .. 'Z])};
END.