#pragma prototyped
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdlib.h>
#ifdef HAVE_STDINT_H
#include <stdint.h>
#endif
#ifdef HAVE_INTTYPES_H
#include <inttypes.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <compile.h>
#include <assert.h>
#include <agraph.h>
#include <error.h>
#include <actions.h>
#include <sfstr.h>
#include <string.h>
#include <stdio.h>
#include <math.h>
#define ISEDGE(e) (AGTYPE(e)&2)
#include <gdefs.h>
#define BITS_PER_BYTE 8
#ifdef HAVE_INTPTR_T
#define INT2PTR(t,v) ((t)(intptr_t)(v))
#define PTR2INT(v) ((Sflong_t)(intptr_t)(v))
#else
#define INT2PTR(t,v) ((t)(v))
#define PTR2INT(v) ((Sflong_t)(v))
#endif
static char*
nameOf (Expr_t* ex, Agobj_t* obj)
{
Sfio_t* tmps;
char* s;
char* key;
Agedge_t* e;
switch (AGTYPE(obj)) {
case AGNODE :
case AGRAPH :
s = agnameof(obj);
break;
default :
tmps = sfstropen();
e = (Agedge_t*)obj;
key = agnameof(obj);
sfputr (tmps, agnameof(AGTAIL(e)),-1);
if (agisdirected(agraphof(e)))
sfputr (tmps, "->", -1);
else
sfputr (tmps, "--", -1);
sfputr (tmps, agnameof(AGHEAD(e)),-1);
if (*key) {
sfputc (tmps, '[');
sfputr (tmps, key, -1);
sfputc (tmps, ']');
}
s = exstring (ex, sfstruse(tmps));
sfclose (tmps);
break;
}
return s;
}
static char*
bbOf (Expr_t* pgm, char* pt, int getll)
{
double x,y,u,v;
char* s;
char* p;
int len;
if (sscanf (pt, "%lf,%lf,%lf,%lf",&x,&y,&u,&v) == 4) {
p = strchr (pt, ',');
p = strchr (p+1, ',');
if (getll) {
len = p - pt;
s = exstralloc (pgm, 0, len+1);
strncpy (s,pt,len);
}
else
s = exstring (pgm, p+1);
}
else s = "";
return s;
}
static char*
xyOf (Expr_t* pgm, char* pt, int getx)
{
double x,y;
char* v;
char* p;
int len;
if (sscanf (pt, "%lf,%lf",&x,&y) == 2) {
p = strchr (pt, ',');
if (getx) {
len = p - pt;
v = exstralloc (pgm, 0, len+1);
strncpy (v,pt,len);
}
else
v = exstring (pgm, p+1);
}
else v = "";
return v;
}
#ifdef DEBUG
static char*
symName (Expr_t* ex, int op)
{
if (op >= MINNAME && op <= MAXNAME)
return gprnames[op];
else {
Sfio_t* sf = sfstropen();
char* s;
sfprintf (sf, "<unknown (%d)>", op);
s = exstring (ex, sfstruse(sf));
sfclose (sf);
return s;
}
}
#endif
static Agdesc_t
xargs (char* args)
{
Agdesc_t desc = Agdirected;
char c;
while ((c = *args++)) {
switch (c) {
case 'u' :
case 'U' :
desc.directed = 0;
break;
case 'd' :
case 'D' :
desc.directed = 1;
break;
case 's' :
case 'S' :
desc.strict = 1;
break;
case 'n' :
case 'N' :
desc.directed = 0;
break;
default :
error (ERROR_WARNING, "unknown graph descriptor '%c' : ignored", c);
break;
}
}
return desc;
}
static char*
deparse (Expr_t* ex, Exnode_t* n, Sfio_t* sf)
{
exdump (ex, n, sf);
return (sfstruse (sf));
}
static Agobj_t*
deref (Exnode_t* x, Exref_t* ref, Agobj_t* objp, Gpr_t* state)
{
void* ptr;
if (ref == 0)
return objp;
else if (ref->symbol->lex == DYNAMIC) {
ptr = INT2PTR(void*,x->data.variable.dyna->data.variable.dyna->data.constant.value.integer);
return deref (x, ref->next, (Agobj_t*)ptr, state);
}
else switch (ref->symbol->index) {
case V_outgraph :
return deref (x, ref->next, (Agobj_t*)state->outgraph, state);
break;
case V_this :
return deref (x, ref->next, state->curobj, state);
break;
case V_thisg :
return deref (x, ref->next, (Agobj_t*)state->curgraph, state);
break;
case V_targt :
return deref (x, ref->next, (Agobj_t*)state->target, state);
break;
case M_head :
if (!objp && !(objp = state->curobj)) {
error (ERROR_WARNING, "Current object $ not defined");
return 0;
}
if (ISEDGE(objp))
return deref (x, ref->next, (Agobj_t*)AGHEAD((Agedge_t*)objp), state);
else error (ERROR_FATAL, "head of non-edge");
break;
case M_tail :
if (!objp && !(objp = state->curobj)) {
error (ERROR_FATAL, "Current object $ not defined");
return 0;
}
if (ISEDGE(objp))
return deref (x, ref->next, (Agobj_t*)AGTAIL((Agedge_t*)objp), state);
else error (ERROR_FATAL, "tail of non-edge %x", objp);
break;
default :
error (ERROR_WARNING, "%s : illegal reference", ref->symbol->name);
return 0;
break;
}
return 0;
}
static int
lookup (Expr_t* pgm, Agobj_t* objp, Exid_t* sym, Extype_t* v)
{
if (sym->lex == ID) {
switch (sym->index) {
case M_head :
if (ISEDGE(objp)) v->integer = PTR2INT(AGHEAD((Agedge_t*)objp));
else {
error (ERROR_WARNING, "head of non-edge");
return -1;
}
break;
case M_tail :
if (ISEDGE(objp)) v->integer = PTR2INT(AGTAIL((Agedge_t*)objp));
else {
error (ERROR_WARNING, "tail of non-edge");
return -1;
}
break;
case M_name :
v->string = nameOf (pgm, objp);
break;
case M_indegree :
if (AGTYPE(objp) == AGNODE) v->integer = agdegree((Agnode_t*)objp,1,0);
else {
error (ERROR_FATAL, "indegree of non-node");
return -1;
}
break;
case M_outdegree :
if (AGTYPE(objp) == AGNODE) v->integer = agdegree((Agnode_t*)objp,0,1);
else {
error (ERROR_FATAL, "outdegree of non-node");
return -1;
}
break;
case M_degree :
if (AGTYPE(objp) == AGNODE) v->integer = agdegree((Agnode_t*)objp,1,1);
else {
error (ERROR_FATAL, "degree of non-node");
return -1;
}
break;
case M_parent :
if (AGTYPE(objp) == AGRAPH) v->integer = PTR2INT(agparent((Agraph_t*)objp));
else {
error (ERROR_FATAL, "root of non-graph");
return -1;
}
break;
case M_root :
v->integer = PTR2INT(agroot(agraphof(objp)));
break;
case M_n_edges :
if (AGTYPE(objp) == AGRAPH) v->integer = agnedges((Agraph_t*)objp);
else {
error (ERROR_FATAL, "n_edges of non-graph");
return -1;
}
break;
case M_n_nodes :
if (AGTYPE(objp) == AGRAPH) v->integer = agnnodes((Agraph_t*)objp);
else {
error (ERROR_FATAL, "n_nodes of non-graph");
return -1;
}
break;
case M_directed :
if (AGTYPE(objp) == AGRAPH) v->integer = agisdirected((Agraph_t*)objp);
else {
error (ERROR_FATAL, "directed of non-graph");
return -1;
}
break;
case M_strict :
if (AGTYPE(objp) == AGRAPH) v->integer = agisstrict((Agraph_t*)objp);
else {
error (ERROR_FATAL, "strict of non-graph");
return -1;
}
break;
default :
error (ERROR_WARNING, "%s : illegal reference", sym->name);
return -1;
break;
}
}
else v->string = agget (objp, sym->name);
return 0;
}
static char*
getArg(int n, Gpr_t* state)
{
if (n >= state->argc) {
error (ERROR_FATAL, "program references ARGV[%d] - undefined", n);
}
return (state->argv[n]);
}
Extype_t
getval(Expr_t* pgm, Exnode_t* node, Exid_t* sym, Exref_t* ref,
void* env, int elt, Exdisc_t* disc)
{
Extype_t v;
Gpr_t* state;
Extype_t* args;
Agobj_t* objp;
char* key;
Agraph_t* gp;
Agnode_t* np;
Agnode_t* hp;
Agedge_t* ep;
assert (sym->lex != CONSTANT);
if (elt == EX_CALL) {
args = (Extype_t*)env;
state = (Gpr_t*)(disc->user);
switch (sym->index) {
case F_graph :
gp = openG (args[0].string, xargs(args[1].string));
v.integer = PTR2INT(gp);
break;
case F_subg :
gp = INT2PTR(Agraph_t*,args[0].integer);
if (gp) {
gp = openSubg (gp, args[1].string);
v.integer = PTR2INT(gp);
}
break;
case F_issubg :
gp = INT2PTR(Agraph_t*,args[0].integer);
if (gp) {
v.integer = PTR2INT(agsubg (gp, args[1].string, 0));
}
else {
error (ERROR_WARNING, "NULL graph passed to isSubg()");
v.integer = 0;
}
break;
case F_fstsubg :
gp = INT2PTR(Agraph_t*,args[0].integer);
if (gp) {
gp = agfstsubg (gp);
v.integer = PTR2INT(gp);
}
else {
error (ERROR_WARNING, "NULL graph passed to fstsubg()");
v.integer = 0;
}
break;
case F_nxtsubg :
gp = INT2PTR(Agraph_t*,args[0].integer);
if (gp) {
gp = agnxtsubg (gp);
v.integer = PTR2INT(gp);
}
else {
error (ERROR_WARNING, "NULL graph passed to nxtsubg()");
v.integer = 0;
}
break;
case F_node :
gp = INT2PTR(Agraph_t*,args[0].integer);
if (gp) {
np = openNode (gp, args[1].string);
v.integer = PTR2INT(np);
}
else {
error (ERROR_WARNING, "NULL graph passed to node()");
v.integer = 0;
}
break;
case F_addnode :
gp = INT2PTR(Agraph_t*,args[0].integer);
np = INT2PTR(Agnode_t*,args[1].integer);
if (!gp) {
error (ERROR_WARNING, "NULL graph passed to addNode()");
v.integer = 0;
}
else if (!np) {
error (ERROR_WARNING, "NULL node passed to addNode()");
v.integer = 0;
}
else v.integer = PTR2INT(addNode(gp,np));
break;
case F_fstnode :
gp = INT2PTR(Agraph_t*,args[0].integer);
if (gp) {
np = agfstnode (gp);
v.integer = PTR2INT(np);
}
else {
error (ERROR_WARNING, "NULL graph passed to fstnode()");
v.integer = 0;
}
break;
case F_nxtnode :
np = INT2PTR(Agnode_t*,args[0].integer);
if (np) {
np = agnxtnode (np);
v.integer = PTR2INT(np);
}
else {
error (ERROR_WARNING, "NULL node passed to nxtnode()");
v.integer = 0;
}
break;
case F_isnode :
gp = INT2PTR(Agraph_t*,args[0].integer);
if (gp) {
v.integer = PTR2INT(agnode (gp, args[1].string, 0));
}
else {
error (ERROR_WARNING, "NULL graph passed to isNode()");
v.integer = 0;
}
break;
case F_isin :
gp = INT2PTR(Agraph_t*,args[0].integer);
objp = INT2PTR(Agobj_t*,args[1].integer);
if (!gp) {
error (ERROR_WARNING, "NULL graph passed to isIn()");
v.integer = 0;
}
else if (!objp) {
error (ERROR_WARNING, "NULL object passed to isIn()");
v.integer = 0;
}
else v.integer = isIn (gp, objp);
break;
case F_compof :
gp = INT2PTR(Agraph_t*,args[0].integer);
np = INT2PTR(Agnode_t*,args[1].integer);
if (!gp) {
error (ERROR_WARNING, "NULL graph passed to compOf()");
v.integer = 0;
}
else if (!np) {
error (ERROR_WARNING, "NULL node passed to compOf()");
v.integer = 0;
}
else v.integer = PTR2INT(compOf (gp, np));
break;
case F_edge :
key = args[2].string;
if (*key == '\0') key = 0;
np = INT2PTR(Agnode_t*,args[0].integer);
hp = INT2PTR(Agnode_t*,args[1].integer);
if (!np) {
error (ERROR_WARNING, "NULL tail node passed to edge()");
v.integer = 0;
}
else if (!hp) {
error (ERROR_WARNING, "NULL head node passed to edge()");
v.integer = 0;
}
else {
ep = openEdge (np, hp, key);
v.integer = PTR2INT(ep);
}
break;
case F_addedge :
gp = INT2PTR(Agraph_t*,args[0].integer);
ep = INT2PTR(Agedge_t*,args[1].integer);
if (!gp) {
error (ERROR_WARNING, "NULL graph passed to addEdge()");
v.integer = 0;
}
else if (!ep) {
error (ERROR_WARNING, "NULL edge passed to addEdge()");
v.integer = 0;
}
else v.integer = PTR2INT(addEdge(gp,ep));
break;
case F_isedge :
key = args[2].string;
if (*key == '\0') key = 0;
np = INT2PTR(Agnode_t*,args[0].integer);
hp = INT2PTR(Agnode_t*,args[1].integer);
if (!np) {
error (ERROR_WARNING, "NULL tail node passed to isEdge()");
v.integer = 0;
}
else if (!hp) {
error (ERROR_WARNING, "NULL head node passed to isEdge()");
v.integer = 0;
}
else v.integer = PTR2INT(isEdge (np, hp, key));
break;
case F_fstout :
np = INT2PTR(Agnode_t*,args[0].integer);
if (np) {
ep = agfstout (np);
v.integer = PTR2INT(ep);
}
else {
error (ERROR_WARNING, "NULL node passed to fstout()");
v.integer = 0;
}
break;
case F_nxtout :
ep = INT2PTR(Agedge_t*,args[0].integer);
if (ep) {
ep = agnxtout (ep);
v.integer = PTR2INT(ep);
}
else {
error (ERROR_WARNING, "NULL edge passed to nxtout()");
v.integer = 0;
}
break;
case F_fstin :
np = INT2PTR(Agnode_t*,args[0].integer);
if (np) {
ep = agfstin (np);
v.integer = PTR2INT(ep);
}
else {
error (ERROR_WARNING, "NULL node passed to fstin()");
v.integer = 0;
}
break;
case F_nxtin :
ep = INT2PTR(Agedge_t*,args[0].integer);
if (ep) {
ep = agnxtin (ep);
v.integer = PTR2INT(ep);
}
else {
error (ERROR_WARNING, "NULL edge passed to nxtin()");
v.integer = 0;
}
break;
case F_fstedge :
np = INT2PTR(Agnode_t*,args[0].integer);
if (np) {
ep = agfstedge (np);
v.integer = PTR2INT(ep);
}
else {
error (ERROR_WARNING, "NULL node passed to fstedge()");
v.integer = 0;
}
break;
case F_nxtedge :
ep = INT2PTR(Agedge_t*,args[0].integer);
np = INT2PTR(Agnode_t*,args[1].integer);
if (!ep) {
error (ERROR_WARNING, "NULL edge passed to nxtedge()");
v.integer = 0;
}
else if (!np) {
error (ERROR_WARNING, "NULL node passed to nxtedge()");
v.integer = 0;
}
else {
ep = agnxtedge (ep, np);
v.integer = PTR2INT(ep);
}
break;
case F_copy :
gp = INT2PTR(Agraph_t*,args[0].integer);
objp = INT2PTR(Agobj_t*,args[1].integer);
if (!objp) {
error (ERROR_WARNING, "NULL object passed to clone()");
v.integer = 0;
}
else v.integer = PTR2INT(copy (gp, objp));
break;
case F_clone :
gp = INT2PTR(Agraph_t*,args[0].integer);
objp = INT2PTR(Agobj_t*,args[1].integer);
if (!objp) {
error (ERROR_WARNING, "NULL object passed to clone()");
v.integer = 0;
}
else v.integer = PTR2INT(clone (gp, objp));
break;
case F_induce :
gp = INT2PTR(Agraph_t*,args[0].integer);
if (!gp) {
error (ERROR_WARNING, "NULL graph passed to induce()");
v.integer = 1;
}
else {
nodeInduce (gp);
v.integer = 0;
}
break;
case F_write :
gp = INT2PTR(Agraph_t*,args[0].integer);
if (!gp) {
error (ERROR_WARNING, "NULL graph passed to write()");
v.integer = 1;
}
else v.integer = agwrite (gp, state->outFile);
break;
case F_writeg :
gp = INT2PTR(Agraph_t*,args[0].integer);
if (!gp) {
error (ERROR_WARNING, "NULL graph passed to writeG()");
v.integer = 1;
}
else v.integer = writeFile (gp, args[1].string);
break;
case F_readg :
gp = readFile (args[0].string);
v.integer = PTR2INT(gp);
break;
case F_fwriteg :
gp = INT2PTR(Agraph_t*,args[0].integer);
if (!gp) {
error (ERROR_WARNING, "NULL graph passed to fwriteG()");
v.integer = 1;
}
else v.integer = fwriteFile (pgm, gp, args[1].integer);
break;
case F_freadg :
gp = freadFile (pgm, args[0].integer);
v.integer = PTR2INT(gp);
break;
case F_openf :
v.integer = openFile (pgm, args[0].string, args[1].string);
break;
case F_closef :
v.integer = closeFile (pgm, args[0].integer);
break;
case F_readl :
v.string = readLine (pgm, args[0].integer);
break;
case F_isdirect :
gp = INT2PTR(Agraph_t*,args[0].integer);
if (!gp) {
error (ERROR_WARNING, "NULL graph passed to isDirect()");
v.integer = 0;
}
else {
v.integer = agisdirected (gp);
}
break;
case F_isstrict :
gp = INT2PTR(Agraph_t*,args[0].integer);
if (!gp) {
error (ERROR_WARNING, "NULL graph passed to isStrict()");
v.integer = 0;
}
else {
v.integer = agisstrict (gp);
}
break;
case F_delete :
gp = INT2PTR(Agraph_t*,args[0].integer);
objp = INT2PTR(Agobj_t*,args[1].integer);
if (!objp) {
error (ERROR_WARNING, "NULL object passed to delete()");
v.integer = 1;
}
else if (objp == (Agobj_t*)(state->curgraph)) {
error (ERROR_WARNING, "cannot delete current graph $G");
v.integer = 1;
}
else if (objp == (Agobj_t*)(state->target)) {
error (ERROR_WARNING, "cannot delete target graph $T");
v.integer = 1;
}
else v.integer = deleteObj (gp, objp);
break;
case F_lock :
gp = INT2PTR(Agraph_t*,args[0].integer);
if (!gp) {
error (ERROR_WARNING, "NULL graph passed to lock()");
v.integer = -1;
}
else v.integer = lockGraph (gp, args[1].integer);
break;
case F_nnodes :
gp = INT2PTR(Agraph_t*,args[0].integer);
if (!gp) {
error (ERROR_WARNING, "NULL graph passed to nNodes()");
v.integer = 0;
}
else {
v.integer = agnnodes (gp);
}
break;
case F_nedges :
gp = INT2PTR(Agraph_t*,args[0].integer);
if (!gp) {
error (ERROR_WARNING, "NULL graph passed to nEdges()");
v.integer = 0;
}
else {
v.integer = agnedges (gp);
}
break;
case F_sqrt :
v.floating = sqrt (args[0].floating);
break;
case F_cos :
v.floating = cos (args[0].floating);
break;
case F_sin :
v.floating = sin (args[0].floating);
break;
case F_atan2 :
v.floating = atan2 (args[0].floating, args[1].floating);
break;
case F_exp :
v.floating = exp (args[0].floating);
break;
case F_pow :
v.floating = pow (args[0].floating, args[1].floating);
break;
case F_log :
v.floating = log (args[0].floating);
break;
case F_canon :
v.string = canon (pgm, args[0].string);
break;
case F_xof :
v.string = xyOf (pgm, args[0].string, 1);
break;
case F_yof :
v.string = xyOf (pgm, args[0].string, 0);
break;
case F_llof :
v.string = bbOf (pgm, args[0].string, 1);
break;
case F_urof :
v.string = bbOf (pgm, args[0].string, 0);
break;
case F_length :
v.integer = strlen (args[0].string);
break;
case F_index :
v.integer = indexOf (args[0].string, args[1].string);
break;
case F_match :
v.integer = match (args[0].string, args[1].string);
break;
default :
error (ERROR_FATAL, "unknown function call: %s", sym->name);
}
return v;
}
else if (elt == EX_ARRAY) {
args = (Extype_t*)env;
state = (Gpr_t*)(disc->user);
switch (sym->index) {
case A_ARGV :
v.string = getArg (args[0].integer, state);
break;
default :
error (ERROR_FATAL, "unknown array name: %s", sym->name);
}
return v;
}
state = (Gpr_t*)env;
if (ref) {
objp = deref (node, ref, 0, state);
if (!objp)
error (ERROR_FATAL, "null reference in expression %s",
deparse (pgm, node, state->tmp));
}
else if ((sym->lex == ID) && (sym->index <= LAST_V)) {
switch (sym->index) {
case V_this :
v.integer = PTR2INT(state->curobj);
break;
case V_thisg :
v.integer = PTR2INT(state->curgraph);
break;
case V_targt :
v.integer = PTR2INT(state->target);
break;
case V_outgraph :
v.integer = PTR2INT(state->outgraph);
break;
case V_tgtname :
v.string = state->tgtname;
break;
case V_infname :
v.string = state->infname;
break;
case V_ARGC :
v.integer = state->argc;
break;
case V_travtype :
v.integer = state->tvt;
break;
case V_travroot :
v.integer = PTR2INT(state->tvroot);
break;
}
return v;
}
else {
objp = state->curobj;
if (!objp)
error (ERROR_FATAL, "current object $ not defined as reference for %s",
deparse(pgm, node, state->tmp));
}
if (lookup (pgm, objp, sym, &v))
error (ERROR_FATAL, "in expression %s", deparse (pgm, node, state->tmp));
return v;
}
#define MINTYPE (LAST_M+1)
static char*
typeName (Expr_t* pg, int op)
{
return typenames[op - MINTYPE];
}
int
setval(Expr_t* pgm, Exnode_t* x, Exid_t* sym, Exref_t* ref,
void* env, int elt, Extype_t v, Exdisc_t* disc)
{
Gpr_t* state;
Agobj_t* objp;
Agsym_t* gsym;
Agnode_t* np;
int iv;
int rv = 0;
state = (Gpr_t*)env;
if (ref) {
objp = deref (x, ref, 0, state);
if (!objp)
error (ERROR_FATAL, "in expression %s", deparse (pgm, x, state->tmp));
}
else if ((MINNAME <= sym->index) && (sym->index <= MAXNAME)) {
switch (sym->index) {
case V_outgraph :
state->outgraph = INT2PTR(Agraph_t*,v.integer);
break;
case V_travtype :
iv = v.integer;
if (validTVT(v.integer))
state->tvt = (trav_type)iv;
else
error(1,"unexpected value %d assigned to %s : ignored",
iv, typeName(pgm, V_travtype));
break;
case V_travroot :
np = INT2PTR(Agnode_t*,v.integer);
if (!np || (agroot(agraphof(np)) == state->curgraph))
state->tvroot = np;
else {
error(1,"cannot set $tvroot, node %s not in $G : ignored",
agnameof(np));
}
break;
case V_tgtname :
if (!streq(state->tgtname,v.string)) {
vmfree (pgm->vm, state->tgtname);
state->tgtname = vmstrdup (pgm->vm, v.string);
state->name_used = 0;
}
break;
default :
rv = -1;
break;
}
return rv;
}
else {
objp = state->curobj;
if (!objp)
error (ERROR_FATAL, "current object $ undefined in expression %s",
deparse(pgm, x, state->tmp));
}
gsym = agattrsym (objp, sym->name);
if (!gsym) {
gsym = agattr(agroot(agraphof(objp)),AGTYPE(objp),sym->name,"");
}
return agxset (objp, gsym, v.string);
}
static int codePhase;
#define haveGraph ((1 <= codePhase) && (codePhase <= 4))
#define haveTarget ((2 <= codePhase) && (codePhase <= 4))
#define inWalk ((2 <= codePhase) && (codePhase <= 3))
static tctype
typeChk (tctype intype, Exid_t* sym)
{
tctype dom = 0, rng = 0;
switch (sym->lex) {
case DYNAMIC :
dom = 0;
switch (sym->type) {
case T_obj :
rng = YALL;;
break;
case T_node :
rng = Y(V);
break;
case T_graph :
rng = Y(G);
break;
case T_edge :
rng = Y(E);
break;
case INTEGER :
rng = Y(I);
break;
case FLOATING :
rng = Y(F);
break;
case STRING :
rng = Y(S);
break;
default :
exerror ( "unknown dynamic type %d of symbol %s", sym->lex, sym->name);
break;
}
break;
case ID :
if (sym->index <= MAXNAME) {
switch (sym->index) {
case V_travroot :
case V_this :
case V_thisg :
if (!haveGraph)
exerror ("keyword %s cannot be used in BEGIN/END statements",
sym->name);
break;
case V_targt :
if (!haveTarget)
exerror ("keyword %s cannot be used in BEGIN/BEG_G/END statements",
sym->name);
break;
}
dom = tchk[sym->index][0];
rng = tchk[sym->index][1];
}
else {
dom = YALL;
rng = Y(S);
}
break;
case NAME :
if (!intype && !haveGraph)
exerror ("undeclared, unmodified names like \"%s\" cannot be\nused in BEGIN and END statements",
sym->name);
dom = YALL;
rng = Y(S);
break;
default :
exerror ("unexpected symbol in typeChk: name %s, lex %d",
sym->name, sym->lex);
break;
}
if (dom) {
if (!intype) intype = Y(E)|Y(V);
if (!(dom & intype)) rng = 0;
}
else if (intype) rng = 0;
return rng;
}
static tctype
typeChkExp (Exref_t* ref, Exid_t* sym)
{
tctype ty;
if (ref) {
ty = typeChk (0, ref->symbol);
for (ref = ref->next; ty && ref; ref = ref->next)
ty = typeChk (ty, ref->symbol);
if (!ty) return 0;
}
else ty = 0;
return typeChk (ty, sym);
}
Extype_t
refval(Expr_t* pgm, Exnode_t* node, Exid_t* sym, Exref_t* ref,
char* str, int elt, Exdisc_t* disc)
{
Extype_t v;
if (sym->lex == CONSTANT) {
switch (sym->index) {
case C_dfs :
v.integer = TV_dfs;
break;
case C_flat :
v.integer = TV_flat;
break;
case C_fwd :
v.integer = TV_fwd;
break;
case C_rev :
v.integer = TV_rev;
break;
case C_ne :
v.integer = TV_ne;
break;
case C_en :
v.integer = TV_en;
break;
case C_null :
v.integer = 0;
break;
default :
v = exzero(node->type);
break;
}
}
else {
if (!typeChkExp (ref, sym))
exerror ("type error using %s", deparse (pgm, node, sfstropen()));
v = exzero(node->type);
}
return v;
}
#ifdef OLD
static void
cvtError (Exid_t* xref, char* msg)
{
if (xref)
error(1, "%s: %s", xref->name, msg);
else
error(1, "%s", msg);
}
#endif
int
binary(Expr_t* pg, Exnode_t* l, Exnode_t* ex, Exnode_t* r, int arg, Exdisc_t* disc)
{
Agobj_t* lobjp;
Agobj_t* robjp;
int ret = -1;
if (BUILTIN(l->type)) return -1;
if (r && BUILTIN(r->type)) return -1;
if (!INTEGRAL(ex->type)) return -1;
if (l->type == T_tvtyp) {
int li, ri;
if (!r) return -1;
if (r->type != T_tvtyp) return -1;
li = l->data.constant.value.integer;
ri = r->data.constant.value.integer;
switch (ex->op) {
case EQ :
if (arg) return 0;
l->data.constant.value.integer = (li == ri);
ret = 0;
break;
case NE :
if (arg) return 0;
l->data.constant.value.integer = (li != ri);
ret = 0;
break;
case '<':
if (arg) return 0;
l->data.constant.value.integer = (li < ri);
ret = 0;
break;
case LE:
if (arg) return 0;
l->data.constant.value.integer = (li <= ri);
ret = 0;
break;
case GE:
if (arg) return 0;
l->data.constant.value.integer = (li >= ri);
ret = 0;
break;
case '>':
if (arg) return 0;
l->data.constant.value.integer = (li > ri);
ret = 0;
break;
}
}
if (r && (r->type == T_tvtyp)) return -1;
lobjp = INT2PTR(Agobj_t*,l->data.constant.value.integer);
if (r)
robjp = INT2PTR(Agobj_t*,r->data.constant.value.integer);
else
robjp = 0;
switch (ex->op) {
case EQ :
if (arg) return 0;
l->data.constant.value.integer = !compare(lobjp,robjp);
ret = 0;
break;
case NE :
if (arg) return 0;
l->data.constant.value.integer = compare(lobjp,robjp);
ret = 0;
break;
case '<':
if (arg) return 0;
l->data.constant.value.integer = (compare(lobjp,robjp) < 0);
ret = 0;
break;
case LE:
if (arg) return 0;
l->data.constant.value.integer = (compare(lobjp,robjp) <= 0);
ret = 0;
break;
case GE:
if (arg) return 0;
l->data.constant.value.integer = (compare(lobjp,robjp) >= 0);
ret = 0;
break;
case '>':
if (arg) return 0;
l->data.constant.value.integer = (compare(lobjp,robjp) > 0);
ret = 0;
break;
}
return ret;
}
int
stringOf(Expr_t* prog, register Exnode_t* x, int arg)
{
Agobj_t* objp;
if (arg) return 0;
if (x->type == T_tvtyp) {
switch (x->data.constant.value.integer) {
case TV_flat :
x->data.constant.value.string = "TV_flat";
break;
case TV_dfs :
x->data.constant.value.string = "TV_dfs";
break;
case TV_fwd :
x->data.constant.value.string = "TV_fwd";
break;
case TV_rev :
x->data.constant.value.string = "TV_rev";
break;
default :
exerror("Unexpected value %d for type tvtype_t",
x->data.constant.value.integer);
break;
}
}
else {
objp = INT2PTR(Agobj_t*,x->data.constant.value.integer);
if (!objp)
exerror ("cannot generate name for NULL %s", typeName(prog, x->type));
x->data.constant.value.string = nameOf (prog, objp);
}
x->type = STRING;
return 0;
}
int
convert(Expr_t* prog, register Exnode_t* x, int type, register Exid_t* xref,
int arg, Exdisc_t* disc)
{
Agobj_t* objp;
int ret = -1;
if (BUILTIN(type) && BUILTIN(x->type)) return -1;
if ((type == T_obj) && (x->type <= T_obj))
ret = 0;
else if ((type <= T_obj) && (x->type == INTEGER)) {
if (x->data.constant.value.integer == 0)
ret = 0;
}
else if (type == INTEGER) {
ret = 0;
}
else if (x->type == T_obj) {
if (arg) {
if ((type != FLOATING) && (type <= T_obj)) ret = 0;
}
else {
objp = INT2PTR(Agobj_t*,x->data.constant.value.integer);
if (!objp)
ret = 0;
else switch (type) {
case T_graph :
if (AGTYPE(objp) == AGRAPH) ret = 0;
break;
case T_node :
if (AGTYPE(objp) == AGNODE) ret = 0;
break;
case T_edge :
if (ISEDGE(objp)) ret = 0;
break;
#ifdef OLD
case STRING :
x->data.constant.value.string = nameOf (prog, objp);
ret = 0;
break;
#endif
}
}
}
else if (type == STRING) {
if (x->type == T_tvtyp) {
ret = 0;
if (!arg) switch (x->data.constant.value.integer) {
case TV_flat :
x->data.constant.value.string = "TV_flat";
break;
case TV_dfs :
x->data.constant.value.string = "TV_dfs";
break;
case TV_fwd :
x->data.constant.value.string = "TV_fwd";
break;
case TV_rev :
x->data.constant.value.string = "TV_rev";
break;
default :
error(3, "Unexpected value %d for type tvtype_t",
x->data.constant.value.integer);
break;
}
}
#ifdef OLD
else {
objp = INT2PTR(Agobj_t*,x->data.constant.value.integer);
if (objp) {
x->data.constant.value.string = nameOf (prog, objp);
ret = 0;
}
else cvtError (xref, "Uninitialized object");
}
#endif
}
else if ((type == T_tvtyp) && (x->type == INTEGER)) {
if (arg) ret = 0;
else switch (x->data.constant.value.integer) {
case TV_flat :
case TV_dfs :
case TV_fwd :
case TV_rev :
break;
default :
error(1, "Integer value %d not legal for type tvtype_t",
x->data.constant.value.integer);
break;
}
}
else if (x->type == type) {
ret = 0;
}
else if (x->type == STRING) {
char* s;
if (type == T_tvtyp) {
if (arg) ret = 0;
else {
s = x->data.constant.value.string;
if (!strncmp(s,"TV_",3)) {
if (!strcmp(s+3,"flat")) {
x->data.constant.value.integer = TV_flat;
ret = 0;
}
else if (!strcmp(s+3,"dfs")) {
x->data.constant.value.integer = TV_dfs;
ret = 0;
}
else if (!strcmp(s+3,"fwd")) {
x->data.constant.value.integer = TV_fwd;
ret = 0;
}
else if (!strcmp(s+3,"rev")) {
x->data.constant.value.integer = TV_rev;
ret = 0;
}
else
error (ERROR_FATAL, "illegal string \"%s\" for type tvtype_t", s);
}
}
}
}
if (!arg && (ret == 0))
x->type = type;
return ret;
}
static Extype_t
keyval(Expr_t* pgm, Extype_t v, int type, Exdisc_t* disc)
{
if (type <= T_obj) {
v.integer = AGID (INT2PTR(Agobj_t*,v.integer));
}
return v;
}
static int
matchval(Expr_t* pgm, Exnode_t* xstr, const char* str, Exnode_t* xpat,
const char* pat, void* env, Exdisc_t* disc)
{
return strgrpmatch(str, pat, NiL, 0, STR_MAXIMAL|STR_LEFT|STR_RIGHT);
}
static int
a2t[] = { 0, FLOATING, INTEGER, STRING,
T_node, T_edge, T_graph, T_obj };
static Exdisc_t*
initDisc (Gpr_t* state)
{
Exdisc_t* dp;
dp = newof (0, Exdisc_t, 1, 0);
if (!dp)
error (ERROR_FATAL, "could not create libexp discipline: out of memory");
dp->version = EX_VERSION;
dp->flags = EX_CHARSTRING|EX_FATAL|EX_UNDECLARED;
dp->symbols = symbols;
dp->convertf = convert;
dp->stringof = stringOf;
dp->binaryf = binary;
dp->typename = typeName;
dp->errorf = (Exerror_f)errorf;
dp->keyf = keyval;
dp->getf = getval;
dp->reff = refval;
dp->setf = setval;
dp->matchf = matchval;
dp->types = a2t;
dp->user = state;
return dp;
}
static Exnode_t*
compile (Expr_t* prog, char* src, char* input, int line, char* lbl,
char* sfx, int kind)
{
Exnode_t* e;
Sfio_t* sf;
Sfio_t* prefix;
if (sfx) {
sf = sfopen (0, sfx, "rs");
if (input) {
prefix = sfopen (0, input, "rs");
sfstack (sf, prefix);
}
}
else sf = sfopen (0, input, "rs");
if (lbl) {
prefix = sfopen (0, 0, "sr+");
sfprintf (prefix, "%s:\n", lbl);
sfseek (prefix, 0,0);
sfstack (sf, prefix);
line--;
}
if (!src) src = "<command line>";
excomp (prog, src, line, 0, sf);
sfclose (sf);
e = exexpr(prog, lbl, NiL, kind);
return e;
}
static void
checkGuard (Exnode_t* gp, char* src, int line)
{
gp = exnoncast (gp);
if (gp && exisAssign(gp)) {
if (src) {
error_info.file = src;
error_info.line = line;
}
error (ERROR_WARNING, "assignment used as boolean in guard");
}
}
static case_stmt*
mkStmts (Expr_t* prog, char* src, case_info* sp, int cnt, char* lbl)
{
case_stmt* cs;
int i;
Sfio_t* tmps = sfstropen();
cs = newof (0, case_stmt, cnt, 0);
for (i = 0; i < cnt ; i++) {
if (sp->guard) {
sfprintf (tmps, "%s_g%d", lbl, i);
cs[i].guard = compile (prog, src, sp->guard, sp->gstart,
sfstruse(tmps), 0, INTEGER);
checkGuard (cs[i].guard, src, sp->gstart);
}
if (sp->action) {
sfprintf (tmps, "%s_a%d", lbl, i);
cs[i].action = compile (prog, src, sp->action, sp->astart,
sfstruse(tmps), 0, INTEGER);
}
sp = sp->next;
}
sfclose (tmps);
return cs;
}
static char*
doFlags (int flags, Sfio_t* s)
{
sfprintf (s, "\n");
if (flags & SRCOUT) sfprintf (s, "$O = $G;\n");
if (flags & INDUCE) sfprintf (s, "induce($O);\n");
return sfstruse (s);
}
comp_prog*
compileProg (parse_prog* inp, Gpr_t* state, int flags)
{
comp_prog* p;
Exdisc_t* dp;
Sfio_t* tmps = sfstropen();
char* endg_sfx = 0;
assert (BITS_PER_BYTE*sizeof(tctype) >= (1<<TBITS));
p = newof (0, comp_prog, 1, 0);
if (!p)
error (ERROR_FATAL, "could not create compiled program: out of memory");
if (flags) {
endg_sfx = doFlags (flags, tmps);
if (*endg_sfx == '\0') endg_sfx = 0;
}
dp = initDisc (state);
p->prog = exopen (dp);
codePhase = 0;
if (inp->begin_stmt)
p->begin_stmt = compile (p->prog, inp->source, inp->begin_stmt,
inp->l_begin, 0, 0, VOID);
codePhase = 1;
if (inp->begg_stmt)
p->begg_stmt = compile (p->prog, inp->source, inp->begg_stmt,
inp->l_beging, "_begin_g", 0, VOID);
codePhase = 2;
if (inp->node_stmts) {
tchk[V_this][1] = Y(V);
p->n_nstmts = inp->n_nstmts;
p->node_stmts = mkStmts (p->prog, inp->source, inp->node_stmts,
inp->n_nstmts, "_nd");
}
codePhase = 3;
if (inp->edge_stmts) {
tchk[V_this][1] = Y(E);
p->n_estmts = inp->n_estmts;
p->edge_stmts = mkStmts (p->prog, inp->source, inp->edge_stmts,
inp->n_estmts, "_eg");
}
codePhase = 4;
if (inp->endg_stmt || endg_sfx)
p->endg_stmt = compile (p->prog, inp->source, inp->endg_stmt,
inp->l_endg, "_end_g", endg_sfx, VOID);
codePhase = 5;
if (inp->end_stmt)
p->end_stmt = compile (p->prog, inp->source, inp->end_stmt,
inp->l_end, "_end_", 0, VOID);
sfclose (tmps);
error_info.line = 0;
return p;
}
int
walksGraph (comp_prog* p)
{
return (p->n_nstmts || p->n_estmts);
}
int
usesGraph (comp_prog* p)
{
return (walksGraph(p) || p->begg_stmt || p->endg_stmt);
}
void
ptchk ()
{
int i;
for (i = 0; i <= LAST_M; i++)
printf ("%d: %d %d\n", i, tchk[i][0], tchk[i][1]);
}
static int
iofread(void *chan, char *buf, int bufsize)
{
return read(sffileno((Sfio_t*)chan), buf, bufsize);
}
static int
ioputstr(void *chan, char *str)
{
return sfputr((Sfio_t*)chan, str, -1);
}
static int
ioflush(void *chan)
{
return sfsync((Sfio_t*)chan);
}
static Agiodisc_t gprIoDisc = { iofread, ioputstr, ioflush };
static Agdisc_t gprDisc = {&AgMemDisc, &AgIdDisc, &gprIoDisc};
Agraph_t*
readG (Sfio_t* fp)
{
Agraph_t* g;
g = agread(fp, &gprDisc);
if (g) {
aginit(g,AGRAPH,UDATA,sizeof(gdata),0);
aginit(g,AGNODE,UDATA,sizeof(ndata),0);
aginit(g,AGEDGE,UDATA,sizeof(edata),0);
}
return g;
}
Agraph_t*
openG (char* name, Agdesc_t desc)
{
Agraph_t* g;
g = agopen (name, desc, &gprDisc);
if (g) agbindrec (g,UDATA,sizeof(gdata),0);
return g;
}
Agraph_t*
openSubg (Agraph_t* g, char* name)
{
Agraph_t* sg;
sg = agsubg (g, name, 1);
if (sg && !aggetrec(sg,UDATA,0)) agbindrec (sg,UDATA,sizeof(gdata),0);
return sg;
}
Agnode_t*
openNode (Agraph_t* g, char* name)
{
Agnode_t* np;
np = agnode (g, name, 1);
if (np && !aggetrec(np,UDATA,0)) agbindrec (np,UDATA,sizeof(ndata),0);
return np;
}
Agedge_t*
openEdge (Agnode_t* t, Agnode_t* h, char* key)
{
Agedge_t* ep;
Agraph_t* root;
root = sameG(t, h, "openEdge", "tail and head node");
if (!root) return 0;
t = (Agnode_t*)agrebind (root, OBJ(t));
h = (Agnode_t*)agrebind (root, OBJ(h));
ep = agedge (t, h, key, 1);
if (ep && !aggetrec(ep,UDATA,0)) agbindrec (ep,UDATA,sizeof(edata),0);
return ep;
}