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
Sasha Koshka 5d27befb6f Fixed require paths
Previously, relative require paths would be resolved based on the
current working directory. They are now resolved based on the path
of the module.
2022-10-12 13:11:36 -04:00

288 lines
6.4 KiB
Go

package analyzer
import "fmt"
import "git.tebibyte.media/arf/arf/parser"
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 {
locatable
// 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) {
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()
}
output += "\n"
return
}
// 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
}
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
}
// analyzeType analyzes a type specifier.
func (analyzer analysisOperation) analyzeType (
inputType parser.Type,
) (
outputType Type,
err error,
) {
outputType.mutable = inputType.Mutable()
outputType.length = inputType.Length()
outputType.location = inputType.Location()
if outputType.length < 1 {
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())
if err != nil { return }
if actual == nil {
err = inputType.NewError (
"this type does not exist",
infoerr.ErrorKindError)
return
}
var worked bool
outputType.actual, worked = actual.(*TypeSection)
if !worked {
err = inputType.NewError (
"this must refer to a type or an interface",
infoerr.ErrorKindError)
return
}
if bitten.Length() > 0 {
err = bitten.NewError(
"cannot use member selection in this context",
infoerr.ErrorKindError)
return
}
}
// TODO
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
}