This repository has been archived on 2024-02-27. You can view files and clone it, but cannot push or open issues or pull requests.
arf/analyzer/type.go

274 lines
6.1 KiB
Go
Raw Normal View History

package analyzer
import "fmt"
2022-09-20 09:01:56 -06:00
import "git.tebibyte.media/arf/arf/parser"
2022-09-22 11:19:23 -06:00
import "git.tebibyte.media/arf/arf/infoerr"
// TypeKind represents what kind of type a type is.
type TypeKind int
const (
// TypeKindBasic means it's a single value.
TypeKindBasic TypeKind = iota
// TypeKindPointer means it's a pointer
TypeKindPointer
// TypeKindVariableArray means it's an array of variable length.
TypeKindVariableArray
)
// Type represents a description of a type. It must eventually point to a
// TypeSection.
type Type struct {
2022-10-11 16:31:37 -06:00
locatable
2022-09-10 17:50:18 -06:00
// one of these must be nil.
actual *TypeSection
points *Type
mutable bool
kind TypeKind
primitiveCache *TypeSection
singularCache *bool
// 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.
// because literally why not.
length uint64
}
// ToString returns all data stored within the type, in string form.
func (what Type) ToString (indent int) (output string) {
2022-09-10 17:50:18 -06:00
output += doIndent(indent, "type ", what.length)
if what.mutable {
output += " mutable"
}
switch what.kind {
case TypeKindBasic:
output += " basic"
case TypeKindPointer:
output += " pointer"
case TypeKindVariableArray:
output += " variableArray"
}
if what.points != nil {
output += " {\n"
output += what.points.ToString(indent + 1)
output += doIndent(indent, "}")
}
if what.actual != nil {
output += " " + what.actual.Name()
2022-09-10 17:50:18 -06:00
}
output += "\n"
return
}
2022-09-20 09:01:56 -06:00
// underlyingPrimitive returns the primitive that this type eventually inherits
// from. If the type ends up pointing to something, this returns nil.
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
}
}
// isSingular returns whether or not the type is a singular value. this goes
// all the way up the inheritence chain, only stopping when it hits a non-basic
// type because this is about data storage of a value.
func (what Type) isSingular () (singular bool) {
// if we have already done this operation, return the cahced result.
if what.singularCache != nil {
singular = *what.singularCache
return
}
2022-10-11 21:51:55 -06:00
if what.length > 0 {
singular = true
return
}
// decide whether or not to recurse
if what.kind != TypeKindBasic { return }
actual := what.actual
if actual == nil {
return
} else {
singular = actual.what.isSingular()
}
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
}
2022-09-20 09:01:56 -06:00
// analyzeType analyzes a type specifier.
func (analyzer AnalysisOperation) analyzeType (
inputType parser.Type,
) (
outputType Type,
err error,
) {
2022-10-11 16:31:37 -06:00
outputType.mutable = inputType.Mutable()
outputType.length = inputType.Length()
outputType.location = inputType.Location()
2022-09-29 18:28:51 -06:00
if outputType.length < 1 {
2022-09-22 11:19:23 -06:00
err = inputType.NewError (
"cannot specify a length of zero",
infoerr.ErrorKindError)
return
}
// analyze type this type points to, if it exists
if inputType.Kind() != parser.TypeKindBasic {
var points Type
points, err = analyzer.analyzeType(inputType.Points())
outputType.points = &points
} else {
var bitten parser.Identifier
var actual Section
actual,
bitten,
err = analyzer.fetchSectionFromIdentifier(inputType.Name())
outputType.actual = actual.(*TypeSection)
// TODO: produce an error if this doesnt work
if bitten.Length() > 0 {
err = bitten.NewError(
"cannot use member selection in this context",
infoerr.ErrorKindError)
return
}
2022-09-22 11:19:23 -06:00
}
// TODO
2022-09-20 09:01:56 -06:00
return
}
// Describe provides a human readable description of the type. The value of this
// should not be computationally analyzed.
func (what Type) Describe () (description string) {
if what.kind == TypeKindBasic {
actual := what.actual
switch actual {
case &PrimitiveF32:
description += "F32"
case &PrimitiveF64:
description += "F64"
case &PrimitiveFunc:
description += "Func"
case &PrimitiveFace:
description += "Face"
case &PrimitiveObj:
description += "Obj"
case &PrimitiveU64:
description += "U64"
case &PrimitiveU32:
description += "U32"
case &PrimitiveU16:
description += "U16"
case &PrimitiveU8:
description += "U8"
case &PrimitiveI64:
description += "I64"
case &PrimitiveI32:
description += "I32"
case &PrimitiveI16:
description += "I16"
case &PrimitiveI8:
description += "I8"
case &PrimitiveUInt:
description += "UInt"
case &PrimitiveInt:
description += "Int"
case &BuiltInString:
description += "String"
case nil:
panic("invalid state: Type.actual is nil")
default:
description += actual.ModuleName() + "." + actual.Name()
return
}
} else {
description += "{"
description += what.points.Describe()
description += "}"
}
if what.length > 0 {
description += fmt.Sprint(":", what.length)
}
return
}