diff --git a/parser/accessors.go b/parser/accessors.go new file mode 100644 index 0000000..28e51d9 --- /dev/null +++ b/parser/accessors.go @@ -0,0 +1,276 @@ +package parser + +import "git.tebibyte.media/arf/arf/types" + +// LookupSection looks returns the section under the give name. If the section +// does not exist, nil is returned. +func (tree SyntaxTree) LookupSection (name string) (section Section) { + section = tree.sections[name] + return +} + +// Sections returns an iterator for the tree's sections +func (tree SyntaxTree) Sections () (iterator types.Iterator[Section]) { + iterator = types.NewIterator(tree.sections) + return +} + +// Kind returns the section's kind (SectionKindType). +func (section TypeSection) Kind () (kind SectionKind) { + kind = SectionKindType + return +} + +// Kind returns the section's kind (SectionKindObjt). +func (section ObjtSection) Kind () (kind SectionKind) { + kind = SectionKindObjt + return +} + +// Kind returns the section's kind (SectionKindEnum). +func (section EnumSection) Kind () (kind SectionKind) { + kind = SectionKindEnum + return +} + +// Kind returns the section's kind (SectionKindFace). +func (section FaceSection) Kind () (kind SectionKind) { + kind = SectionKindFace + return +} + +// Kind returns the section's kind (SectionKindData). +func (section DataSection) Kind () (kind SectionKind) { + kind = SectionKindData + return +} + +// Kind returns the section's kind (SectionKindFunc). +func (section FuncSection) Kind () (kind SectionKind) { + kind = SectionKindFunc + return +} + +// Length returns the amount of names in the identifier. +func (identifier Identifier) Length () (length int) { + length = len(identifier.trail) + return +} + +// Item returns the name at the specified index. +func (identifier Identifier) Item (index int) (item string) { + item = identifier.trail[index] + return +} + +// Kind returns the type's kind. +func (what Type) Kind () (kind TypeKind) { + kind = what.kind + return +} + +// Mutable returns whether or not the type's data is mutable. +func (what Type) Mutable () (mutable bool) { + mutable = what.mutable + return +} + +// Length returns the length of the type if the type is a fixed length array. +// Otherwise, it just returns zero. +func (what Type) Length () (length uint64) { + if what.kind == TypeKindArray { + length = what.length + } + return +} + +// Name returns the name of the type, if it is a basic type. Otherwise, it +// returns a zero value identifier. +func (what Type) Name () (name Identifier) { + if what.kind == TypeKindBasic { + name = what.name + } + return +} + +// Points returns the type that this type points to, or is an array of. If the +// type is a basic type, this returns a zero value type. +func (what Type) Points () (points Type) { + if what.kind != TypeKindBasic { + points = *what.points + } + return +} + +// Values returns an iterator for the initialization values. +func (values ObjectInitializationValues) Sections () ( + iterator types.Iterator[Argument], +) { + iterator = types.NewIterator(values.attributes) + return +} + +// Length returns the amount of values. +func (values ArrayInitializationValues) Length () (length int) { + length = len(values.values) + return +} + +// Item returns the value at index. +func (values ArrayInitializationValues) Value (index int) (value Argument) { + value = values.values[index] + return +} + +// Kind returns what kind of argument it is. +func (argument Argument) Kind () (kind ArgumentKind) { + kind = argument.kind + return +} + +// Value returns the underlying value of the argument. You can use Kind() to +// find out what to cast this to. +func (argument Argument) Value () (value any) { + value = argument.value + return +} + +// BitWidth returns the bit width of the object member. If it is zero, it should +// be treated as unspecified. +func (member ObjtMember) BitWidth () (width uint64) { + width = member.bitWidth + return +} + +// Length returns the amount of members in the section. +func (section ObjtSection) Length () (length int) { + length = len(section.members) + return +} + +// Item returns the member at index. +func (section ObjtSection) Item (index int) (member ObjtMember) { + member = section.members[index] + return +} + +// Length returns the amount of members in the section. +func (section EnumSection) Length () (length int) { + length = len(section.members) + return +} + +// Item returns the member at index. +func (section EnumSection) Item (index int) (member EnumMember) { + member = section.members[index] + return +} + +// InputsLength returns the amount of inputs in the behavior. +func (behavior FaceBehavior) IntputsLength () (length int) { + length = len(behavior.inputs) + return +} + +// Input returns the input at index. +func (behavior FaceBehavior) Input (index int) (input Declaration) { + input = behavior.inputs[index] + return +} + +// OutputsLength returns the amount of outputs in the behavior. +func (behavior FaceBehavior) OutputsLength () (length int) { + length = len(behavior.outputs) + return +} + +// Output returns the output at index. +func (behavior FaceBehavior) Output (index int) (output Declaration) { + output = behavior.outputs[index] + return +} + +// Behaviors returns an iterator for the interface's behaviors. +func (section FaceSection) Behaviors () (iterator types.Iterator[FaceBehavior]) { + iterator = types.NewIterator(section.behaviors) + return +} + +// Kind returns what kind of phrase it is. +func (phrase Phrase) Kind () (kind PhraseKind) { + kind = phrase.kind + return +} + +// ArgumentsLength returns the amount of arguments in the phrase. +func (phrase Phrase) ArgumentsLength () (length int) { + length = len(phrase.arguments) + return +} + +// Argument returns the argument at index. +func (phrase Phrase) Argument (index int) (argument Argument) { + argument = phrase.arguments[index] + return +} + +// ReturnsToLength returns the amount of things the phrase returns to. +func (phrase Phrase) ReturnsToLength () (length int) { + length = len(phrase.returnsTo) + return +} + +// ReturnTo returns the thing alskdjaslkdjsa whatever i dont even know wtf. +func (phrase Phrase) ReturnTo (index int) (returnTo Argument) { + returnTo = phrase.returnsTo[index] + return +} + +// Block returns the block under the phrase, if it is a control flow statement. +func (phrase Phrase) Block () (block Block) { + block = phrase.block + return +} + +// Receiver returns the method receiver, if there is one. Otherwise, it returns +// nil. +func (section FuncSection) Receiver () (receiver *Declaration) { + receiver = section.receiver + return +} + +// InputsLength returns the number of inputs in the function. +func (section FuncSection) InputsLength () (length int) { + length = len(section.inputs) + return +} + +// Input returns the input at index. +func (section FuncSection) Input (index int) (input Declaration) { + input = section.inputs[index] + return +} + +// OutputsLength returns the number of outputs in the function. +func (section FuncSection) OutputsLength () (length int) { + length = len(section.outputs) + return +} + +// Output returns the output at index. +func (section FuncSection) Output (index int) (output FuncOutput) { + output = section.outputs[index] + return +} + +// Root returns the root block of the section. +func (section FuncSection) Root () (root Block) { + root = section.root + return +} + +// External returns whether or not the function is an external function or not. +func (section FuncSection) External () (external bool) { + external = section.external + return +} diff --git a/parser/argument.go b/parser/argument.go index 5dd87e3..e821322 100644 --- a/parser/argument.go +++ b/parser/argument.go @@ -41,13 +41,15 @@ func (parser *ParsingOperation) parseArgument () (argument Argument, err error) infoerr.ErrorKindError) return } + + declaration := Declaration { } + declaration.what = what + declaration.name = identifier.trail[0] + declaration.location = argument.Location() argument.kind = ArgumentKindDeclaration - argument.value = Declaration { - location: argument.location, - name: identifier.trail[0], - what: what, - } + argument.value = declaration + } else { argument.kind = ArgumentKindIdentifier argument.value = identifier diff --git a/parser/body.go b/parser/body.go index 82001fc..15b9092 100644 --- a/parser/body.go +++ b/parser/body.go @@ -8,63 +8,45 @@ func (parser *ParsingOperation) parseBody () (err error) { for { err = parser.expect(lexer.TokenKindName) if err != nil { return } - sectionType := parser.token.Value().(string) + switch sectionType { case "data": - var section *DataSection - section, err = parser.parseDataSection() - if parser.tree.dataSections == nil { - parser.tree.dataSections = - make(map[string] *DataSection) - } - parser.tree.dataSections[section.name] = section - if err != nil { return } + section, parseErr := parser.parseDataSection() + err = parser.tree.addSection(section) + if err != nil { return } + if parseErr != nil { return } + case "type": - var section *TypeSection - section, err = parser.parseTypeSection() - if parser.tree.typeSections == nil { - parser.tree.typeSections = - make(map[string] *TypeSection) - } - parser.tree.typeSections[section.name] = section - if err != nil { return } + section, parseErr := parser.parseTypeSection() + err = parser.tree.addSection(section) + if err != nil { return } + if parseErr != nil { return } + case "objt": - var section *ObjtSection - section, err = parser.parseObjtSection() - if parser.tree.objtSections == nil { - parser.tree.objtSections = - make(map[string] *ObjtSection) - } - parser.tree.objtSections[section.name] = section - if err != nil { return } + section, parseErr := parser.parseObjtSection() + err = parser.tree.addSection(section) + if err != nil { return } + if parseErr != nil { return } + case "face": - var section *FaceSection - section, err = parser.parseFaceSection() - if parser.tree.faceSections == nil { - parser.tree.faceSections = - make(map[string] *FaceSection) - } - parser.tree.faceSections[section.name] = section - if err != nil { return } + section, parseErr := parser.parseFaceSection() + err = parser.tree.addSection(section) + if err != nil { return } + if parseErr != nil { return } + case "enum": - var section *EnumSection - section, err = parser.parseEnumSection() - if parser.tree.enumSections == nil { - parser.tree.enumSections = - make(map[string] *EnumSection) - } - parser.tree.enumSections[section.name] = section - if err != nil { return } + section, parseErr := parser.parseEnumSection() + err = parser.tree.addSection(section) + if err != nil { return } + if parseErr != nil { return } + case "func": - var section *FuncSection - section, err = parser.parseFuncSection() - if parser.tree.funcSections == nil { - parser.tree.funcSections = - make(map[string] *FuncSection) - } - parser.tree.funcSections[section.name] = section - if err != nil { return } + section, parseErr := parser.parseFuncSection() + err = parser.tree.addSection(section) + if err != nil { return } + if parseErr != nil { return } + default: err = parser.token.NewError ( "unknown section type \"" + sectionType + "\"", @@ -73,3 +55,18 @@ func (parser *ParsingOperation) parseBody () (err error) { } } } + +// addSection adds a section to the tree, ensuring it has a unique name within +// the module. +func (tree *SyntaxTree) addSection (section Section) (err error) { + _, exists := tree.sections[section.Name()] + if exists { + err = section.NewError ( + "cannot have multiple sections with the same name", + infoerr.ErrorKindError) + return + } + + tree.sections[section.Name()] = section + return +} diff --git a/parser/data.go b/parser/data.go index eb828af..fa19ff0 100644 --- a/parser/data.go +++ b/parser/data.go @@ -5,13 +5,13 @@ import "git.tebibyte.media/arf/arf/lexer" // parseData parses a data section. func (parser *ParsingOperation) parseDataSection () ( - section *DataSection, + section DataSection, err error, ) { err = parser.expect(lexer.TokenKindName) if err != nil { return } - section = &DataSection { location: parser.token.Location() } + section.location = parser.token.Location() err = parser.nextToken(lexer.TokenKindPermission) if err != nil { return } diff --git a/parser/enum.go b/parser/enum.go index 23d318e..9625dc5 100644 --- a/parser/enum.go +++ b/parser/enum.go @@ -4,14 +4,15 @@ import "git.tebibyte.media/arf/arf/types" import "git.tebibyte.media/arf/arf/lexer" import "git.tebibyte.media/arf/arf/infoerr" +// parseEnumSection parses an enumerated type section. func (parser *ParsingOperation) parseEnumSection () ( - section *EnumSection, + section EnumSection, err error, ) { err = parser.expect(lexer.TokenKindName) if err != nil { return } - section = &EnumSection { location: parser.token.Location() } + section.location = parser.token.Location() // get permission err = parser.nextToken(lexer.TokenKindPermission) @@ -36,7 +37,7 @@ func (parser *ParsingOperation) parseEnumSection () ( if err != nil { return } // parse members - err = parser.parseEnumMembers(section) + err = parser.parseEnumMembers(§ion) if err != nil { return } if len(section.members) == 0 { diff --git a/parser/face.go b/parser/face.go index 14460cb..62f2605 100644 --- a/parser/face.go +++ b/parser/face.go @@ -6,16 +6,14 @@ import "git.tebibyte.media/arf/arf/infoerr" // parseFaceSection parses an interface section. func (parser *ParsingOperation) parseFaceSection () ( - section *FaceSection, + section FaceSection, err error, ) { err = parser.expect(lexer.TokenKindName) if err != nil { return } - section = &FaceSection { - location: parser.token.Location(), - behaviors: make(map[string] FaceBehavior), - } + section.behaviors = make(map[string] FaceBehavior) + section.location = parser.token.Location() // get permission err = parser.nextToken(lexer.TokenKindPermission) diff --git a/parser/func.go b/parser/func.go index 1ee5302..f6288f5 100644 --- a/parser/func.go +++ b/parser/func.go @@ -6,13 +6,13 @@ import "git.tebibyte.media/arf/arf/infoerr" // parseFunc parses a function section. func (parser *ParsingOperation) parseFuncSection () ( - section *FuncSection, + section FuncSection, err error, ) { err = parser.expect(lexer.TokenKindName) if err != nil { return } - section = &FuncSection { location: parser.token.Location() } + section.location = parser.token.Location() // get permission err = parser.nextToken(lexer.TokenKindPermission) @@ -29,7 +29,7 @@ func (parser *ParsingOperation) parseFuncSection () ( if err != nil { return } err = parser.nextToken() if err != nil { return } - err = parser.parseFuncArguments(section) + err = parser.parseFuncArguments(§ion) if err != nil { return } // check to see if the function is external @@ -185,12 +185,12 @@ func (parser *ParsingOperation) parseFuncArguments ( err = parser.nextToken() if err != nil { return } - output.defaultValue, err = + output.value, err = parser.parseInitializationValues(1) into.outputs = append(into.outputs, output) if err != nil { return } } else { - output.defaultValue, err = + output.value, err = parser.parseArgument() into.outputs = append(into.outputs, output) if err != nil { return } diff --git a/parser/misc.go b/parser/misc.go index 0901cdc..9ccc17c 100644 --- a/parser/misc.go +++ b/parser/misc.go @@ -3,6 +3,29 @@ package parser import "git.tebibyte.media/arf/arf/lexer" import "git.tebibyte.media/arf/arf/infoerr" +// TODO: need to come up with another syntax for bitfields, and use that syntax +// for fixed-length arrays. the problem is, we cant use {} for those kinds of +// arrays because they aren't pointers under the hood and that goes against the +// arf design goals in a nasty ugly way, and not only that. but the :mut type +// qualifier is meaningless on fixed length arrays now. the bit field syntax +// solves both of these problems very gracefully. but now, the problem is coming +// up with new bit field syntax. implementing this change is extremely +// necessary, for it will supercharge the coherency of the language and make it +// way more awesome. +// +// this new syntax cannot conflict with arguments, so it cannot have any +// tokens that begin those. basically all symbol tokens are on the table here. +// some ideas: +// +// ro member:Type ~ 4 +// ro member:Type & 4 <- i like this one because binary &. so intuitive. +// ro member:Type % 4 +// ro member:Type | 4 +// ro member:Type ! 4 +// ro member:Type (4) <- this looks phenomenal, but it needs new tokens not +// used anywhere else, and it would be mildly annoying +// to parse. + // parseType parses a type notation of the form Name, {Name}, etc. func (parser *ParsingOperation) parseType () (what Type, err error) { err = parser.expect(lexer.TokenKindName, lexer.TokenKindLBrace) @@ -34,7 +57,7 @@ func (parser *ParsingOperation) parseType () (what Type, err error) { err = parser.nextToken(lexer.TokenKindRBrace) if err != nil { return } } else if parser.token.Is(lexer.TokenKindElipsis) { - what.kind = TypeKindArray + what.kind = TypeKindVariableArray err = parser.nextToken(lexer.TokenKindRBrace) if err != nil { return } diff --git a/parser/node-traits.go b/parser/node-traits.go new file mode 100644 index 0000000..e16aeaf --- /dev/null +++ b/parser/node-traits.go @@ -0,0 +1,70 @@ +package parser + +import "git.tebibyte.media/arf/arf/file" +import "git.tebibyte.media/arf/arf/types" +import "git.tebibyte.media/arf/arf/infoerr" + +// locatable allows a tree node to have a location. +type locatable struct { + location file.Location +} + +// Location returns the location of the node. +func (trait locatable) Location () (location file.Location) { + location = trait.location + return +} + +// NewError creates a new error at the node's location. +func (trait locatable) NewError ( + message string, + kind infoerr.ErrorKind, +) ( + err error, +) { + err = infoerr.NewError(trait.location, message, kind) + return +} + +// nameable allows a tree node to have a name. +type nameable struct { + name string +} + +// Name returns the name of the node. +func (trait nameable) Name () (name string) { + name = trait.name + return +} +// typeable allows a node to have a type. +type typeable struct { + what Type +} + +// Type returns the type of the node. +func (trait typeable) Type () (what Type) { + what = trait.what + return +} + +// permissionable allows a node to have a permission. +type permissionable struct { + permission types.Permission +} + +// Permission returns the permision of the node. +func (trait permissionable) Permission () (permission types.Permission) { + permission = trait.permission + return +} + +// valuable allows a node to have an argument value. +type valuable struct { + value Argument +} + +// Value returns the value argument of the node. +func (trait valuable) Value () (value Argument) { + value = trait.value + return +} diff --git a/parser/objt.go b/parser/objt.go index 3c99acb..1e83e98 100644 --- a/parser/objt.go +++ b/parser/objt.go @@ -7,13 +7,13 @@ import "git.tebibyte.media/arf/arf/infoerr" // parseObjtSection parses an object type definition. This allows for structured // types to be defined, and for member variables to be added and overridden. func (parser *ParsingOperation) parseObjtSection () ( - section *ObjtSection, + section ObjtSection, err error, ) { err = parser.expect(lexer.TokenKindName) if err != nil { return } - section = &ObjtSection { location: parser.token.Location() } + section.location = parser.token.Location() // get permission err = parser.nextToken(lexer.TokenKindPermission) @@ -38,7 +38,7 @@ func (parser *ParsingOperation) parseObjtSection () ( if err != nil { return } // parse members - err = parser.parseObjtMembers(section) + err = parser.parseObjtMembers(§ion) if err != nil { return } if len(section.members) == 0 { @@ -108,11 +108,11 @@ func (parser *ParsingOperation) parseObjtMember () ( err = parser.nextToken() if err != nil { return } - member.defaultValue, + member.value, err = parser.parseInitializationValues(1) if err != nil { return } } else { - member.defaultValue, err = parser.parseArgument() + member.value, err = parser.parseArgument() if err != nil { return } err = parser.expect(lexer.TokenKindNewline) diff --git a/parser/parser.go b/parser/parser.go index 226665f..215a679 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -14,13 +14,25 @@ type ParsingOperation struct { tokens []lexer.Token tokenIndex int - tree *SyntaxTree + tree SyntaxTree } +// TODO: +// - implement parser cache +// - have this try to hit the cache, and actually parse on miss +// - rename this to Fetch +// - add `skim bool` argument. when this is true, don't parse any code or data +// section initialization values, just definitions and their default values. + // Parse reads the files located in the module specified by modulePath, and // converts them into an abstract syntax tree. -func Parse (modulePath string) (tree *SyntaxTree, err error) { - parser := ParsingOperation { modulePath: modulePath } +func Parse (modulePath string) (tree SyntaxTree, err error) { + parser := ParsingOperation { + modulePath: modulePath, + tree: SyntaxTree { + sections: make(map[string] Section), + }, + } if parser.modulePath[len(parser.modulePath) - 1] != '/' { parser.modulePath += "/" @@ -54,9 +66,6 @@ func (parser *ParsingOperation) parse (sourceFile *file.File) (err error) { if err != nil { return } // reset the parser - if parser.tree == nil { - parser.tree = &SyntaxTree { } - } if len(tokens) == 0 { return } parser.tokens = tokens parser.token = tokens[0] diff --git a/parser/test-common.go b/parser/test-common.go index b983534..2784087 100644 --- a/parser/test-common.go +++ b/parser/test-common.go @@ -7,15 +7,6 @@ import "testing" func checkTree (modulePath string, correct string, test *testing.T) { tree, err := Parse(modulePath) - if tree == nil { - test.Log("TREE IS NIL!") - if err != io.EOF && err != nil { - test.Log("returned error:") - test.Log(err) - } - test.Fail() - return - } treeString := tree.ToString(0) treeRunes := []rune(treeString) diff --git a/parser/tree-tostring.go b/parser/tree-tostring.go index 35a3d35..78dd94c 100644 --- a/parser/tree-tostring.go +++ b/parser/tree-tostring.go @@ -30,7 +30,7 @@ func sortMapKeysAlphabetically[KEY_TYPE any] ( return } -func (tree *SyntaxTree) ToString (indent int) (output string) { +func (tree SyntaxTree) ToString (indent int) (output string) { output += doIndent(indent, ":arf\n") if tree.author != "" { @@ -47,35 +47,11 @@ func (tree *SyntaxTree) ToString (indent int) (output string) { output += doIndent(indent, "---\n") - typeSectionKeys := sortMapKeysAlphabetically(tree.typeSections) - for _, name := range typeSectionKeys { - output += tree.typeSections[name].ToString(indent) + sectionKeys := sortMapKeysAlphabetically(tree.sections) + for _, name := range sectionKeys { + output += tree.sections[name].ToString(indent) } - objtSectionKeys := sortMapKeysAlphabetically(tree.objtSections) - for _, name := range objtSectionKeys { - output += tree.objtSections[name].ToString(indent) - } - - enumSectionKeys := sortMapKeysAlphabetically(tree.enumSections) - for _, name := range enumSectionKeys { - output += tree.enumSections[name].ToString(indent) - } - - faceSectionKeys := sortMapKeysAlphabetically(tree.faceSections) - for _, name := range faceSectionKeys { - output += tree.faceSections[name].ToString(indent) - } - - dataSectionKeys := sortMapKeysAlphabetically(tree.dataSections) - for _, name := range dataSectionKeys { - output += tree.dataSections[name].ToString(indent) - } - - funcSectionKeys := sortMapKeysAlphabetically(tree.funcSections) - for _, name := range funcSectionKeys { - output += tree.funcSections[name].ToString(indent) - } return } @@ -90,7 +66,7 @@ func (identifier Identifier) ToString () (output string) { return } -func (what *Type) ToString () (output string) { +func (what Type) ToString () (output string) { if what.kind == TypeKindBasic { output += what.name.ToString() } else { @@ -98,12 +74,9 @@ func (what *Type) ToString () (output string) { output += what.points.ToString() if what.kind == TypeKindArray { - output += " " - if what.length == 0 { - output += ".." - } else { - output += fmt.Sprint(what.length) - } + output += fmt.Sprint(" ", what.length) + } else if what.kind == TypeKindVariableArray { + output += " .." } output += "}" @@ -154,7 +127,7 @@ func (values ArrayInitializationValues) ToString ( return } -func (argument *Argument) ToString (indent int, breakLine bool) (output string) { +func (argument Argument) ToString (indent int, breakLine bool) (output string) { if !breakLine { indent = 0 } if argument.kind == ArgumentKindNil { output += "NIL-ARGUMENT" @@ -279,7 +252,7 @@ func (argument *Argument) ToString (indent int, breakLine bool) (output string) return } -func (section *DataSection) ToString (indent int) (output string) { +func (section DataSection) ToString (indent int) (output string) { output += doIndent ( indent, "data ", @@ -303,25 +276,25 @@ func (section *DataSection) ToString (indent int) (output string) { return } -func (section *TypeSection) ToString (indent int) (output string) { +func (section TypeSection) ToString (indent int) (output string) { output += doIndent ( indent, "type ", section.permission.ToString(), " ", section.name, ":", - section.inherits.ToString()) + section.what.ToString()) isComplexInitialization := - section.defaultValue.kind == ArgumentKindObjectInitializationValues || - section.defaultValue.kind == ArgumentKindArrayInitializationValues + section.value.kind == ArgumentKindObjectInitializationValues || + section.value.kind == ArgumentKindArrayInitializationValues - if section.defaultValue.value == nil { + if section.value.value == nil { output += "\n" } else if isComplexInitialization { output += "\n" - output += section.defaultValue.ToString(indent + 1, true) + output += section.value.ToString(indent + 1, true) } else { - output += " " + section.defaultValue.ToString(0, false) + output += " " + section.value.ToString(0, false) output += "\n" } return @@ -339,23 +312,23 @@ func (member ObjtMember) ToString (indent int) (output string) { } isComplexInitialization := - member.defaultValue.kind == ArgumentKindObjectInitializationValues || - member.defaultValue.kind == ArgumentKindArrayInitializationValues + member.value.kind == ArgumentKindObjectInitializationValues || + member.value.kind == ArgumentKindArrayInitializationValues - if member.defaultValue.value == nil { + if member.value.value == nil { output += "\n" } else if isComplexInitialization { output += "\n" - output += member.defaultValue.ToString(indent + 1, true) + output += member.value.ToString(indent + 1, true) } else { - output += " " + member.defaultValue.ToString(0, false) + output += " " + member.value.ToString(0, false) output += "\n" } return } -func (section *ObjtSection) ToString (indent int) (output string) { +func (section ObjtSection) ToString (indent int) (output string) { output += doIndent ( indent, "objt ", @@ -369,7 +342,7 @@ func (section *ObjtSection) ToString (indent int) (output string) { return } -func (section *EnumSection) ToString (indent int) (output string) { +func (section EnumSection) ToString (indent int) (output string) { output += doIndent ( indent, "enum ", @@ -397,7 +370,7 @@ func (section *EnumSection) ToString (indent int) (output string) { return } -func (section *FaceSection) ToString (indent int) (output string) { +func (section FaceSection) ToString (indent int) (output string) { output += doIndent ( indent, "face ", @@ -412,7 +385,7 @@ func (section *FaceSection) ToString (indent int) (output string) { return } -func (behavior *FaceBehavior) ToString (indent int) (output string) { +func (behavior FaceBehavior) ToString (indent int) (output string) { output += doIndent(indent, behavior.name, "\n") for _, inputItem := range behavior.inputs { @@ -473,13 +446,13 @@ func (block Block) ToString (indent int) (output string) { func (funcOutput FuncOutput) ToString () (output string) { output += funcOutput.Declaration.ToString() - if funcOutput.defaultValue.kind != ArgumentKindNil { - output += " " + funcOutput.defaultValue.ToString(0, false) + if funcOutput.value.kind != ArgumentKindNil { + output += " " + funcOutput.value.ToString(0, false) } return } -func (section *FuncSection) ToString (indent int) (output string) { +func (section FuncSection) ToString (indent int) (output string) { output += doIndent ( indent, "func ", diff --git a/parser/tree.go b/parser/tree.go index 257960a..fa47cc9 100644 --- a/parser/tree.go +++ b/parser/tree.go @@ -2,6 +2,7 @@ package parser import "git.tebibyte.media/arf/arf/file" import "git.tebibyte.media/arf/arf/types" +import "git.tebibyte.media/arf/arf/infoerr" // SyntaxTree represents an abstract syntax tree. It covers an entire module. It // can be expected to be syntactically correct, but it might not be semantically @@ -10,22 +11,40 @@ type SyntaxTree struct { license string author string - requires []string - typeSections map[string] *TypeSection - objtSections map[string] *ObjtSection - enumSections map[string] *EnumSection - faceSections map[string] *FaceSection - dataSections map[string] *DataSection - funcSections map[string] *FuncSection + requires []string + sections map[string] Section } -// Identifier represents a chain of arguments separated by a dot. +// SectionKind differentiates Section interfaces. +type SectionKind int + +const ( + SectionKindType = iota + SectionKindObjt + SectionKindEnum + SectionKindFace + SectionKindData + SectionKindFunc +) + +// Section can be any kind of section. You can find out what type of section it +// is with the Kind method. +type Section interface { + Location () (location file.Location) + Kind () (kind SectionKind) + Permission () (permission types.Permission) + Name () (name string) + NewError (message string, kind infoerr.ErrorKind) (err error) + ToString (indent int) (output string) +} + +// Identifier represents a chain of names separated by a dot. type Identifier struct { - location file.Location - trail []string + locatable + trail []string } -// TypeKind represents what kind of type a type is +// TypeKind represents what kind of type a type is. type TypeKind int const ( @@ -36,19 +55,21 @@ const ( // TypeKindPointer means it's a pointer TypeKindPointer - // TypeKindArray means it's an array. + // TypeKindArray means it's a fixed length array. TypeKindArray + + // TypeKindVariableArray means it's an array of variable length. + TypeKindVariableArray ) // Type represents a type specifier type Type struct { - location file.Location + locatable mutable bool kind TypeKind - // only applicable for arrays. a value of zero means it has an - // undefined/dynamic length. + // only applicable for fixed length arrays. length uint64 // only applicable for basic. @@ -60,23 +81,23 @@ type Type struct { // Declaration represents a variable declaration. type Declaration struct { - location file.Location - name string - what Type + locatable + nameable + typeable } // ObjectInitializationValues represents a list of object member initialization // attributes. type ObjectInitializationValues struct { - location file.Location + locatable attributes map[string] Argument } // ArrayInitializationValues represents a list of attributes initializing an // array. type ArrayInitializationValues struct { - location file.Location - values []Argument + locatable + values []Argument } // ArgumentKind specifies the type of thing the value of an argument should be @@ -139,75 +160,73 @@ const ( // Argument represents a value that can be placed anywhere a value goes. This // allows things like phrases being arguments to other phrases. type Argument struct { - location file.Location - kind ArgumentKind - value any + locatable + kind ArgumentKind + value any // TODO: if there is an argument expansion operator its existence should // be stored here in a boolean. } // DataSection represents a global variable. type DataSection struct { - location file.Location - name string - - what Type - permission types.Permission - value Argument + locatable + nameable + typeable + permissionable + valuable } // TypeSection represents a blind type definition. type TypeSection struct { - location file.Location - name string - - inherits Type - permission types.Permission - defaultValue Argument + locatable + nameable + typeable + permissionable + valuable } // ObjtMember represents a part of an object type definition. type ObjtMember struct { - location file.Location - name string - - what Type - bitWidth uint64 - permission types.Permission - defaultValue Argument + locatable + nameable + typeable + permissionable + valuable + + bitWidth uint64 } // ObjtSection represents an object type definition. type ObjtSection struct { - location file.Location - name string + locatable + nameable + permissionable + inherits Identifier - inherits Identifier - permission types.Permission - members []ObjtMember + members []ObjtMember } // EnumMember represents a member of an enum section. type EnumMember struct { - location file.Location - name string - value Argument + locatable + nameable + valuable } // EnumSection represents an enumerated type section. type EnumSection struct { - location file.Location - name string - - what Type - permission types.Permission - members []EnumMember + locatable + nameable + typeable + permissionable + + members []EnumMember } // FaceBehavior represents a behavior of an interface section. type FaceBehavior struct { - location file.Location - name string + locatable + nameable inputs []Declaration outputs []Declaration @@ -215,12 +234,12 @@ type FaceBehavior struct { // FaceSection represents an interface type section. type FaceSection struct { - location file.Location - name string + locatable + nameable + permissionable inherits Identifier - permission types.Permission - behaviors map[string] FaceBehavior + behaviors map[string] FaceBehavior } // PhraseKind determines what semantic role a phrase plays. @@ -248,6 +267,8 @@ type Phrase struct { location file.Location command Argument arguments []Argument + // TODO: this is wack. it should be named after a plural noun like, + // returnees or something. accessor methods should beupdated to match. returnsTo []Argument kind PhraseKind @@ -263,14 +284,14 @@ type Block []Phrase // that it can have a default value. type FuncOutput struct { Declaration - defaultValue Argument + valuable } // FuncSection represents a function section. type FuncSection struct { - location file.Location - name string - permission types.Permission + locatable + nameable + permissionable receiver *Declaration inputs []Declaration diff --git a/parser/type.go b/parser/type.go index 04bc9fd..309e543 100644 --- a/parser/type.go +++ b/parser/type.go @@ -2,18 +2,17 @@ package parser import "git.tebibyte.media/arf/arf/types" import "git.tebibyte.media/arf/arf/lexer" -// import "git.tebibyte.media/arf/arf/infoerr" // parseTypeSection parses a blind type definition, meaning it can inherit from // anything including primitives, but cannot define structure. func (parser *ParsingOperation) parseTypeSection () ( - section *TypeSection, + section TypeSection, err error, ) { err = parser.expect(lexer.TokenKindName) if err != nil { return } - section = &TypeSection { location: parser.token.Location() } + section.location = parser.token.Location() // get permission err = parser.nextToken(lexer.TokenKindPermission) @@ -30,7 +29,7 @@ func (parser *ParsingOperation) parseTypeSection () ( if err != nil { return } err = parser.nextToken() if err != nil { return } - section.inherits, err = parser.parseType() + section.what, err = parser.parseType() if err != nil { return } // parse default values @@ -38,10 +37,10 @@ func (parser *ParsingOperation) parseTypeSection () ( err = parser.nextToken() if err != nil { return } - section.defaultValue, err = parser.parseInitializationValues(0) + section.value, err = parser.parseInitializationValues(0) if err != nil { return } } else { - section.defaultValue, err = parser.parseArgument() + section.value, err = parser.parseArgument() if err != nil { return } err = parser.expect(lexer.TokenKindNewline) diff --git a/types/iterator.go b/types/iterator.go new file mode 100644 index 0000000..acc8cc2 --- /dev/null +++ b/types/iterator.go @@ -0,0 +1,50 @@ +package types + +// Iterator is an object capable of iterating over any string-indexed map, while +// protecting its data. +type Iterator[VALUE_TYPE any] struct { + index int + keys []string + underlying map[string] VALUE_TYPE +} + +// NewIterator creates a new iterator that iterates over the specified map. +func NewIterator[VALUE_TYPE any] ( + underlying map[string] VALUE_TYPE, +) ( + iterator Iterator[VALUE_TYPE], +) { + iterator.underlying = underlying + iterator.keys = make([]string, len(underlying)) + + index := 0 + for key, _ := range underlying { + iterator.keys[index] = key + index ++ + } + + return +} + +// Key returns the current key the iterator is on. +func (iterator Iterator[VALUE_TYPE]) Key () (key string) { + key = iterator.keys[iterator.index] + return +} + +// Value returns the current value the iterator is on. +func (iterator Iterator[VALUE_TYPE]) Value () (value VALUE_TYPE) { + value = iterator.underlying[iterator.keys[iterator.index]] + return +} + +// Next advances the iterator by 1. +func (iterator Iterator[VALUE_TYPE]) Next () { + iterator.index ++ +} + +// End returns whether the iterator has reached the end of the map. +func (iterator Iterator[VALUE_TYPE]) End () (atEnd bool) { + atEnd = iterator.index >= len(iterator.keys) || iterator.index < 0 + return +}