Quelle presentation.c
Sprache: C
/****************************************************************************
* *
* * 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 >= (1 U << (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 = "" ;
}
Messung V0.5 in Prozent C=90 H=100 G=95
¤ Dauer der Verarbeitung: 0.21 Sekunden
¤
*© Formatika GbR, Deutschland
2026-08-25
Neuigkeiten
Aktuelles
Motto des Tages
Open Source Software
Jenseits des Üblichen ....
Besucherstatistik
Statistik
#Sources=1019547
#Domains=890699