A datatype definition can be refined in a few ways:
For example, the following datatype Exp is inherited from some user
defined classes AST and Object. Furthermore, a virtual
member function called print is defined.
datatype Exp : public AST, virtual public Object
= INT int
with { ostream& print(ostream&); }
| PLUS Exp, Exp
with { ostream& print(ostream&); }
| MINUS Exp, Exp
with { ostream& print(ostream&); }
| MULT Exp, Exp
with { ostream& print(ostream&); }
| DIV Exp, Exp
with { ostream& print(ostream&); }
| VAR { name : const char *, typ : Type }
with { ostream& print(ostream&); }
public:
{ virtual ostream& print (ostream&) = 0;
void * operator new (size_t);
}
;
The allocator and printing methods can be defined as follows:
void * classof Exp::operator new(size_t) { ... }
ostream& classof INT::print(ostream& s) { return s << INT; }
ostream& classof PLUS::print(ostream& s)
{ return s << '(' << this->#1 << '+' << this->#2 <<')'; }
ostream& classof MINUS::print(ostream& s)
{ return s << '(' << this->#1 << '-' << this->#2 <<')'; }
ostream& classof MULT::print(ostream& s)
{ return s << '(' << this->#1 << '*' << this->#2 <<')'; }
ostream& classof DIV::print(ostream& s)
{ return s << '(' << this->#1 << '/' << this->#2 <<')'; }
ostream& classof VAR::print(ostream& s) { return s << name; }
The special type form classof con is used to reference
the type of a constructor. Arguments of a constructor uses the following
naming convention: (i) if the constructor C takes only one argument,
then the name of the argument is C; (ii) if C takes two or more
unnamed arguments, then these are named #1, #2, etc;
(iii) finally, labeled arguments are given the same name as the label.
For readability reasons, it is often useful to separate a datatype definition from the member functions and inheritance definitions. The refine declaration can be used in this situation. For example, the above example can be restated more succinctly as follows:
//
// Datatype definition section
//
datatype Exp = INT int
| PLUS Exp, Exp
| MINUS Exp, Exp
| MULT Exp, Exp
| DIV Exp, Exp
| VAR { name : const char *, typ : Type }
;
//
// Refinement section
//
refine Exp : public AST, virtual public Object
{ virtual ostream& print (ostream&) = 0;
void * operator new (size_t);
}
and INT, PLUS, MINUS, MULT, DIV, VAR
{ ostream& print(ostream&);
}
;
The general syntax of the refine declaration is as follows:
Here, Id refers to either a constructor name or a datatype name.
Refine_Decl ::= refine Refine_Spec and ¼ and Refine_Spec ; Refine_Spec ::= Id, ¼ , Id [ : Inherit_List ] [ :: Datatype_Qualifiers ] [ { Datatype_Body } ]