I'm working on a parsing application and I have to deal with both terminal and non-terminal nodes in the model. The terminal nodes (tokens) can have values of different types, expressed as an object variant:
type
TokenKind = enum
IntegerNumber,
FloatNumber,
StringToken,
Plus,
Minus,
Multiply,
Divide,
#...
Location = object
line : int
column : int
TokenValue = object
case kind: TokenKind
of FloatNumber: floatValue: float64
of StringToken: stringValue: string
else: intValue: int64
Token = ref object of RootObj
text: string
value: TokenValue
But the non-terminal nodes also have a "kind-of", for example
type
ASTNode = ref object of RootObj
kind: TokenKind # could be Plus, Minus, etc.
startpos: Location
endpos: Location
# ...
BinaryNode = ref object of ASTNode
lhs, rhs: ASTNode
I'd like to unify ASTNode and Token so that Token inherits from ASTNode, but the kind is already "embedded" in the TokenValue. To unify the two things, does that mean that storage for kind would be duplicated (one in the ASTNode, one in the TokenValue), or is there some way of avoiding this by somehow disconnecting the discriminator field from the discriminated fields?
short answer: no, you can't redefine a field in an inherited object, or in variant branches, even if it's the same type
I hate not being able to give someone the answer they were looking for, so now, my eagerness to please souring to resentment, I will tell you you are wrong for asking the question in the first place, and assure you that you don't want to do it that way.
ahem.
never mind duplicating storage, you're duplicating the inheritance tree in the kind field. will you use if x is BinaryNode or if x.kind in {Plus,Minus,Multiply,Divide}? What if they disagree?
I suggest making ASTNode the variant
const BinaryKinds = {Plus,Minus,Multiply,Divide}
type ASTNode = ref object
startPos,endPos: Location
case kind:NodeKind
of FloatNumber: floatValue: float64
of StringToken: stringValue: string
of IntToken: intValue: int64
of BinaryKinds: lhs,rhs:ASTNode