/****************************************************************************
**
** 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.
*/
static char **GenNames;
static unsigned NrGens = 0;
#define NOCREATE 0
#define CREATE 1
static gen GenNumber(char *gname, int status) {
unsigned i;
if (status == CREATE && NrGens == 0) /* Initialize GenNames[]. */
GenNames = (char **)Allocate(128 * sizeof(char*));
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 *));
i = strlen(gname);
GenNames[ NrGens ] = (char *)Allocate((i + 1) * sizeof(char));
strcpy(GenNames[NrGens], gname);
return NrGens;
}
/*
** GenName() is the inverse function for GenNumber(). It returns the
** name of a generator given by its number.
*/
const char *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.
*/
typedef enum {
LPAREN,
RPAREN,
LBRACK,
RBRACK,
LBRACE,
RBRACE,
MULT,
POWER,
EQUAL,
DEQUALL,
DEQUALR,
PLUS,
MINUS,
LANGLE,
RANGLE,
PIPE,
COMMA,
SEMICOLON,
NUMBER,
GEN
} TokenType;
static int Ch; /* Contains the next char on the input. */
static TokenType Token; /* Contains the current token. */
static int Line; /* Current line number. */
static int TLine; /* Line number where token starts. */
static int Char; /* Current character number. */
static int TChar; /* Character number where token starts. */
static const char *InFileName; /* Current input file name. */
static const char *OutFileName; /* Current output file name. */
static FILE *InFp; /* Current input file pointer. */
static FILE *OutFp; /* Current output file pointer. */
static int N; /* Contains the integer just read. */
static char 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 :-)
*/
static void SyntaxError(const char *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'.
*/
static void ReadCh(void) {
Ch = getc(InFp);
Char++;
if (Ch == '\\') {
Ch = getc(InFp);
if (Ch == '\n') { Line++; Char = 0; ReadCh(); }
else { ungetc(Ch, InFp); Ch = '\\'; }
}
}
/*
** SkipBlanks() skips the characters ' ', '\t' and '\n' as well as
** comments. A comment starts with '#' and finishes at the end of
** the line.
*/
static void 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.
*/
static void Number(void) {
unsigned int 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();
}
if (overflow) {
fprintf(stderr, "Integer overflow reading %u%c", m, Ch);
ReadCh();
while (isdigit(Ch)) { fprintf(stderr, "%c", Ch); ReadCh(); }
fprintf(stderr, " in\n");
SyntaxError((char *)0);
} else if (n >= (1U << (8 * sizeof(unsigned int) - 1))) {
fprintf(stderr, "Integer overflow reading %u in\n", n);
SyntaxError((char *)0);
}
N = n;
}
/*
** 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.
*/
static void 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.
*/
static void 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; }
case '*':
{ Token = MULT; ReadCh(); break; }
case '^':
{ Token = POWER; ReadCh(); break; }
case ':': {
ReadCh();
if (Ch != '=') SyntaxError("illegal character");
Token = DEQUALL;
ReadCh();
break;
}
case '=': {
ReadCh();
if (Ch != ':') Token = EQUAL;
else { Token = DEQUALR; ReadCh(); }
break;
}
case '+':
{ Token = PLUS; ReadCh(); break; }
case '-':
{ Token = MINUS; ReadCh(); break; }
case '<':
{ Token = LANGLE; ReadCh(); break; }
case '>':
{ Token = RANGLE; ReadCh(); break; }
case '|':
{ Token = PIPE; ReadCh(); break; }
case ',':
{ Token = COMMA; ReadCh(); break; }
case ';':
{ Token = SEMICOLON; ReadCh(); break; }
case '0':
case '1' :
case '2' :
case '3' :
case '4' :
case '5':
case '6' :
case '7' :
case '8' :
case '9' :
{ Token = NUMBER; Number(); break; }
default :
if (isalnum(Ch) || Ch == '_' || Ch == '.')
{ Token = GEN; Generator(); break; }
else SyntaxError("illegal character");
}
/* printf( "# NextToken(): %s\n", TokenName[Token] );*/
}
/*
** ------------------------- 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.
*/
static void InitParser(FILE *fp, const char *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();
} else if (Token == PLUS) {
NextToken();
if (Token != NUMBER) SyntaxError("Number expected");
n = GetNode(TNUM);
n->cont.n = N;
NextToken();
} else if (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");
n->cont.op.e = GetNode(TNUM);
n->cont.op.e->cont.n = N;
NextToken();
n->cont.op.r = Word();
break;
} else {
n = GetNode(TCOMM);
n->cont.op.l = o;
n->cont.op.r = Word();
o = n;
}
}
if (Token != RBRACK) SyntaxError("Right square bracket missing");
NextToken();
return n;
}
/*
** 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();
} else if (Token == LPAREN) {
NextToken();
n = Word();
if (Token != RPAREN)
SyntaxError("Closing parenthesis expected");
NextToken();
} else if (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();
} else if (Token == DEQUALL) {
NextToken();
n = o;
o = GetNode(TDRELL);
o->cont.op.l = n;
o->cont.op.r = Word();
} else if (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) {
node **rellist;
unsigned n = 0;
rellist = (node **)Allocate(sizeof(node *));
rellist[0] = (node *)0;
if (Token != GEN && Token != LPAREN && Token != LBRACK)
return rellist;
rellist = (node**)ReAllocate((void *)rellist, 2 * sizeof(node *));
rellist[n++] = Relation();
while (Token == COMMA) {
NextToken();
rellist = (node**)ReAllocate((void *)rellist, (n + 2) * sizeof(node *));
rellist[n++] = Relation();
}
rellist[n] = (node *)0;
return rellist;
}
/*
** 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
*/
static int 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 */
};
static struct 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.
*/
static int 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;
return Pres.rels[n];
}
node *CurrentRelation(void) { return Pres.rels[NextRel - 1]; }
/*
** Presentation reads a finite presentation. The syntax of a presentation
** is:
** presentation: '<' genlist '|' rellist '>' |
** '<' genlist ; genlist '|' rellist '>'
*/
void Presentation(FILE *fp, const char *filename) {
InitParser(fp, filename);
if (Token != LANGLE) SyntaxError("presentation expected");
NextToken();
if (Token != GEN && Token != PIPE)
SyntaxError("generator or vertical bar expected");
Pres.nragens = GenList();
if (Token == SEMICOLON) {
NextToken();
Pres.nrigens = GenList();
} else
Pres.nrigens = 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;
if (l == (void *)0) { Free(e); return l; }
if (r == (void *)0) { Free(e); Free(l); return r; }
return WordEngel((word)l, (word)r, (int *)e);
default:
if (EvalFunctions[n->type] == 0) {
fprintf(stderr, "No evaluation function for type %d.\n", n->type);
exit(5);
}
if ((l = EvalNode(n->cont.op.l)) == (void *)0) return l;
if ((r = EvalNode(n->cont.op.r)) == (void *)0) {
Free(l);
return r;
}
return (*EvalFunctions[n->type])((word)l, r);
}
}
static
void TraverseNode(node *n, gen *igens) {
if (n->type == TNUM) return;
if (n->type == TGEN) {
if (WordGen(n->cont.g) == (void *)0)
igens[ n->cont.g - NumberOfAbstractGens() ] = 1;
return;
}
TraverseNode(n->cont.op.l, igens);
TraverseNode(n->cont.op.r, igens);
}
int NrIdenticalGensNode = 0;
gen *IdenticalGenNumberNode = 0;
int NumberOfIdenticalGensNode(node *n) {
gen g, nr;
if (IdenticalGenNumberNode != (gen *)0)
Free(IdenticalGenNumberNode);
IdenticalGenNumberNode =
(gen *)Allocate((NumberOfIdenticalGens() + 1) * sizeof(gen));
TraverseNode(n, IdenticalGenNumberNode);
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.
*/
static void 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]
*/
static void 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]
*/
static void 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.
*/
static void 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.
*/
static void 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.
*/
static void PrintConj(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 == TPOW || r->type == TCONJ || r->type == TMULT) {
putc('(', OutFp);
PrintNode(r);
putc(')', OutFp);
} else
PrintNode(r);
}
/*
** PrintRel() prints a relation. No parenthesis are necessary since
** '=' has the lowest precedence of all binary operators.
*/
static void 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.
*/
static void 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.
*/
static void 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");
}
void InitPrint(FILE *fp) {
OutFp = fp;
OutFileName = "";
}
¤ Dauer der Verarbeitung: 0.26 Sekunden
¤
*© Formatika GbR, Deutschland
|
|