void zu::postfix_writer::do_if_node(zu::if_node * const node, int lvl) {
  int lbl1;
  node->condition()->accept(this, lvl);
  _pf.JZ(mklbl(lbl1 = ++_lbl));
  node->block()->accept(this, lvl + 2);
  _pf.LABEL(mklbl(lbl1));
}
Beispiel #2
0
void simple::postfix_writer::do_while_node(simple::while_node * const node, int lvl) {
  int lbl1, lbl2;
  _pf.LABEL(mklbl(lbl1 = ++_lbl));
  node->condition()->accept(this, lvl);
  _pf.JZ(mklbl(lbl2 = ++_lbl));
  node->block()->accept(this, lvl + 2);
  _pf.JMP(mklbl(lbl1));
  _pf.LABEL(mklbl(lbl2));
}
// Done
void pwn::postfix_writer::do_if_node(cdk::if_node * const node, int lvl) {
  CHECK_TYPES(_compiler, _symtab, node);
  int lbl1;
  node->condition()->accept(this, lvl);
  _pf.JZ(mklbl(lbl1 = ++_lbl));
  node->block()->accept(this, lvl + 2);
  _pf.ALIGN();
  _pf.LABEL(mklbl(lbl1));
}
void zu::postfix_writer::do_double_node(cdk::double_node * const node, int lvl) {
    _pf.RODATA();
    _pf.ALIGN();
    _pf.LABEL(mklbl(++_lbl));
    _pf.DOUBLE(node->value());
    _pf.TEXT();
    _pf.ADDR(mklbl(_lbl));
    _pf.DLOAD();
}
void zu::postfix_writer::do_or_node(zu::or_node * const node, int lvl){
 int lbl1;
    CHECK_TYPES(_compiler, _symtab, node);
  node->left()->accept(this, lvl);
  _pf.DUP();                            // if it is true, then do not eval the next expression
  _pf.JNZ(mklbl(lbl1 = ++_lbl));    
  node->right()->accept(this, lvl);
  _pf.OR();
  _pf.LABEL(mklbl(lbl1));
}
void zu::postfix_writer::do_if_else_node(zu::if_else_node * const node, int lvl) {
  int lbl1, lbl2;
  node->condition()->accept(this, lvl);
  _pf.JZ(mklbl(lbl1 = ++_lbl));
  node->thenblock()->accept(this, lvl + 2);
  _pf.JMP(mklbl(lbl2 = ++_lbl));
  _pf.LABEL(mklbl(lbl1));
  node->elseblock()->accept(this, lvl + 2);
  _pf.LABEL(mklbl(lbl1 = lbl2));
}
void pwn::postfix_writer::do_or_node(pwn::or_node * const node, int lvl) { 
  CHECK_TYPES(_compiler, _symtab, node);
  int lbl1;
  node->left()->accept(this, lvl);
  _pf.DUP();
  _pf.JNZ(mklbl(lbl1 = ++_lbl));
//   _pf.TRASH(4);
  node->right()->accept(this, lvl);
  _pf.OR();
  _pf.LABEL(mklbl(lbl1));
}
void zu::postfix_writer::do_string_node(cdk::string_node * const node, int lvl) {
  int lbl1;

  /* generate the string */
  _pf.RODATA(); // strings are DATA readonly
  _pf.ALIGN(); // make sure we are aligned
  _pf.LABEL(mklbl(lbl1 = ++_lbl)); // give the string a name
  _pf.STR(node->value()); // output string characters

  /* leave the address on the stack */
  _pf.TEXT(); // return to the TEXT segment
  _pf.ADDR(mklbl(lbl1)); // the string to be printed
}
  //NEW NODES
