Implement `int*` and `uint*` builtin functinos
Opened this issue · 2 comments
antonmedv commented
We already have int() and float() builtins. Let's add others as well: uint8, byte, int64, float64, etc.
And we can change compiler to compile int for IntegerNode, and remove this code:
func (c *compiler) IntegerNode(node *ast.IntegerNode) {
t := node.Type()
if t == nil {
c.emitPush(node.Value)
return
}
switch t.Kind() {
case reflect.Float32:
c.emitPush(float32(node.Value))
case reflect.Float64:
c.emitPush(float64(node.Value))
case reflect.Int:
c.emitPush(node.Value)
case reflect.Int8:
if node.Value > math.MaxInt8 || node.Value < math.MinInt8 {
panic(fmt.Sprintf("constant %d overflows int8", node.Value))
}
c.emitPush(int8(node.Value))
case reflect.Int16:
if node.Value > math.MaxInt16 || node.Value < math.MinInt16 {
panic(fmt.Sprintf("constant %d overflows int16", node.Value))
}
c.emitPush(int16(node.Value))
case reflect.Int32:
if node.Value > math.MaxInt32 || node.Value < math.MinInt32 {
panic(fmt.Sprintf("constant %d overflows int32", node.Value))
}
c.emitPush(int32(node.Value))
case reflect.Int64:
if node.Value > math.MaxInt64 || node.Value < math.MinInt64 {
panic(fmt.Sprintf("constant %d overflows int64", node.Value))
}
c.emitPush(int64(node.Value))
case reflect.Uint:
if node.Value < 0 {
panic(fmt.Sprintf("constant %d overflows uint", node.Value))
}
c.emitPush(uint(node.Value))
case reflect.Uint8:
if node.Value > math.MaxUint8 || node.Value < 0 {
panic(fmt.Sprintf("constant %d overflows uint8", node.Value))
}
c.emitPush(uint8(node.Value))
case reflect.Uint16:
if node.Value > math.MaxUint16 || node.Value < 0 {
panic(fmt.Sprintf("constant %d overflows uint16", node.Value))
}
c.emitPush(uint16(node.Value))
case reflect.Uint32:
if node.Value > math.MaxUint32 || node.Value < 0 {
panic(fmt.Sprintf("constant %d overflows uint32", node.Value))
}
c.emitPush(uint32(node.Value))
case reflect.Uint64:
if node.Value < 0 {
panic(fmt.Sprintf("constant %d overflows uint64", node.Value))
}
c.emitPush(uint64(node.Value))
default:
c.emitPush(node.Value)
}
}
And for function calls we can delete this code:
func traverseAndReplaceIntegerNodesWithIntegerNodes(node *ast.Node, newType reflect.Type) {
switch (*node).(type) {
case *ast.IntegerNode:
(*node).SetType(newType)
case *ast.UnaryNode:
(*node).SetType(newType)
unaryNode := (*node).(*ast.UnaryNode)
traverseAndReplaceIntegerNodesWithIntegerNodes(&unaryNode.Node, newType)
case *ast.BinaryNode:
// TODO: Binary node return type is dependent on the type of the operands. We can't just change the type of the node.
binaryNode := (*node).(*ast.BinaryNode)
switch binaryNode.Operator {
case "+", "-", "*":
traverseAndReplaceIntegerNodesWithIntegerNodes(&binaryNode.Left, newType)
traverseAndReplaceIntegerNodesWithIntegerNodes(&binaryNode.Right, newType)
}
}
}
And replace it with implicit added calls to int*.
NOTE
We should check for overflows on conversion in checkers!
jippi commented
If you haven't looking into go-cty, then I can 10/10 recommend it for a pseudo-type system like this - it's really flexible and powerful :)
antonmedv commented
Although cty looks interesting, I believe we better stick with native Go types. Also, I'm not sure how cty can help in case of:
func(123)
In func takes uint32 param.