language-oberon-0.1.1: examples/AGRS/Grammars.Mod
MODULE Grammars;
IMPORT AGRS, Names, Library, Parser, Texts;
CONST
SetBits= MAX(SET)+1;
MaxStrLength*= 30;
TYPE
ParserProc= PROCEDURE;
ParserTerm= POINTER TO RECORD(AGRS.TermDesc)
handler: ParserProc;
END;
BuilderTerm= POINTER TO RECORD(AGRS.TermDesc)
property: AGRS.Name;
END;
GenericBuilderTerm= POINTER TO RECORD(AGRS.TermDesc)
END;
SetLimitTerm= POINTER TO RECORD(AGRS.TermDesc)
limit: LONGINT;
END;
ReaderProc= PROCEDURE(VAR in: CHAR);
VAR
Read*: ReaderProc;
BackSpace*: PROCEDURE;
input: Texts.Reader;
inputString: Names.CharPtr;
inputText: Texts.Text;
position,limit: LONGINT;
EmptyMarker: AGRS.Atomic;
spaceChars,idChars: Library.CharSet;
i: INTEGER;
temp: AGRS.SubTerm;
tempDis: AGRS.Disjunction;
disjunctionBuilder,continuationBuilder: AGRS.SystemTerm;
fieldBuilder,blockBuilder: AGRS.SystemTerm;
nameToTreeBuilder: AGRS.SystemTerm;
localConsName,classConsName: AGRS.Name;
grammarName*,followName: AGRS.Name;
treeName*,propertyName*,valueName*,rootName*: AGRS.Name;
sentenceName*,genericRootName,emptyName: AGRS.Name;
spaceCharsName,idCharsName: AGRS.Name;
genericAttrName*,disjunctionName*,continuationName*: AGRS.Name;
fieldName*,blockName*: AGRS.Name;
optionName*: AGRS.Name;
terminalName*,attrName*,constructName*,parseEndName: AGRS.Name;
parseName*,defaultGrammarName: AGRS.Name;
charParser*,stringParser*: AGRS.Name;
charTerminalParser*,stringTerminalParser*: AGRS.Name;
spaceParser,nameParser*,idParser*,recurrenceParser: AGRS.Name;
PROCEDURE ReadFile(VAR ch: CHAR);
BEGIN
REPEAT
Texts.Read(input, ch);
INC(position);
UNTIL input.elem=NIL;
END ReadFile;
PROCEDURE ReadString(VAR ch: CHAR);
BEGIN
ch:= inputString[position];
INC(position);
END ReadString;
PROCEDURE BackSpaceFile;
BEGIN
DEC(position);
Texts.OpenReader(input,inputText,position);
END BackSpaceFile;
PROCEDURE BackSpaceString;
BEGIN
DEC(position);
END BackSpaceString;
PROCEDURE SkipSpaces;
VAR
ch: CHAR;
spaces: AGRS.Term;
BEGIN
spaces:= spaceCharsName.Value();
WITH spaces: Library.CharSet DO
REPEAT
Read(ch);
UNTIL ~(ORD(ch) MOD SetBits IN
spaces.value[ORD(ch) DIV SetBits]) OR (position>limit);
BackSpace;
END;
END SkipSpaces;
PROCEDURE pSkipSpaces;
BEGIN
SkipSpaces;
AGRS.Continue;
END pSkipSpaces;
PROCEDURE ParseConstruct;
VAR
expect,skeleton: AGRS.Term;
newTerm: AGRS.Tree;
BEGIN
expect:= treeName.indirection;
skeleton:= rootName.indirection;
IF expect=AGRS.Variable THEN
NEW(newTerm);
WITH skeleton: AGRS.Tree DO
newTerm^:= skeleton^;
ELSE
newTerm.Init(skeleton.indirection);
END;
treeName.Assign(newTerm);
AGRS.Continue;
treeName.Restore;
ELSIF AGRS.Equal(expect,skeleton) THEN
AGRS.Continue;
ELSE
AGRS.Fail;
END;
END ParseConstruct;
PROCEDURE ParseGenericSkeleton(builder: AGRS.Term; check: BOOLEAN);
VAR
skeleton: AGRS.Class;
BEGIN
IF check & (treeName.indirection#AGRS.Variable) THEN
AGRS.Fail;
RETURN
END;
AGRS.Push(builder);
AGRS.Push(treeName);
NEW(skeleton);
skeleton.Init(genericRootName);
treeName.Assign(skeleton);
grammarName.Reduce;
treeName.Restore;
END ParseGenericSkeleton;
PROCEDURE ParseDisjunctConstruct;
BEGIN
ParseGenericSkeleton(disjunctionBuilder,TRUE);
END ParseDisjunctConstruct;
PROCEDURE ParseContinueConstruct;
BEGIN
ParseGenericSkeleton(continuationBuilder,TRUE);
END ParseContinueConstruct;
PROCEDURE ParseFieldConstruct;
BEGIN
ParseGenericSkeleton(fieldBuilder,TRUE);
END ParseFieldConstruct;
PROCEDURE ParseBlockConstruct;
BEGIN
Names.LocalBlock;
ParseGenericSkeleton(blockBuilder,TRUE);
Names.EndBlock;
END ParseBlockConstruct;
PROCEDURE ParseGenericAttribute;
VAR
builder: GenericBuilderTerm;
BEGIN
NEW(builder);
builder.Init(treeName.indirection);
ParseGenericSkeleton(builder,FALSE);
END ParseGenericAttribute;
PROCEDURE LocalConstruct;
VAR
newTerm: AGRS.Block;
BEGIN
IF treeName.indirection IS AGRS.Tree THEN
NEW(newTerm);
newTerm.Init(treeName.indirection.indirection);
treeName.Assign(newTerm);
AGRS.Continue;
treeName.Restore();
ELSE
AGRS.Fail;
END;
END LocalConstruct;
PROCEDURE ClassConstruct;
VAR
newTerm: AGRS.Class;
BEGIN
IF treeName.indirection IS AGRS.Tree THEN
NEW(newTerm);
newTerm.Init(treeName.indirection.indirection);
treeName.Assign(newTerm);
AGRS.Continue;
treeName.Restore();
ELSE
AGRS.Fail;
END;
END ClassConstruct;
PROCEDURE ParseAttribute;
VAR
prop,val: AGRS.Term;
root: AGRS.Name;
builder: BuilderTerm;
BEGIN
prop:= propertyName.indirection;
IF ~(prop IS AGRS.Name) THEN
prop:= prop.indirection;
END;
WITH prop: AGRS.Name DO
root:= treeName.indirection.indirection(AGRS.Name);
root.Assign(AGRS.Variable);
prop.Assign(EmptyMarker);
val:= treeName.Evaluate(prop);
prop.Restore;
root.Restore;
IF val=EmptyMarker THEN
val:= AGRS.Variable;
END;
NEW(builder);
builder.Init(treeName.indirection);
builder.property:= prop;
AGRS.Push(builder);
treeName.Assign(val);
grammarName.Reduce;
treeName.Restore;
END;
END ParseAttribute;
PROCEDURE ParseTerminal;
VAR
val: AGRS.Term;
builder: BuilderTerm;
BEGIN
NEW(builder);
builder.Init(treeName.indirection);
builder.property:= NIL;
AGRS.Push(builder);
grammarName.Reduce;
END ParseTerminal;
PROCEDURE ParseCharTerminal;
VAR
chRead: CHAR;
chTerm: AGRS.Term;
BEGIN
Read(chRead);
chTerm:= grammarName.Value();
IF chRead=chTerm(Library.Character).value THEN
AGRS.Continue;
ELSE
AGRS.Fail;
END;
END ParseCharTerminal;
PROCEDURE ParseChar;
VAR
chRead: CHAR;
chTerm: Library.Character;
BEGIN
Read(chRead);
chTerm:= Library.NewChar(chRead);
AGRS.Unify(treeName,chTerm);
END ParseChar;
PROCEDURE ParseTheStringPrim(termExpect: AGRS.Term);
VAR
strExpect: Names.CharPtr;
chRead: CHAR;
i: INTEGER;
BEGIN
IF ~(termExpect IS Library.String) THEN
AGRS.Fail;
RETURN
END;
strExpect:= termExpect(Library.String).value;
i:= 0;
WHILE (i<LEN(strExpect^)) & (strExpect[i]#0X) DO
Read(chRead);
IF strExpect[i]#chRead THEN
AGRS.Fail;
RETURN
END;
INC(i);
END;
IF strExpect[i]=0X THEN
AGRS.Continue;
ELSE
AGRS.Fail;
END;
END ParseTheStringPrim;
PROCEDURE ParseTheString;
BEGIN
ParseTheStringPrim(treeName.Value());
END ParseTheString;
PROCEDURE ParseStringTerminal;
BEGIN
ParseTheStringPrim(grammarName.Value());
END ParseStringTerminal;
PROCEDURE ParseIdentifier;
VAR
wd: AGRS.Name;
str: ARRAY MaxStrLength OF CHAR;
ch: CHAR;
i: INTEGER;
termExpect: AGRS.Term;
BEGIN
i:= 0;
REPEAT
Read(ch);
str[i]:= ch;
INC(i);
UNTIL ~(ORD(ch) MOD SetBits IN
idChars.value[ORD(ch) DIV SetBits]);
IF i=1 THEN
AGRS.Fail;
RETURN
END;
BackSpace;
str[i-1]:= 0X;
wd:= Names.FindPublicName(str);
termExpect:= treeName.indirection;
IF termExpect IS AGRS.Tree THEN
termExpect:= termExpect.indirection;
END;
IF termExpect=wd THEN
AGRS.Continue;
ELSIF (termExpect=AGRS.Variable) OR
(termExpect=AGRS.Undefined) THEN
IF wd=NIL THEN
Names.AddArgument(wd,str);
treeName.Assign(wd);
AGRS.Continue;
ELSE
treeName.Assign(wd);
(* wd.Assign(AGRS.Undefined); *)
AGRS.Continue;
(* wd.Restore; *)
END;
treeName.Restore;
ELSE
AGRS.Fail;
END;
END ParseIdentifier;
PROCEDURE NameToTree;
VAR
newTerm: AGRS.Tree;
BEGIN
IF treeName.indirection IS AGRS.Name THEN
NEW(newTerm);
newTerm.Init(treeName.indirection);
treeName.Assign(newTerm);
AGRS.Continue;
treeName.Restore;
ELSE
AGRS.Continue;
END;
END NameToTree;
PROCEDURE (t: BuilderTerm) Reduce*;
VAR
newTerm: AGRS.Tree;
newTerm2: AGRS.Class;
newTerm3: AGRS.Block;
BEGIN
(*
IF treeName.indirection=AGRS.Variable THEN
AGRS.Fail;
RETURN
END;
*)
IF t.property=NIL THEN
treeName.Assign(t.indirection);
ELSE
IF t.indirection IS AGRS.Tree THEN
IF t.indirection IS AGRS.Class THEN
NEW(newTerm2);
newTerm2^:= t.indirection(AGRS.Class)^;
newTerm:= newTerm2;
ELSIF t.indirection IS AGRS.Block THEN
NEW(newTerm3);
newTerm3^:= t.indirection(AGRS.Block)^;
newTerm:= newTerm3;
ELSE
NEW(newTerm);
newTerm^:= t.indirection(AGRS.Tree)^;
END;
ELSE
ASSERT(t.indirection IS AGRS.Name);
newTerm.Init(t.indirection);
END;
newTerm.AddProperty(t.property,treeName.indirection);
treeName.Assign(newTerm);
END;
AGRS.Continue;
treeName.Restore;
END Reduce;
PROCEDURE (t: GenericBuilderTerm) Reduce*;
VAR
newTerm: AGRS.Tree;
newTerm2: AGRS.Class;
newTerm3: AGRS.Block;
prop: AGRS.Term;
BEGIN
NEW(newTerm);
IF t.indirection IS AGRS.Tree THEN
IF t.indirection IS AGRS.Class THEN
NEW(newTerm2);
newTerm2^:= t.indirection(AGRS.Class)^;
newTerm:= newTerm2;
ELSIF t.indirection IS AGRS.Block THEN
NEW(newTerm3);
newTerm3^:= t.indirection(AGRS.Block)^;
newTerm:= newTerm3;
ELSE
NEW(newTerm);
newTerm^:= t.indirection(AGRS.Tree)^;
END;
ELSE
ASSERT(t.indirection IS AGRS.Name);
newTerm.Init(t.indirection);
END;
prop:= propertyName.indirection;
IF ~(prop IS AGRS.Name) THEN
prop:= prop.indirection;
END;
newTerm.AddProperty(prop(AGRS.Name),valueName.indirection);
treeName.Assign(newTerm);
AGRS.Continue;
treeName.Restore;
END Reduce;
PROCEDURE BuildDisjunction;
VAR
newTerm: AGRS.Disjunction;
BEGIN
NEW(newTerm);
newTerm.Init(Library.argName1.indirection);
newTerm.InitAlternative(Library.argName2.indirection);
treeName.Assign(newTerm);
AGRS.Continue;
treeName.Restore;
END BuildDisjunction;
PROCEDURE BuildContinuation;
VAR
newTerm: AGRS.SubTerm;
BEGIN
NEW(newTerm);
newTerm.Init(Library.argName1.indirection);
newTerm.InitQuery(Library.argName2.indirection);
treeName.Assign(newTerm);
AGRS.Continue;
treeName.Restore;
END BuildContinuation;
PROCEDURE BuildField;
VAR
newTerm: AGRS.Field;
BEGIN
NEW(newTerm);
newTerm.Init(Library.argName1.indirection);
newTerm.InitQuery(Library.argName2.indirection);
treeName.Assign(newTerm);
AGRS.Continue;
treeName.Restore;
END BuildField;
PROCEDURE BuildBlock;
VAR
newTerm: AGRS.Term;
BEGIN
newTerm:= treeName.indirection;
newTerm(AGRS.Tree).RemoveProperty(rootName);
treeName.Assign(AGRS.MakeLocalBlock(newTerm,rootName.indirection));
AGRS.Continue;
treeName.Restore;
END BuildBlock;
PROCEDURE (t: SetLimitTerm) Reduce*;
VAR
oldLimit: LONGINT;
BEGIN
oldLimit:= limit;
limit:= t.limit;
t.indirection.Reduce;
limit:= oldLimit;
END Reduce;
PROCEDURE ParseRecurrence;
VAR
oldLimit: LONGINT;
seeker: Texts.Reader;
expect: AGRS.Term;
chExpect,chRead: CHAR;
newTerm: AGRS.Tree;
restoreTerm: SetLimitTerm;
BEGIN
oldLimit:= limit;
NEW(newTerm);
expect:= followName.Value();
IF expect IS Library.Character THEN
chExpect:= expect(Library.Character).value;
newTerm.Init(charTerminalParser);
ELSE
chExpect:= expect(Library.String).value[0];
newTerm.Init(stringTerminalParser);
END;
IF inputString=NIL THEN
REPEAT
DEC(limit);
Texts.OpenReader(seeker,inputText,limit);
Texts.Read(seeker,chRead);
UNTIL (chRead=chExpect) OR (limit<=position);
ELSE
REPEAT
DEC(limit);
chRead:= inputString[limit];
UNTIL (chRead=chExpect) OR (limit<=position);
END;
IF chRead=chExpect THEN
newTerm.AddProperty(grammarName,expect);
NEW(restoreTerm);
restoreTerm.Init(newTerm);
restoreTerm.limit:= oldLimit;
AGRS.Push(restoreTerm);
grammarName.Reduce;
ELSE
AGRS.Fail;
END;
limit:= oldLimit;
END ParseRecurrence;
PROCEDURE (t: ParserTerm) Reduce*;
VAR
oldPos: LONGINT;
ch: CHAR;
spaces: AGRS.Term;
BEGIN
oldPos:= position;
spaces:= spaceCharsName.Value();
WITH spaces: Library.CharSet DO
REPEAT
Read(ch);
UNTIL ~(ORD(ch) MOD SetBits IN
spaces.value[ORD(ch) DIV SetBits])
OR (position>limit);
BackSpace;
END;
t.handler;
position:= oldPos;
IF inputString=NIL THEN
Texts.OpenReader(input,inputText,oldPos);
END;
END Reduce;
PROCEDURE pParse;
VAR
txt,r: AGRS.Term;
BEGIN
txt:= sentenceName.Value();
WITH txt: Library.Text DO
inputText:= txt.base;
inputString:= NIL;
position:= txt.startOffset;
Texts.OpenReader(input,txt.base,txt.startOffset);
limit:= txt.endOffset;
Read:= ReadFile;
BackSpace:= BackSpaceFile;
ELSE
WITH txt: Library.String DO
position:= 0;
inputString:= txt.value;
limit:= txt.Length();
Read:= ReadString;
BackSpace:= BackSpaceString;
END;
END;
treeName.Assign(AGRS.Variable);
r:= grammarName.Evaluate(parseEndName);
treeName.Restore;
IF r.indirection#AGRS.failName THEN
IF ~AGRS.Continued() THEN
AGRS.result:= r.indirection;
END;
RETURN
END;
r:= Library.NewError(Library.SyntaxError);
r.Reduce;
END pParse;
PROCEDURE ExtractTree;
VAR
newTerm: AGRS.Term;
BEGIN
SkipSpaces;
IF position<limit THEN
AGRS.Fail;
ELSE
AGRS.Continue;
NEW(newTerm);
newTerm.Init(treeName.indirection);
AGRS.result:= newTerm;
END;
END ExtractTree;
PROCEDURE ParseByDefault;
VAR
r: AGRS.Term;
BEGIN
Parser.sentenceName.Assign(sentenceName.indirection);
r:= Parser.parseName.Value();
Parser.sentenceName.Restore;
treeName.Assign(r);
AGRS.Continue;
treeName.Restore;
END ParseByDefault;
PROCEDURE DefineParser*(VAR parserName: AGRS.Name;
spelling: ARRAY OF CHAR; handlerProc: ParserProc);
VAR
parser: ParserTerm;
BEGIN
NEW(parser);
parser.Init(Names.SystemRoot);
parser.handler:= handlerProc;
Names.DefinePublicName(parserName,spelling,parser);
END DefineParser;
BEGIN
Names.AddArgument(rootName, 'Root');
Names.AddArgument(treeName, 'Tree');
Names.AddArgument(propertyName, 'Property');
Names.AddArgument(grammarName, 'Grammar');
Names.AddArgument(followName, 'Follow');
Names.AddArgument(genericRootName, 'GenericRoot');
Names.AddArgument(valueName, 'Value');
Names.AddArgument(sentenceName, 'TextSentence');
Names.AddSystemName(emptyName,'Empty',AGRS.AtomicHandler);
Names.AddSystemName(defaultGrammarName,'DefaultGrammar',ParseByDefault);
grammarName.Init(defaultGrammarName);
Names.AddSystemName(attrName,'Attr',ParseAttribute);
Names.AddSystemName(genericAttrName,'Attribute',ParseGenericAttribute);
Names.AddSystemName(constructName,'Construct',ParseConstruct);
Names.AddSystemName(disjunctionName,'Disjunction',
ParseDisjunctConstruct);
Names.AddSystemName(continuationName,'Continuation',
ParseContinueConstruct);
Names.AddSystemName(fieldName,'FieldConstruct',ParseFieldConstruct);
Names.AddSystemName(blockName,'BlockConstruct',ParseBlockConstruct);
Names.AddSystemName(localConsName,'LocalConstruct',LocalConstruct);
Names.AddSystemName(classConsName,'ClassConstruct',ClassConstruct);
Names.AddSystemName(parseEndName,'ParseEnding',ExtractTree);
Names.AddSystemName(terminalName,'Terminal',ParseTerminal);
NEW(EmptyMarker);
EmptyMarker.Init(emptyName);
DefineParser(charParser,'CharGrammar',ParseChar);
DefineParser(stringParser,'StringGrammar',ParseTheString);
DefineParser(charTerminalParser,'CharTerminal',ParseCharTerminal);
DefineParser(stringTerminalParser,'StringTerminal',ParseStringTerminal);
DefineParser(nameParser,'NameGrammar',ParseIdentifier);
DefineParser(spaceParser,'SpaceGrammar',pSkipSpaces);
DefineParser(recurrenceParser,'Recurrence',ParseRecurrence);
Names.AddSystemName(parseName,'GrammarParser',pParse);
NEW(disjunctionBuilder);
disjunctionBuilder.Init(Names.SystemRoot);
disjunctionBuilder.InitHandler(BuildDisjunction);
NEW(continuationBuilder);
continuationBuilder.Init(Names.SystemRoot);
continuationBuilder.InitHandler(BuildContinuation);
NEW(fieldBuilder);
fieldBuilder.Init(Names.SystemRoot);
fieldBuilder.InitHandler(BuildField);
NEW(blockBuilder);
blockBuilder.Init(Names.SystemRoot);
blockBuilder.InitHandler(BuildBlock);
NEW(nameToTreeBuilder);
nameToTreeBuilder.Init(Names.SystemRoot);
nameToTreeBuilder.InitHandler(NameToTree);
NEW(temp);
temp.Init(nameParser);
temp.InitQuery(nameToTreeBuilder);
Names.DefinePublicName(idParser,'IdentifierGrammar',temp);
NEW(tempDis);
tempDis.Init(grammarName);
tempDis.InitAlternative(AGRS.Undefined);
Names.DefinePublicName(optionName,'Option',tempDis);
spaceChars:= Library.NewCharSet();
spaceChars.Include(' ');
spaceChars.Include(0X);
spaceChars.Include(9X);
spaceChars.Include(0AX);
spaceChars.Include(0DX);
Names.DefinePublicName(spaceCharsName,'SpaceChars',spaceChars);
idChars:= Library.NewCharSet();
FOR i:= ORD('a') TO ORD('z') DO
idChars.Include(CHR(i));
idChars.Include(CAP(CHR(i)));
END;
FOR i:= ORD('0') TO ORD('9') DO
idChars.Include(CHR(i));
END;
idChars.Include('_');
Names.DefinePublicName(idCharsName,'IdentifierChars',idChars);
END Grammars.