Merge pull request 'syntax-tree-accessors' (#2) from syntax-tree-accessors into main

Reviewed-on: arf/arf#2
This commit is contained in:
Sasha Koshka 2022-09-05 14:52:09 +00:00
commit 464826f447
16 changed files with 629 additions and 219 deletions

276
parser/accessors.go Normal file
View File

@ -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
}

View File

@ -42,12 +42,14 @@ func (parser *ParsingOperation) parseArgument () (argument Argument, err error)
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

View File

@ -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
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
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
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
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
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
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
}

View File

@ -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 }

View File

@ -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(&section)
if err != nil { return }
if len(section.members) == 0 {

View File

@ -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)

View File

@ -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(&section)
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 }

View File

@ -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 }

70
parser/node-traits.go Normal file
View File

@ -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
}

View File

@ -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(&section)
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)

View File

@ -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]

View File

@ -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)

View File

@ -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 ",

View File

@ -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
@ -11,21 +12,39 @@ type SyntaxTree struct {
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
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
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,22 +81,22 @@ 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
locatable
values []Argument
}
@ -139,7 +160,7 @@ 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
locatable
kind ArgumentKind
value any
// TODO: if there is an argument expansion operator its existence should
@ -148,66 +169,64 @@ type Argument struct {
// 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
locatable
nameable
typeable
permissionable
valuable
what Type
bitWidth uint64
permission types.Permission
defaultValue Argument
}
// ObjtSection represents an object type definition.
type ObjtSection struct {
location file.Location
name string
locatable
nameable
permissionable
inherits Identifier
permission types.Permission
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
locatable
nameable
typeable
permissionable
what Type
permission types.Permission
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,11 +234,11 @@ 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
}
@ -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

View File

@ -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)

50
types/iterator.go Normal file
View File

@ -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
}