void pwn::postfix_writer::do_and_node(pwn::and_node * const node, int lvl) { 
  CHECK_TYPES(_compiler, _symtab, node);
  int lbl1;
  node->left()->accept(this, lvl);
  _pf.INT(1);
  _pf.AND();
  _pf.DUP();
  _pf.JZ(mklbl(lbl1 = ++_lbl));
  node->left()->accept(this, lvl);
  node->right()->accept(this, lvl);
  _pf.AND();
  _pf.LABEL(mklbl(lbl1));
}
void pwn::postfix_writer::do_string_node(cdk::string_node * const node, int lvl) {
  int lbl1;
  if (_args || !_insidefunc)
    _pf.STR(node->value());
  else {
    _pf.RODATA();
    _pf.ALIGN();
    _pf.LABEL(mklbl(lbl1 = ++_lbl));
    _pf.STR(node->value());
    _pf.TEXT();
    _pf.ALIGN();
    _pf.ADDR(mklbl(lbl1));
  }
}
Beispiel #11
0
void zu::postfix_writer::do_or_node(zu::or_node * const node, int lvl) {
    int lbl = ++_lbl;
    CHECK_TYPES(_compiler, _symtab, node);
    node->left()->accept(this, lvl+2);
    _pf.DUP();
    _pf.JZ(mklbl(lbl));
    
    node->right()->accept(this, lvl+2);
    _pf.DUP();
    
    _pf.OR();
    
    _pf.ALIGN();
    _pf.LABEL(mklbl(lbl));
}
Beispiel #12
0
// Done
void pwn::postfix_writer::do_not_node(pwn::not_node * const node, int lvl) {
  CHECK_TYPES(_compiler, _symtab, node);

  int lbl1 = ++_lbl;
  int lbl_end = ++_lbl;
  
  node->argument()->accept(this, lvl + 1);
  _pf.JZ(mklbl(lbl1));
  _pf.INT(0);
  _pf.JMP(mklbl(lbl_end));
  _pf.ALIGN();
  _pf.LABEL(mklbl(lbl1));
  _pf.INT(1);
  _pf.ALIGN();
  _pf.LABEL(mklbl(lbl_end));
}
Beispiel #13
0
Datei: node.c Projekt: oridb/mc
Node *
genlbl(Srcloc loc)
{
	char buf[128];

	genlblstr(buf, 128, "");
	return mklbl(loc, buf);
}
Beispiel #14
0
// Done
void pwn::postfix_writer::do_and_node(pwn::and_node * const node, int lvl) {
  CHECK_TYPES(_compiler, _symtab, node);
  
  int lbl0 = ++_lbl;
  int lbl_end = ++_lbl;
  
  node->left()->accept(this, lvl + 1);
  _pf.JZ(mklbl(lbl0));
  node->right()->accept(this, lvl + 1);
  _pf.JZ(mklbl(lbl0));
  _pf.INT(1);
  _pf.JMP(mklbl(lbl_end));
  _pf.ALIGN();
  _pf.LABEL(mklbl(lbl0));
  _pf.INT(0);
  _pf.ALIGN();
  _pf.LABEL(mklbl(lbl_end));
}
Beispiel #15
0
// Done
void pwn::postfix_writer::do_double_node(cdk::double_node * const node, int lvl) {
  CHECK_TYPES(_compiler, _symtab, node);
  if (_current_function) {
    // local scope
    _pf.RODATA();
    _pf.ALIGN();
    _pf.LABEL(mklbl(++_lbl));
    _pf.DOUBLE(node->value());
    _pf.TEXT();
    _pf.ADDR(mklbl(_lbl));
    _pf.DLOAD();
  } else {
    // global scope
  	_pf.ALIGN();
  	_pf.LABEL(*_current_id);
    _pf.DOUBLE(node->value());
  }
}
void pwn::postfix_writer::do_double_node(cdk::double_node * const node, int lvl) {
  int lbl1;
  if (!_insidefunc) {
    _pf.RODATA();
    _pf.ALIGN();
    _pf.LABEL(mklbl(lbl1 = ++_lbl));
    _pf.DOUBLE(node->value());
    _pf.TEXT();
    _pf.ADDR(mklbl(lbl1));
  }
  else if (_insidefunc && !_args)
    _pf.RODATA();
    _pf.ALIGN();
    _pf.LABEL(mklbl(lbl1 = ++_lbl));
    _pf.DOUBLE(node->value());
    _pf.TEXT();
    _pf.ADDR(mklbl(lbl1));
    _pf.DLOAD();
}
Beispiel #17
0
// Done
void pwn::postfix_writer::do_string_node(cdk::string_node * const node, int lvl) {
  CHECK_TYPES(_compiler, _symtab, node);
  int lbl1;

  /* generate the string */
  _pf.RODATA(); // strings are DATA readonly
  _pf.ALIGN(); // make sure we are aligned
  _pf.LABEL(mklbl(lbl1 = ++_lbl)); // give the string a name
  _pf.STR(node->value()); // output string characters

  if (_current_function) {
    // local scope
    _pf.TEXT(); // return to the TEXT segment
    _pf.ADDR(mklbl(lbl1)); // the string to be printed
  } else {
    // global scope
    _pf.ALIGN();
    _pf.LABEL(*_current_id); 
    _pf.ID(mklbl(lbl1));
  }
}
void zu::postfix_writer::do_double_node(cdk::double_node * const node, int lvl) {
    CHECK_TYPES(_compiler, _symtab, node);
  int lbl1;

  if (_am_I_in_function) { 				//Contexto local
    _pf.RODATA();
    _pf.ALIGN();
    _pf.LABEL(mklbl(lbl1 = ++_lbl));
    _pf.DOUBLE(node->value());
    _pf.TEXT();
    _pf.ALIGN();
    _pf.ADDR(mklbl(lbl1));
    _pf.DLOAD();
  }
  else {																		//Contexto global
    _pf.DATA();
    _pf.ALIGN();
    _pf.LABEL(_id_to_label);
    _pf.DOUBLE(node->value());
  }
}
Beispiel #19
0
Datei: cfg.c Projekt: 8l/mc
static Bb *addlabel(Cfg *cfg, Bb *bb, Node **nl, size_t i, Srcloc loc)
{
	/* if the current block assumes fall-through, insert an explicit jump */
	if (i > 0 && nl[i - 1]->type == Nexpr) {
		if (exprop(nl[i - 1]) != Ocjmp && exprop(nl[i - 1]) != Ojmp)
			addnode(cfg, bb, mkexpr(loc, Ojmp, mklbl(loc, lblstr(nl[i])), NULL));
	}
	if (bb->nnl)
		bb = mkbb(cfg);
	label(cfg, nl[i], bb);
	return bb;
}
void pwn::postfix_writer::do_identity_node(pwn::identity_node * const node, int lvl) {
  
  int lbl1;
  CHECK_TYPES(_compiler, _symtab, node);
  node->argument()->accept(this, lvl); // determine the value
  //TODO check this function
  if (node->type()->name() == basic_type::TYPE_DOUBLE) {
    _pf.DDUP();
    _pf.DOUBLE(0);
    _pf.LT();
    _pf.JZ(mklbl(lbl1 = ++_lbl));
    _pf.DNEG();
    _pf.LABEL(mklbl(lbl1));
  }
  if (node->type()->name() == basic_type::TYPE_INT) {
    _pf.DUP();
    _pf.INT(0);
    _pf.LT();
    _pf.JZ(mklbl(lbl1 = ++_lbl));
    _pf.NEG(); // 2-complement
    _pf.LABEL(mklbl(lbl1));
  }
}
Beispiel #21
0
void zu::postfix_writer::do_for_node(zu::for_node * const node, int lvl) {
    int cond, end, inc;
    std::string scond = mklbl(cond = ++_lbl);
    std::string send  = mklbl(end  = ++_lbl);
    std::string sinc  = mklbl(inc  = ++_lbl);
    _break_labels.push_back(send);
    _continue_labels.push_back(sinc);
    
    if(node->init() != nullptr)
        node->init()->accept(this, lvl + 2);
    
    _pf.ALIGN();
    _pf.LABEL(scond);

    if(node->condition() != nullptr){
        node->condition()->accept(this, lvl +2 );
        _pf.JZ(send);
    }
    
    
    node->block()->accept(this, lvl + 2);
    
    _pf.ALIGN();
    _pf.LABEL(sinc);
    if(node->increment() != nullptr)
        node->increment()->accept(this, lvl + 2);
    
    _pf.JMP(scond);
    
    _pf.ALIGN();
    _pf.LABEL(send);
    
    _break_labels.pop_back();
    _continue_labels.pop_back();
    
    
}
Beispiel #22
0
// Maybe done - i think it's done but it's untested
void pwn::postfix_writer::do_repeat_node(pwn::repeat_node * const node, int lvl) {
  CHECK_TYPES(_compiler, _symtab, node);
  int lbl1 = ++_lbl;
  int lbl2 = ++_lbl; _repeat_increment.push_back(lbl2);
  int lbl3 = ++_lbl; _repeat_end.push_back(lbl3);
  
  if (node->inicializations()) {
    node->inicializations()->accept(this, lvl); // init repeat    
  }
  _pf.ALIGN();
  _pf.LABEL(mklbl(lbl1)); // condition label
  node->conditions()->accept(this, lvl);
  _pf.JZ(mklbl(lbl3)); // jump if condition is false
  node->block()->accept(this, lvl + 2);
  _pf.ALIGN();
  _pf.LABEL(mklbl(lbl2)); // increment label
  node->updates()->accept(this, lvl);
  _pf.JMP(mklbl(lbl1)); // go to condition label
  _pf.ALIGN();
  _pf.LABEL(mklbl(lbl3)); // end cycle
  
  _repeat_increment.pop_back();
  _repeat_end.pop_back();
}
Beispiel #23
0
void pwn::postfix_writer::do_stop_node(pwn::stop_node * const node, int lvl) {
  CHECK_TYPES(_compiler, _symtab, node);

  unsigned int n = node->value();
  int lbl;
  
  if (n > _repeat_end.size()) {
    throw std::string("Error: there are not enough active cycles");
  }
  
  for (unsigned int i = 0; i < n; i++) {
    lbl = _repeat_end.back();
    _repeat_increment.pop_back();
    _repeat_end.pop_back();
  }
  
  _pf.JMP(mklbl(lbl));
}
void pwn::postfix_writer::do_repeat_node(pwn::repeat_node * const node, int lvl) {
  int condition = ++_lbl;
  int increment = ++_lbl;
  int end = ++_lbl;
  _symtab.push();
  _stack.push_back(mklbl(end));
  _stack.push_back(mklbl(increment));
  node->init()->accept(this, lvl);
  _pf.LABEL(mklbl(condition));
  node->condition()->accept(this, lvl);
  _pf.JZ(mklbl(end));
  node->block()->accept(this, lvl);
  _pf.LABEL(mklbl(increment));
  node->incr()->accept(this, lvl);
  _pf.JMP(mklbl(condition));
  _pf.LABEL(mklbl(end));
  _stack.pop_back();
  _stack.pop_back();
  _symtab.pop();
}
void zu::postfix_writer::do_for_node(zu::for_node * const node, int lvl) {
  CHECK_TYPES(_compiler, _symtab, node);
  int lbl1, lbl2, lbl3;
  if(node->init() != nullptr)
      node->init()->accept(this, lvl);
 
  _pf.LABEL(mklbl(lbl1 = ++_lbl));
  if(node->cond() != nullptr)
    node->cond()->accept(this, lvl);
  else
      (new cdk::integer_node(lvl, 1))->accept(this, lvl); 
  _pf.JZ(mklbl(lbl2 = ++_lbl));
  
  _break_lbls.push_back(mklbl(lbl2));
  _continue_lbls.push_back(mklbl(lbl3 = ++_lbl));
  
  node->block()->accept(this, lvl + 2);
  _pf.LABEL(mklbl(lbl3));
  if(node->incr() != nullptr)
      node->incr()->accept(this, lvl);
  
  _pf.JMP(mklbl(lbl1));
  _pf.LABEL(mklbl(lbl2));
}
Beispiel #26
0
void pwn::postfix_writer::do_return_node(pwn::return_node * const node, int lvl) {
  CHECK_TYPES(_compiler, _symtab, node);

  _pf.JMP(mklbl(_current_function_end));
}
void zu::postfix_writer::do_return_node(zu::return_node * const node, int lvl){
    _pf.JMP(mklbl(_lbl_return));
    
}
void zu::postfix_writer::do_function_definition_node(zu::function_definition_node * const node, int lvl) {
  CHECK_TYPES(_compiler, _symtab, node);
  std::string name = "";
  if(*(node->identifier()->identifier()) == "zu") name = "_main";
  else if(*(node->identifier()->identifier()) == "_main") name = "zu";
  else name = *(node->identifier()->identifier());
  
  delete_extern(name); //only removes if it exists
  // generate the main function (RTS mandates that its name be "_main")
  _pf.TEXT();
  _pf.ALIGN();
  if(node->visibility()) _pf.GLOBAL("_main", _pf.FUNC());
  _pf.LABEL(name);
  
  declarations_counter dc(_compiler, _symtab, _pf);
  
  node->accept(&dc, lvl);
  size_t declars_size = dc.get_size();
  
  size_t retsize = 0;
  if(node->return_type() != nullptr)
    retsize = node->return_type()->size();
  
  _pf.ENTER(declars_size); 
  
  _max_offset = declars_size ;
  _am_I_in_function = true;
  if(node->literal() != nullptr){
    node->literal()->accept(this, lvl+2);
    _pf.LOCA(-retsize);
  }
  _symtab.push(); //args
  
  _arg_offset =  8;
  if(node->args() != nullptr){
      _is_arg = true;
    node->args()->accept(this, lvl+2);
      _is_arg = false;
  }
  mklbl(_lbl_return = ++_lbl);
  node->body()->accept(this, lvl);

  
  _pf.LABEL(mklbl(_lbl_return));
  // end of function
    if(node->return_type() != nullptr){
        _pf.LOCAL(-retsize); //tirar o tipo de retorno (tamanho)
        if(node->return_type()->name() != basic_type::TYPE_DOUBLE){
            _pf.LOAD();
            _pf.POP();
        }else{ // doubles
            _pf.DLOAD();
            _pf.DPOP();
        }
   }
  _pf.LEAVE();
  _pf.RET();
  _max_offset = 0;
  
  _am_I_in_function = false;
  _arg_offset =  8;
  _symtab.pop(); //args

}
Beispiel #29
0
void pwn::postfix_writer::do_function_definition_node(pwn::function_definition_node * const node, int lvl) {
  CHECK_TYPES(_compiler, _symtab, node);

  _current_function_end = ++_lbl;

  _pf.TEXT();
  _pf.ALIGN();
  std::string name = fix_id(node->name());
  
  // is global ?
  if ( ! node->is_local()) {
    _pf.GLOBAL(name, _pf.FUNC());    
  }
  
  _pf.LABEL(name);
  
  // find space needed for local variables
  pwn::enter_size *visitor = new pwn::enter_size(_compiler);
  node->accept(visitor, 0);
  int size = visitor->size();
  delete visitor;
  
  // reserve memory
  _pf.ENTER(size);

  // set current function
  _current_function = _symtab.find_local(node->name());
  
  // create new context
  _symtab.push();

  if (node->arguments()) {
    _current_offset = 8;  // function arguments offset
    node->arguments()->accept(this, lvl + 1);
  }
  
  _current_offset = 0; // local variables offset
  
  // reserve space for return variable
  int return_size = 0;
  if (is_int(node->return_type()) || is_string(node->return_type()) || is_pointer(node->return_type())) {
    return_size = 4; // int, string, pointer
  } else if (is_double(node->return_type())) {
    return_size = 8; // double
  }
  
  _current_offset -= return_size;
  
  // handle default return value
  if (node->return_default()) {
    node->return_default()->accept(this, lvl + 1);
    if (return_size == 4) {
      _pf.LOCA(-4);
    } else if (return_size == 8) {
      _pf.LOCAL(-8);
      _pf.DSTORE();
    }
  } else {
    // if no return default is specified, return 0 if int or pointer
    _pf.INT(0);
    _pf.LOCA(-4);
  }
  
  // function body
  node->block()->accept(this, lvl);
  
  _pf.ALIGN();
  _pf.LABEL(mklbl(_current_function_end));

  // put return value in eax
  if (return_size == 4) {
    // int string pointer
    _pf.LOCV(-4);
    _pf.POP();
  } else if (return_size == 8) {
    // double
    _pf.LOCAL(-8);
    _pf.DLOAD();
    _pf.DPOP();
  }

  // return from function
  _pf.LEAVE();
  _pf.RET();
  
  // leave local context
  _symtab.pop(); 
  _current_function = nullptr;

  // add this function to defined functions list
  _defined_functions.push_back(new std::string(name));
}
Beispiel #30
0
static void eval(Node *p) {
    int i, lbl1, lbl2;
    char *name;

    if (p == 0) return;
    switch(p->attrib) {
    case INTEGER:
        fprintf(out, pfIMM, p->value.i);	/* push an integer immediate */
        break;
    case STRING:
	/* generate the string */
	fprintf(out, pfRODATA);			/* strings are DATA readonly  */
	fprintf(out, pfALIGN);			/* make sure we are aligned   */
	fprintf(out, pfLABEL, mklbl(lbl1 = ++lbl));	/* name the string    */
	fprintf(out, pfSTR, p->value.s);	/* output string characters   */
	/* make the call */
	fprintf(out, pfTEXT);			/* return to the TEXT segment */
	fprintf(out, pfADDR, mklbl(lbl1));	/* the string to be printed   */
	fprintf(out, pfCALL, "_prints");	/* call the print rotine      */
	fprintf(out, pfCALL, "_println");	/* print a newline	      */
	fprintf(out, pfTRASH, sizeof(regint));		/* remove the string address: 4/8 bytes    */
        break;
    case VARIABLE:
	if (IDfind(p->value.s, 0) >= 0) {
	  fprintf(out, pfADDR, p->value.s);
	  fprintf(out, pfLOAD);
	}
	break;
    case WHILE:
	fprintf(out, pfLABEL, mklbl(lbl1 = ++lbl));
	eval(p->SUB(0));
	fprintf(out, pfJZ, mklbl(lbl2 = ++lbl));
	eval(p->SUB(1));
	fprintf(out, pfJMP, mklbl(lbl1));
	fprintf(out, pfLABEL, mklbl(lbl2));
	break;
    case IF:
	eval(p->SUB(0));
	fprintf(out, pfJZ, mklbl(lbl1 = ++lbl));
	eval(p->SUB(1));
	if (p->value.sub.num > 2) { /* if else */
	    fprintf(out, pfJMP, mklbl(lbl2 = ++lbl));
	    fprintf(out, pfLABEL, mklbl(lbl1));
	    eval(p->SUB(2));
	    lbl1 = lbl2;
	}
	fprintf(out, pfLABEL, mklbl(lbl1));
	break;
    case READ:
	if (IDfind(p->value.s, 0) >= 0) {
	  fprintf(out, pfCALL, "_readi");
	  fprintf(out, pfPUSH);
	  fprintf(out, pfADDR, p->value.s);
	  fprintf(out, pfSTORE);
	}
	break;
    case PRINT:
	eval(p->SUB(0));			/* determine the value        */
	fprintf(out, pfCALL, "_printi");	/* call the print function    */
	fprintf(out, pfCALL, "_println");	/* print a newline	      */
	fprintf(out, pfTRASH, sizeof(regint));		/* remove the printed value: 4/8 bytes   */
	break;
    case ';':
	for (i = 0; i < p->value.sub.num; i++)
	  eval(p->SUB(i));
	break;
    case '=':
	name = p->SUB(0)->value.s;
	if (IDfind(name, (int*)IDtest) == -1) { /* variable not found ?       */
	  fprintf(out, pfDATA);			/* variables are DATA         */
	  fprintf(out, pfALIGN);		/* make sure we are aligned   */
	  fprintf(out, pfLABEL, name);		/* name variable location     */
#ifdef _64bits_
	  fprintf(out, pfLONG, 0LL);		/* initialize it to 0 (zero)  */
#else
	  fprintf(out, pfINTEGER, 0);		/* initialize it to 0 (zero)  */
#endif
	  fprintf(out, pfTEXT);			/* return to the TEXT segment */
	  IDnew(INTEGER, name, 0);		/* put in the symbol table    */
	}
	eval(p->SUB(1));			/* determine the new value    */
	fprintf(out, pfADDR, name);		/* where to store the value   */
	fprintf(out, pfSTORE);			/* store the value at address */
	break;
    case UMINUS:
	eval(p->SUB(0));			/* determine the value	      */
	fprintf(out, pfNEG);			/* make the 2-compliment      */
	break;
    default:
	eval(p->SUB(0));			/* evaluate first argument    */
	eval(p->SUB(1));			/* evaluate second argument   */
	switch(p->attrib) {			/* make the operation ...     */
	case '+':   fprintf(out, pfADD); break;
	case '-':   fprintf(out, pfSUB); break;
	case '*':   fprintf(out, pfMUL); break;
	case '/':   fprintf(out, pfDIV); break;
	case '%':   fprintf(out, pfMOD); break;
	case '<':   fprintf(out, pfLT); break;
	case '>':   fprintf(out, pfGT); break;
	case GE:    fprintf(out, pfGE); break;
	case LE:    fprintf(out, pfLE); break;
	case NE:    fprintf(out, pfNE); break;
	case EQ:    fprintf(out, pfEQ); break;
	default:    printf("unknown %d\n", p->attrib);
	}
    }
}