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/enum-section.go
2022-10-16 02:07:25 -04:00

213 lines
5.5 KiB
Go

package analyzer
import "git.tebibyte.media/arf/arf/types"
import "git.tebibyte.media/arf/arf/parser"
import "git.tebibyte.media/arf/arf/infoerr"
// EnumSection represents an enumerated type section.
type EnumSection struct {
sectionBase
what Type
members []EnumMember
argument Argument
}
// EnumMember is a member of an enumerated type.
type EnumMember struct {
locatable
name string
argument Argument
}
// ToString returns all data stored within the member, in string form.
func (member EnumMember) ToString (indent int) (output string) {
output += doIndent(indent, "member ", member.name, "\n")
if member.argument != nil {
output += member.argument.ToString(indent + 1)
}
return
}
// ToString returns all data stored within the type section, in string form.
func (section EnumSection) ToString (indent int) (output string) {
output += doIndent(indent, "enumSection ")
output += section.permission.ToString() + " "
output += section.where.ToString()
output += "\n"
if section.argument != nil {
output += section.argument.ToString(indent + 1)
}
output += section.what.ToString(indent + 1)
for _, member := range section.members {
output += member.ToString(indent + 1)
}
return
}
// analyzeEnumSection analyzes an enumerated type section.
func (analyzer analysisOperation) analyzeEnumSection () (
section Section,
err error,
) {
outputSection := EnumSection { }
outputSection.where = analyzer.currentPosition
section = &outputSection
analyzer.addSection(section)
inputSection := analyzer.currentSection.(parser.EnumSection)
outputSection.location = analyzer.currentSection.Location()
if inputSection.Permission() == types.PermissionReadWrite {
err = inputSection.NewError (
"read-write (rw) permission not understood in this " +
"context, try read-only (ro)",
infoerr.ErrorKindError)
return
}
outputSection.permission = inputSection.Permission()
// get inherited type
outputSection.what, err = analyzer.analyzeType(inputSection.Type())
if err != nil { return }
// if the inherited type is a single number, we take note of that here
// because it will allow us to do things like automatically fill in
// member values if they are not specified.
isNumeric :=
outputSection.what.isNumeric() &&
outputSection.what.isSingular()
// enum sections are only allowed to inherit from type sections
_, inheritsFromTypeSection := outputSection.what.actual.(*TypeSection)
if !inheritsFromTypeSection {
err = inputSection.Type().NewError (
"enum sections can only inherit from other type " +
"sections",
infoerr.ErrorKindError)
return
}
// analyze members
for index := 0; index < inputSection.Length(); index ++ {
inputMember := inputSection.Item(index)
outputMember := EnumMember { }
outputMember.location = inputMember.Location()
outputMember.name = inputMember.Name()
if !inputMember.Argument().Nil() {
outputMember.argument,
err = analyzer.analyzeArgument(inputMember.Argument())
if err != nil { return }
// attempt to resolve the argument to a single constant
// literal
outputMember.argument, err =
outputMember.argument.Resolve()
if err != nil { return }
// type check value
err = analyzer.typeCheck (
outputMember.argument,
outputSection.what)
if err != nil { return }
} else if !isNumeric {
// non-numeric enums must have filled in values
err = inputMember.NewError (
"member value must be specified manually for " +
"non-numeric enums",
infoerr.ErrorKindError)
return
}
for _, compareMember := range outputSection.members {
if compareMember.name == outputMember.name {
err = inputMember.NewError (
"enum member names must be unique",
infoerr.ErrorKindError)
return
}
if outputMember.argument == nil { continue }
if compareMember.argument == nil { continue }
if compareMember.argument.Equals (
outputMember.argument.Value(),
) {
err = inputMember.NewError (
"enum member values must be unique",
infoerr.ErrorKindError)
return
}
}
outputSection.members = append (
outputSection.members,
outputMember)
}
// fill in members that do not have values
if isNumeric {
for index, fillInMember := range outputSection.members {
if fillInMember.argument != nil { continue }
max := uint64(0)
for _, compareMember := range outputSection.members {
compareValue := compareMember.argument
switch compareValue.(type) {
case IntLiteral:
number := uint64 (
compareValue.(IntLiteral).value)
if number > max {
max = number
}
case UIntLiteral:
number := uint64 (
compareValue.(UIntLiteral).value)
if number > max {
max = number
}
case FloatLiteral:
number := uint64 (
compareValue.(FloatLiteral).value)
if number > max {
max = number
}
case nil:
// do nothing
default:
panic (
"invalid state: illegal " +
"argument type while " +
"attempting to fill in enum " +
"member value for " +
fillInMember.name + " in " +
outputSection.location.Describe())
}
}
// fill in
fillInMember.argument = UIntLiteral {
locatable: fillInMember.locatable,
value: max + 1,
}
outputSection.members[index] = fillInMember
}
}
if len(outputSection.members) < 1 {
err = outputSection.NewError (
"cannot create an enum with no members",
infoerr.ErrorKindError)
return
}
outputSection.argument = outputSection.members[0].argument
outputSection.complete = true
return
}