Added some useful type checking thigns to literals
This commit is contained in:
parent
e2947eab8c
commit
5c286cf955
@ -19,6 +19,7 @@ type Argument interface {
|
|||||||
// RuneLiteral
|
// RuneLiteral
|
||||||
|
|
||||||
ToString (indent int) (output string)
|
ToString (indent int) (output string)
|
||||||
|
canBePassedAs (what Type) (allowed bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
// analyzeArgument analyzes an argument
|
// analyzeArgument analyzes an argument
|
||||||
|
@ -14,24 +14,118 @@ func (literal IntLiteral) ToString (indent int) (output string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// canBePassedAs returns true if this literal can be implicitly cast to the
|
||||||
|
// specified type, and false if it can't.
|
||||||
|
func (literal IntLiteral) canBePassedAs (what Type) (allowed bool) {
|
||||||
|
// must be a singlular value
|
||||||
|
if what.length != 1 { return }
|
||||||
|
|
||||||
|
// can be passed to types that are signed numbers at a primitive level.
|
||||||
|
primitive := what.underlyingPrimitive()
|
||||||
|
switch primitive {
|
||||||
|
case
|
||||||
|
&PrimitiveF64,
|
||||||
|
&PrimitiveF32,
|
||||||
|
&PrimitiveI64,
|
||||||
|
&PrimitiveI32,
|
||||||
|
&PrimitiveI16,
|
||||||
|
&PrimitiveI8,
|
||||||
|
&PrimitiveInt:
|
||||||
|
|
||||||
|
allowed = true
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// ToString outputs the data in the argument as a string.
|
// ToString outputs the data in the argument as a string.
|
||||||
func (literal UIntLiteral) ToString (indent int) (output string) {
|
func (literal UIntLiteral) ToString (indent int) (output string) {
|
||||||
output += doIndent(indent, fmt.Sprint("arg uint ", literal, "\n"))
|
output += doIndent(indent, fmt.Sprint("arg uint ", literal, "\n"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// canBePassedAs returns true if this literal can be implicitly cast to the
|
||||||
|
// specified type, and false if it can't.
|
||||||
|
func (literal UIntLiteral) canBePassedAs (what Type) (allowed bool) {
|
||||||
|
// must be a singlular value
|
||||||
|
if what.length != 1 { return }
|
||||||
|
|
||||||
|
// can be passed to types that are numbers at a primitive level.
|
||||||
|
primitive := what.underlyingPrimitive()
|
||||||
|
switch primitive {
|
||||||
|
case
|
||||||
|
&PrimitiveF64,
|
||||||
|
&PrimitiveF32,
|
||||||
|
&PrimitiveI64,
|
||||||
|
&PrimitiveI32,
|
||||||
|
&PrimitiveI16,
|
||||||
|
&PrimitiveI8,
|
||||||
|
&PrimitiveInt,
|
||||||
|
&PrimitiveU64,
|
||||||
|
&PrimitiveU32,
|
||||||
|
&PrimitiveU16,
|
||||||
|
&PrimitiveU8,
|
||||||
|
&PrimitiveUInt:
|
||||||
|
|
||||||
|
allowed = true
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// ToString outputs the data in the argument as a string.
|
// ToString outputs the data in the argument as a string.
|
||||||
func (literal FloatLiteral) ToString (indent int) (output string) {
|
func (literal FloatLiteral) ToString (indent int) (output string) {
|
||||||
output += doIndent(indent, fmt.Sprint("arg float ", literal, "\n"))
|
output += doIndent(indent, fmt.Sprint("arg float ", literal, "\n"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// canBePassedAs returns true if this literal can be implicitly cast to the
|
||||||
|
// specified type, and false if it can't.
|
||||||
|
func (literal FloatLiteral) canBePassedAs (what Type) (allowed bool) {
|
||||||
|
// must be a singlular value
|
||||||
|
if what.length != 1 { return }
|
||||||
|
|
||||||
|
// can be passed to types that are floats at a primitive level.
|
||||||
|
primitive := what.underlyingPrimitive()
|
||||||
|
switch primitive {
|
||||||
|
case
|
||||||
|
&PrimitiveF64,
|
||||||
|
&PrimitiveF32:
|
||||||
|
|
||||||
|
allowed = true
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// ToString outputs the data in the argument as a string.
|
// ToString outputs the data in the argument as a string.
|
||||||
func (literal StringLiteral) ToString (indent int) (output string) {
|
func (literal StringLiteral) ToString (indent int) (output string) {
|
||||||
output += doIndent(indent, fmt.Sprint("arg string \"", literal, "\"\n"))
|
output += doIndent(indent, fmt.Sprint("arg string \"", literal, "\"\n"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// canBePassedAs returns true if this literal can be implicitly cast to the
|
||||||
|
// specified type, and false if it can't.
|
||||||
|
func (literal StringLiteral) canBePassedAs (what Type) (allowed bool) {
|
||||||
|
// can be passed to types that are numbers at a primitive level.
|
||||||
|
primitive := what.underlyingPrimitive()
|
||||||
|
switch primitive {
|
||||||
|
case
|
||||||
|
&PrimitiveF64,
|
||||||
|
&PrimitiveF32,
|
||||||
|
&PrimitiveI64,
|
||||||
|
&PrimitiveI32,
|
||||||
|
&PrimitiveI16,
|
||||||
|
&PrimitiveI8,
|
||||||
|
&PrimitiveInt,
|
||||||
|
&PrimitiveU64,
|
||||||
|
&PrimitiveU32,
|
||||||
|
&PrimitiveU16,
|
||||||
|
&PrimitiveU8,
|
||||||
|
&PrimitiveUInt:
|
||||||
|
|
||||||
|
allowed = true
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// ToString outputs the data in the argument as a string.
|
// ToString outputs the data in the argument as a string.
|
||||||
func (literal RuneLiteral) ToString (indent int) (output string) {
|
func (literal RuneLiteral) ToString (indent int) (output string) {
|
||||||
output += doIndent(indent, fmt.Sprint("arg rune '", literal, "'\n"))
|
output += doIndent(indent, fmt.Sprint("arg rune '", literal, "'\n"))
|
||||||
|
@ -2,6 +2,8 @@ package analyzer
|
|||||||
|
|
||||||
// This is a global, cannonical list of primitive and built-in types.
|
// This is a global, cannonical list of primitive and built-in types.
|
||||||
|
|
||||||
|
var PrimitiveF32 = createPrimitive("Int", Type {})
|
||||||
|
var PrimitiveF64 = createPrimitive("Int", Type {})
|
||||||
var PrimitiveInt = createPrimitive("Int", Type {})
|
var PrimitiveInt = createPrimitive("Int", Type {})
|
||||||
var PrimitiveUInt = createPrimitive("UInt", Type {})
|
var PrimitiveUInt = createPrimitive("UInt", Type {})
|
||||||
var PrimitiveI8 = createPrimitive("I8", Type {})
|
var PrimitiveI8 = createPrimitive("I8", Type {})
|
||||||
|
@ -16,21 +16,20 @@ const (
|
|||||||
|
|
||||||
// TypeKindVariableArray means it's an array of variable length.
|
// TypeKindVariableArray means it's an array of variable length.
|
||||||
TypeKindVariableArray
|
TypeKindVariableArray
|
||||||
|
|
||||||
// TypeKindObject means it's a structured type with members.
|
|
||||||
TypeKindObject
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Type represents a description of a type. It must eventually point to a
|
// Type represents a description of a type. It must eventually point to a
|
||||||
// TypeSection.
|
// TypeSection.
|
||||||
type Type struct {
|
type Type struct {
|
||||||
// one of these must be nil.
|
// one of these must be nil.
|
||||||
actual Section
|
actual *TypeSection
|
||||||
points *Type
|
points *Type
|
||||||
|
|
||||||
mutable bool
|
mutable bool
|
||||||
kind TypeKind
|
kind TypeKind
|
||||||
|
|
||||||
|
primitiveCache *TypeSection
|
||||||
|
|
||||||
// if this is greater than 1, it means that this is a fixed-length array
|
// if this is greater than 1, it means that this is a fixed-length array
|
||||||
// of whatever the type is. even if the type is a variable length array.
|
// of whatever the type is. even if the type is a variable length array.
|
||||||
// because literally why not.
|
// because literally why not.
|
||||||
@ -52,8 +51,6 @@ func (what Type) ToString (indent int) (output string) {
|
|||||||
output += " pointer"
|
output += " pointer"
|
||||||
case TypeKindVariableArray:
|
case TypeKindVariableArray:
|
||||||
output += " variableArray"
|
output += " variableArray"
|
||||||
case TypeKindObject:
|
|
||||||
output += " object"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if what.points != nil {
|
if what.points != nil {
|
||||||
@ -70,6 +67,78 @@ func (what Type) ToString (indent int) (output string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// underlyingPrimitive returns the primitive that this type eventually inherits
|
||||||
|
// from.
|
||||||
|
func (what Type) underlyingPrimitive () (underlying *TypeSection) {
|
||||||
|
// if we have already done this operation, return the cahced result.
|
||||||
|
if what.primitiveCache != nil {
|
||||||
|
underlying = what.primitiveCache
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if what.kind != TypeKindBasic {
|
||||||
|
// if we point to something, return nil because there is no void
|
||||||
|
// pointer bullshit in this language
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
actual := what.actual
|
||||||
|
switch actual {
|
||||||
|
case
|
||||||
|
&PrimitiveF32,
|
||||||
|
&PrimitiveF64,
|
||||||
|
&PrimitiveFunc,
|
||||||
|
&PrimitiveFace,
|
||||||
|
&PrimitiveObj,
|
||||||
|
&PrimitiveU64,
|
||||||
|
&PrimitiveU32,
|
||||||
|
&PrimitiveU16,
|
||||||
|
&PrimitiveU8,
|
||||||
|
&PrimitiveI64,
|
||||||
|
&PrimitiveI32,
|
||||||
|
&PrimitiveI16,
|
||||||
|
&PrimitiveI8,
|
||||||
|
&PrimitiveUInt,
|
||||||
|
&PrimitiveInt:
|
||||||
|
|
||||||
|
underlying = actual
|
||||||
|
return
|
||||||
|
|
||||||
|
case nil:
|
||||||
|
panic("invalid state: Type.actual is nil")
|
||||||
|
|
||||||
|
default:
|
||||||
|
// if none of the primitives matched, recurse.
|
||||||
|
underlying = actual.what.underlyingPrimitive()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// reduce ascends up the inheritence chain and gets the first type it finds that
|
||||||
|
// isn't basic. If the type has a clear path of inheritence to a simple
|
||||||
|
// primitive, there will be no non-basic types in the chain and this method will
|
||||||
|
// return false for reducible. If the type this method is called on is not
|
||||||
|
// basic, it itself is returned.
|
||||||
|
func (what Type) reduce () (reduced Type, reducible bool) {
|
||||||
|
reducible = true
|
||||||
|
|
||||||
|
// returns itself if it is not basic (cannot be reduced further)
|
||||||
|
if what.kind != TypeKindBasic {
|
||||||
|
reduced = what
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we can't recurse, return false for reducible
|
||||||
|
if what.actual == nil {
|
||||||
|
reducible = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// otherwise, recurse
|
||||||
|
reduced, reducible = what.actual.what.reduce()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// analyzeType analyzes a type specifier.
|
// analyzeType analyzes a type specifier.
|
||||||
func (analyzer AnalysisOperation) analyzeType (
|
func (analyzer AnalysisOperation) analyzeType (
|
||||||
inputType parser.Type,
|
inputType parser.Type,
|
||||||
@ -93,10 +162,14 @@ func (analyzer AnalysisOperation) analyzeType (
|
|||||||
outputType.points = &points
|
outputType.points = &points
|
||||||
} else {
|
} else {
|
||||||
var bitten parser.Identifier
|
var bitten parser.Identifier
|
||||||
outputType.actual,
|
var actual Section
|
||||||
|
actual,
|
||||||
bitten,
|
bitten,
|
||||||
err = analyzer.fetchSectionFromIdentifier(inputType.Name())
|
err = analyzer.fetchSectionFromIdentifier(inputType.Name())
|
||||||
|
|
||||||
|
outputType.actual = actual.(*TypeSection)
|
||||||
|
// TODO: produce an error if this doesnt work
|
||||||
|
|
||||||
if bitten.Length() > 0 {
|
if bitten.Length() > 0 {
|
||||||
err = bitten.NewError(
|
err = bitten.NewError(
|
||||||
"cannot use member selection in this context",
|
"cannot use member selection in this context",
|
||||||
|
Reference in New Issue
Block a user