/**************************************************************************** ** ** presentation.c Presentation Werner Nickel ** nickel@mathematik.tu-darmstadt.de
*/
#include"config.h"
#include"presentation.h" #include"pcarith.h"
static node *Word(void);
/* ** ------------------------ GENERAL PURPOSE ------------------------- ** The first part of this file just contain some auxiliary functions.
*/
/* ** FreeNode() recursively frees a given node.
*/ void FreeNode(node *n) { if (n->type != TGEN && n->type != TNUM) {
FreeNode(n->cont.op.l);
FreeNode(n->cont.op.r);
}
Free(n);
}
/* ** GetNode() allocates space for a node of given type.
*/ static node *GetNode(EvalType type) {
node *n;
n = (node *)Allocate(sizeof(node));
n->type = type;
return n;
}
/* ** GenNumber() maintains the array GenNames[] of generator names which ** have been read so far. GenNumber() is called from the parser in order ** to create a new generator or to look up an existing one. The generator ** name is communicated to GenNumber() through the variable gname. ** ** If GenNumber() is called with the flag CREATE it checks if the generator ** name in gname has not occurred before and, if not, creates a new entry ** and returns the new generator number. If the name had occurred before ** the illegal generator number 0 is returned. ** ** If GenNumber() is called with the flag NOCREATE it searches for the ** generator name and returns the corresponding number if a matching entry ** in GenNames[] is found. If no matching entry is found, the illegal ** generator number 0 is returned.
*/ staticchar **GenNames; staticunsigned NrGens = 0;
#define NOCREATE 0 #define CREATE 1
static gen GenNumber(char *gname, int status) { unsigned i;
for (i = 1; i <= NrGens; i++) /* Find the generator name. */ if (strcmp(gname, GenNames[i]) == 0) { if (status == CREATE) return (gen)0; return (gen)i;
}
/* It's a new generator. */ if (status == NOCREATE) return (gen)0;
NrGens++; if (NrGens % 128 == 0)
GenNames = (char **)ReAllocate((void *)GenNames,
(NrGens + 128) * sizeof(char *));
/* ** GenName() is the inverse function for GenNumber(). It returns the ** name of a generator given by its number.
*/ constchar *GenName(gen g) { if (g > (gen)NrGens) return 0; return GenNames[g];
}
/* ** ------------------------- SCANNER --------------------------- ** The second part of this file contains the scanner. The parser ** starts after the function Generator().
*/
/* ** The following macros define tokens.
*/ typedefenum {
LPAREN,
RPAREN,
LBRACK,
RBRACK,
LBRACE,
RBRACE,
MULT,
POWER,
EQUAL,
DEQUALL,
DEQUALR,
PLUS,
MINUS,
LANGLE,
RANGLE,
PIPE,
COMMA,
SEMICOLON,
NUMBER,
GEN
} TokenType;
staticint Ch; /* Contains the next char on the input. */ static TokenType Token; /* Contains the current token. */ staticint Line; /* Current line number. */ staticint TLine; /* Line number where token starts. */ staticintChar; /* Current character number. */ staticint TChar; /* Character number where token starts. */ staticconstchar *InFileName; /* Current input file name. */ staticconstchar *OutFileName; /* Current output file name. */ static FILE *InFp; /* Current input file pointer. */ static FILE *OutFp; /* Current output file pointer. */ staticint N; /* Contains the integer just read. */ staticchar Gen[128]; /* Contains the generator name. */ /* static const char *TokenName[] = { "", "LParen", "RParen", "LBrack", "RBrack", "LBrace", "RBrace", "Mult", "Power", "Equal", "DEqualL", "DEqualR", "Plus", "Minus", "LAngle", "RAngle", "Pipe", "Comma", "Number", "Gen" };
*/
/* ** SyntaxError() just prints a syntax error and the line and place ** where is occurred and then exits. ** No recovery from syntax errors :-)
*/ staticvoid SyntaxError(constchar *str) { if (str == 0)
fprintf(stderr, "%s, line %d, char %d.\n",
InFileName, TLine, TChar); else
fprintf(stderr, "%s, line %d, char %d: %s.\n",
InFileName, TLine, TChar, str);
exit(1);
}
/* ** ReadCh() reads the next character from the current input file. ** At the same time it checks for lines terminated with '\'. Such ** a line is continued in the next line, therefore ReadCh() discards ** '\' and the following '\n'.
*/ staticvoid ReadCh(void) {
/* ** SkipBlanks() skips the characters ' ', '\t' and '\n' as well as ** comments. A comment starts with '#' and finishes at the end of ** the line.
*/ staticvoid SkipBlanks(void) {
/* If Ch is empty, the next character is fetched. */ if (Ch == '\0') ReadCh();
/* First blank characters and comments are skipped. */ while (Ch == ' ' || Ch == '\t' || Ch == '\n' || Ch == '#') { if (Ch == '#') { /* Skip to the end of line. */ while (Ch != '\n') ReadCh();
} if (Ch == '\n') { Line++; Char = 0; }
ReadCh();
}
}
/* ** Number reads a number from the input.
*/ staticvoid Number(void) {
unsignedint m, n = 0, overflow = 0;
while (isdigit(Ch)) {
m = n;
n = 10 * n + (Ch - '0'); if ((n - (Ch - '0')) / 10 != m) { overflow = 1; break; }
ReadCh();
}
/* ** Generator() reads characters from the input stream until a non- ** alphanumeric character is encountered. Only the first 127 characters ** are significant as generator name and are copied into the global ** array Gen[]. All other characters are discarded.
*/ staticvoid Generator(void) {
int i;
for (i = 0; i < 127 && (isalnum(Ch) || Ch == '_' || Ch == '.'); i++) {
Gen[i] = Ch;
ReadCh();
}
Gen[i] = '\0'; /* Discard the rest. */ while (isalnum(Ch) || Ch == '_' || Ch == '.') ReadCh();
}
/* ** NextToken reads the next token from the input stream. It first ** skips all the blank characters and comments.
*/ staticvoid NextToken(void) {
SkipBlanks();
TChar = Char;
TLine = Line; switch (Ch) { case'(':
{ Token = LPAREN; ReadCh(); break; } case')':
{ Token = RPAREN; ReadCh(); break; } case'[':
{ Token = LBRACK; ReadCh(); break; } case']':
{ Token = RBRACK; ReadCh(); break; } case'{':
{ Token = LBRACE; ReadCh(); break; } case'}':
{ Token = RBRACE; ReadCh(); break; }
/* ** ------------------------- PARSER ---------------------------- ** Here the third part of this file starts containing the parser. ** ** This is the grammar that defines the syntax of a finite presentation. ** Quoted items (except 'empty') are recognized by the scanner and returned ** as so called tokens. The unquoted symbol | indicates alternatives. ** ** presentation: '<' genlist '|' rellist '>' | ** '<' genlist ; genlist '|' rellist '>' ** ** genlist: 'empty' | genseq ** genseq: 'generator' | 'generator' ',' genseq ** ** rellist: 'empty' | relseq ** relseq: relation | relation ',' relseq ** ** relation: word | word '=' word | word '=:' word | word ':=' word ** ** word: power | power '*' word ** ** power: atom '^' atom | atom '^' snumber | atom ** ** atom: 'generator' | '(' word ')' | commutator ** ** commutator: '[' word ',' wordseq ']' ** wordseq word | word ',' wordseq ** ** snumber: 'sign' 'number' | 'number'
*/
/* ** InitParser() does exactly what the name suggests.
*/ staticvoid InitParser(FILE *fp, constchar *filename) {
InFp = fp;
InFileName = filename;
Ch = '\0'; Char = 0;
Line = 1;
NextToken();
}
/* ** Snumber() reads a signed number. The defining rule is: ** ** snumber: '+' 'number' | '-' 'number' | 'number'
*/ static node *Snumber(void) {
node *n = 0;
if (Token == NUMBER) {
n = GetNode(TNUM);
n->cont.n = N;
NextToken();
} elseif (Token == PLUS) {
NextToken(); if (Token != NUMBER) SyntaxError("Number expected");
n = GetNode(TNUM);
n->cont.n = N;
NextToken();
} elseif (Token == MINUS) {
NextToken(); if (Token != NUMBER) SyntaxError("Number expected");
n = GetNode(TNUM);
n->cont.n = -N;
NextToken();
} else SyntaxError("Number expected");
return n;
}
/* ** The defining rules for commutators are: ** ** commutator: '[' word ',' wordseq ']' ** | '[' word ',' 'number' word ] ** wordseq word | word ',' wordseq ** ** A word starts either with 'generator', with '(' or with '['.
*/ static node *Commutator(void) {
node *n, *o;
if (Token != LBRACK)
SyntaxError("Left square bracket expected");
NextToken(); if (Token != GEN && Token != LPAREN && Token != LBRACK)
SyntaxError("Word expected");
o = Word(); if (Token != COMMA) SyntaxError("Comma expected"); while (Token == COMMA) {
NextToken(); if (Token != GEN && Token != NUMBER &&
Token != LPAREN && Token != LBRACK)
SyntaxError("Word expected");
if (Token == NUMBER) { /* An Engel relation is on the input stream. */
n = GetNode(TENGEL);
n->cont.op.l = o;
if (N <= 0) SyntaxError("Engel-n must be positive");
/* ** Atom() reads an atom. Note that Atom() creates a generator by ** calling GenNumber(). ** ** The defining rule for atoms is: ** ** atom: 'generator' | '(' word ')' | commutator
*/ static node *Atom(void) {
node *n = 0;
if (Token == GEN) {
n = GetNode(TGEN);
n->cont.g = GenNumber(Gen, NOCREATE); if (n->cont.g == (gen)0) SyntaxError("Unknown generator");
NextToken();
} elseif (Token == LPAREN) {
NextToken();
n = Word(); if (Token != RPAREN)
SyntaxError("Closing parenthesis expected");
NextToken();
} elseif (Token == LBRACK) {
n = Commutator();
} else {
SyntaxError("Generator, left parenthesis or commutator expected");
} return n;
}
/* ** Power() reads a power. The defining rule is: ** ** power: atom | atom '^' atom | atom '^' snumber | **
*/ static node *Power_(void) {
node *n, *o;
o = Atom(); if (Token == POWER) {
NextToken(); if (Token == PLUS || Token == MINUS || Token == NUMBER) {
n = o;
o = GetNode(TPOW);
o->cont.op.l = n;
o->cont.op.r = Snumber();
} else {
n = o;
o = GetNode(TCONJ);
o->cont.op.l = n;
o->cont.op.r = Atom();
}
} return o;
}
/* ** Word() reads a word. The defining rule is: ** ** word: power | power '*' word ** ** A word starts either with 'generator', with '(' or with '['.
*/ static node *Word(void) {
node *n, *o;
o = Power_(); if (Token == MULT) {
NextToken();
n = o;
o = GetNode(TMULT);
o->cont.op.l = n;
o->cont.op.r = Word();
}
return o;
}
/* ** Relation() reads a relation. The defining rule is: ** ** relation: word | word '=' word | word '=:' word | word ':=' word ** ** A relation starts either with 'generator', with '(' or with '['.
*/ static node *Relation(void) {
node *n, *o;
if (Token != GEN && Token != LPAREN && Token != LBRACK)
SyntaxError("relation expected");
o = Word(); if (Token == EQUAL) {
NextToken();
n = o;
o = GetNode(TREL);
o->cont.op.l = n;
o->cont.op.r = Word();
} elseif (Token == DEQUALL) {
NextToken();
n = o;
o = GetNode(TDRELL);
o->cont.op.l = n;
o->cont.op.r = Word();
} elseif (Token == DEQUALR) {
NextToken();
n = o;
o = GetNode(TDRELR);
o->cont.op.l = n;
o->cont.op.r = Word();
}
return o;
}
/* ** RelList() reads a list of relations. The defining rules are: ** ** rellist: 'empty' | relseq ** relseq: relation | relation ',' relseq ** ** A relation starts either with 'generator', with '(' or with '['.
*/ static node **RelList(void) {
/* ** GenList() reads a list of generators. The list of generators may ** consist of abstract generators and identical generators. Identical ** generators are used to specify identical relations. ** ** The defining rules are: ** ** genlist: 'empty' | genseq | genseq ; genseq ** genseq: 'generator' | 'generator' ',' genseq
*/ staticint GenList(void) {
int nrgens = 0;
if (Token != GEN) return nrgens;
nrgens++; if (GenNumber(Gen, CREATE) == (gen)0)
SyntaxError("Duplicate generator");
NextToken(); while (Token == COMMA) {
NextToken(); if (Token != GEN) SyntaxError("Generator expected");
nrgens++; if (GenNumber(Gen, CREATE) == (gen)0)
SyntaxError("Duplicate generator");
NextToken();
}
return nrgens;
}
/* ** The following data structure holds a presentation.
*/ struct pres { unsigned nragens; /* number of abstract generators */ unsigned nrigens; /* number of identical generators */ unsigned nrrels; /* number of relations */
node **rels; /* pointer to relations */
};
staticstruct pres Pres;
/* ** NumberOfAbstractGens() returns the number of abstract generators.
*/ int NumberOfAbstractGens(void) { return Pres.nragens; }
/* ** NumberOfIdenticalGens() returns the number of identical generators.
*/ int NumberOfIdenticalGens(void) { return Pres.nrigens; }
/* ** NumberOfGens() returns the number of abstract and identical generators.
*/ int NumberOfGens(void) { return Pres.nragens + Pres.nrigens; }
/* ** NumberOfRels() returns the number of relations.
*/ int NumberOfRels(void) { return Pres.nrrels; }
/* ** NextRelation() returns the next relation, if it exists, ** and returns the null pointer otherwise. ** FirstRelation initializes the variable NextRel and calls ** NextRelation(). ** NthRelation() returns the n-th relation, if n is in the ** range [0..NumberOfRels()-1] and the null pointer otherwise. ** CurrentRelation() returns the relation just being processed.
*/ staticint NextRel;
node *NextRelation(void) {
if (NextRel >= NumberOfRels()) return (node *)0;
return Pres.rels[NextRel++];
}
node *FirstRelation(void) {
NextRel = 0; return NextRelation();
}
node *NthRelation(int n) { if (n < 0 || n >= NumberOfRels()) return (node *)0;
if (Token != PIPE) SyntaxError("vertical bar expected");
NextToken();
Pres.rels = RelList();
Pres.nrrels = 0; while (Pres.rels[Pres.nrrels]) Pres.nrrels++;
if (Token != RANGLE)
SyntaxError("presentation has to be closed by '>'");
}
node *ReadWord(void) {
node *n;
if (Token != SEMICOLON) n = Word(); else {
NextToken(); return ReadWord();
}
if (Token != SEMICOLON)
SyntaxError("word has to be finished by ';'");
return n;
}
/* ** ----------------------- EVALUATOR ------------------------ ** The fourth part of this file contains the evaluator.
*/ static EvalFunc EvalFunctions[TLAST];
void SetEvalFunc(EvalType type, EvalFunc function) { if (type <= TNUM || type >= TLAST) {
printf("Evaluation error: illegal type in SetEvalFunc()\n"); exit(1);
}
EvalFunctions[type] = function;
}
void *EvalNode(node *n) { void *e, *l, *r;
if (n->type == TNUM) return (void *) & (n->cont.n);
switch (n->type) { case TGEN: /* TGEN is a unary node. */ return WordGen(n->cont.g);
case TCOMM: /* Adjust the class. */
Class--;
l = EvalNode(n->cont.op.l); if (l != (void *)0) r = EvalNode(n->cont.op.r); Class++;
if (l == (void *)0) return l; if (r == (void *)0) { Free(l); return r; }
return WordComm((word)l, (word)r);
case TENGEL: /* TENGEL is a ternary node. */
if ((e = EvalNode(n->cont.op.e)) == (void *)0) return e;
Class -= *(int *)e;
l = EvalNode(n->cont.op.l); if (l != (void *)0) r = EvalNode(n->cont.op.r); Class += *(int *)e;
for (nr = 0, g = 1; g <= NumberOfIdenticalGens(); g++) if (IdenticalGenNumberNode[ g ] == 1)
IdenticalGenNumberNode[ g ] = ++nr;
NrIdenticalGensNode = nr; return nr;
}
void **EvalRelations(void) {
void **results; unsigned r;
results = (void **)Allocate((Pres.nrrels + 1) * sizeof(void *)); for (r = 0; r < Pres.nrrels; r++)
results[r] = EvalNode(Pres.rels[r]);
results[r] = (void *)0; return results;
}
/* ** ----------------------- PRINTING ------------------------ ** And the last part contains the print functions.
*/
/* ** PrintNum() prints an integer.
*/ staticvoid PrintNum(int n) {
fprintf(OutFp, "%d", n);
}
/* ** PrintGen() prints a generator.
*/ void PrintGen(gen g) {
fprintf(OutFp, "%s", GenName(g));
}
/* ** PrintComm() prints a commutator using the following rule for ** left normed commutators : ** [[a,b],c] = [a,b,c]
*/ staticvoid PrintComm(node *l, node *r, int bracket) { if (bracket) fprintf(OutFp, "[");
/* If the left operand is a commutator, don't print its brackets. */ if (l->type == TCOMM)
PrintComm(l->cont.op.l, l->cont.op.r, 0); else
PrintNode(l);
fprintf(OutFp, ",");
PrintNode(r);
if (bracket) fprintf(OutFp, "]");
}
/* ** PrintEngel() prints a commutator using the following rule for ** Engel relations: ** [u, n v]
*/ staticvoid PrintEngel(node *l, node *r, node *e) {
fprintf(OutFp, "[");
PrintNode(l);
fprintf(OutFp, ", ");
PrintNode(e);
fprintf(OutFp, " ");
PrintNode(r);
fprintf(OutFp, "]");
}
/* ** PrintMult() prints a product. It is not necessary to check if ** parentheses have to be printed since '*' has the lowest precedence ** of all operators except '='. But '=' can only occur at the top of ** an expression tree.
*/ staticvoid PrintMult(node *l, node *r) {
PrintNode(l);
fprintf(OutFp, "*");
PrintNode(r);
}
/* ** PrintPow() prints an expression raised to an integer. If the expression ** is a product, it has to be enclosed in parentheses because of the lower ** precedence of '*'. If the expression is again a power or a conjugation, ** it has to be enclosed in parenthesis because '^' is not an associative ** operator.
*/ staticvoid PrintPow(node *l, node *r) { if (l->type == TPOW || l->type == TCONJ || l->type == TMULT) {
putc('(', OutFp);
PrintNode(l);
putc(')', OutFp);
} else
PrintNode(l);
putc('^', OutFp); if (r->type != TNUM) {
fprintf(OutFp, "Fatal error in tree.\n"); exit(5);
}
fprintf(OutFp, "%d", r->cont.n);
}
/* ** PrintConj() prints an expression conjugated by another expression. ** If one of the expressions is a product, a power or another conjugation, ** it has to be enclosed in parentheses for the same reasons PrintPow() ** has to enclose the basis in parentheses.
*/ staticvoid PrintConj(node *l, node *r) { if (l->type == TPOW || l->type == TCONJ || l->type == TMULT) {
putc('(', OutFp);
PrintNode(l);
putc(')', OutFp);
} else
PrintNode(l);
/* ** PrintRel() prints a relation. No parenthesis are necessary since ** '=' has the lowest precedence of all binary operators.
*/ staticvoid PrintRel(node *l, node *r) {
PrintNode(l);
fprintf(OutFp, " = ");
PrintNode(r);
}
/* ** PrintDRelL() prints a defining relation. No parenthesis are necessary ** since '=:' has the lowest precedence of all binary operators.
*/ staticvoid PrintDRelL(node *l, node *r) {
PrintNode(l);
fprintf(OutFp, " := ");
PrintNode(r);
}
/* ** PrintDRelR() prints a defining relation. No parenthesis are necessary ** since '=:' has the lowest precedence of all binary operators.
*/ staticvoid PrintDRelR(node *l, node *r) {
PrintNode(l);
fprintf(OutFp, " =: ");
PrintNode(r);
}
/* ** PrintNode() just looks at the type of a node and then calls the ** appropriate print function.
*/ void PrintNode(node *n) { switch (n->type) { case TNUM:
{ PrintNum(n->cont.n); break; } case TGEN:
{ PrintGen(n->cont.g); break; } case TMULT:
{ PrintMult(n->cont.op.l, n->cont.op.r); break; } case TPOW:
{ PrintPow(n->cont.op.l, n->cont.op.r); break; } case TCONJ:
{ PrintConj(n->cont.op.l, n->cont.op.r); break; } case TCOMM:
{ PrintComm(n->cont.op.l, n->cont.op.r, 1); break; } case TREL:
{ PrintRel(n->cont.op.l, n->cont.op.r); break; } case TDRELL:
{ PrintDRelL(n->cont.op.l, n->cont.op.r); break; } case TDRELR:
{ PrintDRelR(n->cont.op.l, n->cont.op.r); break; } case TENGEL: {
PrintEngel(n->cont.op.l, n->cont.op.r,
n->cont.op.e); break;
} default:
{ fprintf(OutFp, "\nunknown node type\n"); exit(5); }
}
}
/* ** PrintPresentation() prints the presentation stored in the global ** variable Pres.
*/ void PrintPresentation(FILE *fp) {
gen g; int r;
InitPrint(fp);
if (Pres.nragens == 0) return;
/* Open the presentation. */
fprintf(OutFp, "< ");
/* Print the generators first. */
PrintGen(1); for (g = 2; g <= (gen)Pres.nragens; g++) {
fprintf(OutFp, ", ");
PrintGen(g);
}
if (Pres.nrigens > 0) {
fprintf(OutFp, "; ");
PrintGen(Pres.nragens + 1); for (g = Pres.nragens + 2; g <= (gen)(Pres.nragens + Pres.nrigens); g++) {
fprintf(OutFp, ", ");
PrintGen(g);
}
}
/* Now the delimiter. */
fprintf(OutFp, " |\n");
/* Now the relations. */ if (Pres.rels[0] != (node *)0) {
fprintf(OutFp, " ");
PrintNode(Pres.rels[0]);
} for (r = 1; Pres.rels[r] != (node *)0; r++) {
fprintf(OutFp, ",\n ");
PrintNode(Pres.rels[r]);
}
/* And close the presentation. */
fprintf(OutFp, " >\n");
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.