diff --git a/analyzer/argument.go b/analyzer/argument.go index bd292a6..2f032e8 100644 --- a/analyzer/argument.go +++ b/analyzer/argument.go @@ -22,6 +22,7 @@ type Argument interface { ToString (indent int) (output string) Equals (value any) (equal bool) Value () (value any) + Resolve () (constant Argument, err error) canBePassedAs (what Type) (allowed bool) } diff --git a/analyzer/enum-section.go b/analyzer/enum-section.go index b018a55..8be1cf0 100644 --- a/analyzer/enum-section.go +++ b/analyzer/enum-section.go @@ -69,6 +69,9 @@ func (analyzer analysisOperation) analyzeEnumSection () ( 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() @@ -94,8 +97,14 @@ func (analyzer analysisOperation) analyzeEnumSection () ( 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 default value + // type check value err = analyzer.typeCheck ( outputMember.argument, outputSection.what) @@ -117,7 +126,8 @@ func (analyzer analysisOperation) analyzeEnumSection () ( return } - if outputMember.argument == nil { continue } + if outputMember.argument == nil { continue } + if compareMember.argument == nil { continue } if compareMember.argument.Equals ( outputMember.argument.Value(), @@ -134,8 +144,55 @@ func (analyzer analysisOperation) analyzeEnumSection () ( outputMember) } - // TODO: fill in members that do not have values with incrementing - // values. take care to not duplicate them. + // 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 + } + } outputSection.complete = true return diff --git a/analyzer/literals.go b/analyzer/literals.go index 8236b90..35a64d4 100644 --- a/analyzer/literals.go +++ b/analyzer/literals.go @@ -51,6 +51,13 @@ func (literal IntLiteral) Value () (value any) { return } +// Resolve resolves the argument to a constant literal, which in this case is +// trivial because the literal is already constant. +func (literal IntLiteral) Resolve () (constant Argument, err error) { + constant = literal + 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) { @@ -97,6 +104,13 @@ func (literal UIntLiteral) canBePassedAs (what Type) (allowed bool) { return } +// Resolve resolves the argument to a constant literal, which in this case is +// trivial because the literal is already constant. +func (literal UIntLiteral) Resolve () (constant Argument, err error) { + constant = literal + return +} + // What returns the type of the argument func (literal FloatLiteral) What () (what Type) { what.actual = &PrimitiveF64 @@ -122,6 +136,13 @@ func (literal FloatLiteral) Value () (value any) { return } +// Resolve resolves the argument to a constant literal, which in this case is +// trivial because the literal is already constant. +func (literal FloatLiteral) Resolve () (constant Argument, err error) { + constant = literal + 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) { @@ -165,6 +186,13 @@ func (literal StringLiteral) Value () (value any) { return } +// Resolve resolves the argument to a constant literal, which in this case is +// trivial because the literal is already constant. +func (literal StringLiteral) Resolve () (constant Argument, err error) { + constant = literal + 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) { diff --git a/tests/analyzer/enumSection/main.arf b/tests/analyzer/enumSection/main.arf index 0093333..1b37c1c 100644 --- a/tests/analyzer/enumSection/main.arf +++ b/tests/analyzer/enumSection/main.arf @@ -5,7 +5,7 @@ require '../typeSection' enum ro aWeekday:Int - sunday - monday - - tuesday + - tuesday 3 - wednesday - thursday - friday @@ -13,7 +13,7 @@ enum ro aWeekday:Int type ro bColor:U32 -# enum ro cNamedColor:bColor - # - red 0xFF0000 - # - green 0x00FF00 - # - blue 0x0000FF +enum ro cNamedColor:bColor + - red 0xFF0000 + - green 0x00FF00 + - blue 0x0000FF