diff --git a/parser/body.go b/parser/body.go index 5def988..82b2635 100644 --- a/parser/body.go +++ b/parser/body.go @@ -27,7 +27,16 @@ func (parser *ParsingOperation) parseBody () (err error) { parser.tree.typeSections = make(map[string] *TypeSection) } - parser.tree.typeSections[section.root.name] = section + parser.tree.typeSections[section.name] = section + if err != 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 } case "face": case "enum": diff --git a/parser/objt.go b/parser/objt.go new file mode 100644 index 0000000..f384cff --- /dev/null +++ b/parser/objt.go @@ -0,0 +1,119 @@ +package parser + +import "git.tebibyte.media/sashakoshka/arf/types" +import "git.tebibyte.media/sashakoshka/arf/lexer" +import "git.tebibyte.media/sashakoshka/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, + err error, +) { + err = parser.expect(lexer.TokenKindName) + if err != nil { return } + + section = &ObjtSection { + location: parser.token.Location(), + members: make(map[string] ObjtMember), + } + + // get permission + err = parser.nextToken(lexer.TokenKindPermission) + if err != nil { return } + section.permission = parser.token.Value().(types.Permission) + + // get name + err = parser.nextToken(lexer.TokenKindName) + if err != nil { return } + section.name = parser.token.Value().(string) + + // parse inherited type + err = parser.nextToken(lexer.TokenKindColon) + if err != nil { return } + err = parser.nextToken() + if err != nil { return } + section.inherits, err = parser.parseType() + if err != nil { return } + err = parser.expect(lexer.TokenKindNewline) + if err != nil { return } + err = parser.nextToken() + if err != nil { return } + + // parse members + err = parser.parseObjtMembers(section) + if err != nil { return } + + if len(section.members) == 0 { + infoerr.NewError ( + section.location, + "defining an object with no members", + infoerr.ErrorKindWarn).Print() + } + return +} + +// parseObjtMembers parses a list of members for an object section. Indentation +// level is assumed. +func (parser *ParsingOperation) parseObjtMembers ( + into *ObjtSection, +) ( + err error, +) { + for { + // if we've left the block, stop parsing + if !parser.token.Is(lexer.TokenKindIndent) { return } + if parser.token.Value().(int) != 1 { return } + + // add member to object section + var member ObjtMember + member, err = parser.parseObjtMember() + into.members[member.name] = member + if err != nil { return } + } +} + +// parseObjtMember parses a single member of an object section. Indentation +// level is assumed. +func (parser *ParsingOperation) parseObjtMember () ( + member ObjtMember, + err error, +) { + // get permission + err = parser.nextToken(lexer.TokenKindPermission) + if err != nil { return } + member.permission = parser.token.Value().(types.Permission) + + // get name + err = parser.nextToken(lexer.TokenKindName) + if err != nil { return } + member.name = parser.token.Value().(string) + + // get type + err = parser.nextToken(lexer.TokenKindColon) + if err != nil { return } + err = parser.nextToken() + if err != nil { return } + member.what, err = parser.parseType() + if err != nil { return } + + // parse default value + if parser.token.Is(lexer.TokenKindNewline) { + err = parser.nextToken() + if err != nil { return } + + member.defaultValue, + err = parser.parseInitializationValues(1) + if err != nil { return } + } else { + member.defaultValue, err = parser.parseArgument() + if err != nil { return } + + err = parser.expect(lexer.TokenKindNewline) + if err != nil { return } + err = parser.nextToken() + if err != nil { return } + } + + return +} diff --git a/parser/parser_test.go b/parser/parser_test.go index d628e91..d433d64 100644 --- a/parser/parser_test.go +++ b/parser/parser_test.go @@ -1,6 +1,7 @@ package parser import "io" +import "strings" import "testing" // import "git.tebibyte.media/sashakoshka/arf/types" @@ -10,9 +11,9 @@ func checkTree (modulePath string, correct string, test *testing.T) { treeRunes := []rune(treeString) test.Log("CORRECT TREE:") - test.Log(correct) + logWithLineNumbers(correct, test) test.Log("WHAT WAS PARSED:") - test.Log(treeString) + logWithLineNumbers(treeString, test) if err != io.EOF && err != nil { test.Log("returned error:") @@ -63,6 +64,15 @@ func checkTree (modulePath string, correct string, test *testing.T) { } } +func logWithLineNumbers (bigString string, test *testing.T) { + lines := strings.Split ( + strings.Replace(bigString, "\t", " ", -1), "\n") + + for index, line := range lines { + test.Logf("%3d | %s", index + 1, line) + } +} + func TestMeta (test *testing.T) { checkTree ("../tests/parser/meta", `:arf @@ -115,20 +125,6 @@ func TestType (test *testing.T) { --- type ro Basic:Int type ro BasicInit:Int 6 -type ro Complex:Obj - ro that:Basic - ro this:Basic -type ro ComplexInit:Obj - ro that:BasicInit - ro this:Basic 23 -type ro ComplexWithComplexInit:Obj - ro basic:Basic 87 - ro complex0:Complex - .that 98 - .this 2 - ro complex1:Complex - .that 98902 - .this 235 type ro IntArray:{Int ..} type ro IntArrayInit:{Int 3} 3298 @@ -137,3 +133,28 @@ type ro IntArrayInit:{Int 3} `, test) } +func TestObjt (test *testing.T) { + checkTree ("../tests/parser/objt", +`:arf +--- +objt ro Basic:Obj + ro that:Basic + ro this:Basic +objt ro ComplexInit:Obj + ro basic:Int 87 + ro complex0:Bird + .that 98 + .this 2 + ro complex1:Bird + .that 98902 + .this 235 + ro whatever:{Int 3} + 230984 + 849 + 394580 +objt ro Init:Obj + ro that:String "hello world" + ro this:Int 23 +`, test) +} + diff --git a/parser/tree-tostring.go b/parser/tree-tostring.go index 6dcb2ca..d469ed3 100644 --- a/parser/tree-tostring.go +++ b/parser/tree-tostring.go @@ -51,6 +51,11 @@ func (tree *SyntaxTree) ToString (indent int) (output string) { output += tree.typeSections[name].ToString(indent) } + objtSectionKeys := sortMapKeysAlphabetically(tree.objtSections) + for _, name := range objtSectionKeys { + output += tree.objtSections[name].ToString(indent) + } + dataSectionKeys := sortMapKeysAlphabetically(tree.dataSections) for _, name := range dataSectionKeys { output += tree.dataSections[name].ToString(indent) @@ -253,39 +258,64 @@ func (section *DataSection) ToString (indent int) (output string) { } func (section *TypeSection) ToString (indent int) (output string) { - output += section.root.ToString(indent, true) - return -} + output += doIndent ( + indent, + "type ", + section.permission.ToString(), " ", + section.name, ":", + section.inherits.ToString()) -func (node TypeNode) ToString (indent int, isRoot bool) (output string) { - output += doIndent(indent) - if isRoot { - output += "type " - } - - output += node.permission.ToString() + " " - output += node.name + ":" - output += node.what.ToString() - isComplexInitialization := - node.defaultValue.kind == ArgumentKindObjectInitializationValues || - node.defaultValue.kind == ArgumentKindArrayInitializationValues - - if node.defaultValue.value == nil { + section.defaultValue.kind == ArgumentKindObjectInitializationValues || + section.defaultValue.kind == ArgumentKindArrayInitializationValues + + if section.defaultValue.value == nil { output += "\n" - if len(node.children) > 0 { - for _, name := range sortMapKeysAlphabetically(node.children) { - child := node.children[name] - output += child.ToString(indent + 1, false) - } - } } else if isComplexInitialization { output += "\n" - output += node.defaultValue.ToString(indent + 1, true) + output += section.defaultValue.ToString(indent + 1, true) } else { - output += " " + node.defaultValue.ToString(0, false) - output += "\n" + output += " " + section.defaultValue.ToString(0, false) + output += "\n" + } + return +} + +func (member ObjtMember) ToString (indent int) (output string) { + output += doIndent(indent) + + output += member.permission.ToString() + " " + output += member.name + ":" + output += member.what.ToString() + + isComplexInitialization := + member.defaultValue.kind == ArgumentKindObjectInitializationValues || + member.defaultValue.kind == ArgumentKindArrayInitializationValues + + if member.defaultValue.value == nil { + output += "\n" + } else if isComplexInitialization { + output += "\n" + output += member.defaultValue.ToString(indent + 1, true) + } else { + output += " " + member.defaultValue.ToString(0, false) + output += "\n" } return } + +func (section *ObjtSection) ToString (indent int) (output string) { + output += doIndent ( + indent, + "objt ", + section.permission.ToString(), " ", + section.name, ":", + section.inherits.ToString(), "\n") + + for _, name := range sortMapKeysAlphabetically(section.members) { + output += section.members[name].ToString(indent + 1) + } + return +} + diff --git a/parser/tree.go b/parser/tree.go index 63f885d..c1b5e74 100644 --- a/parser/tree.go +++ b/parser/tree.go @@ -12,6 +12,7 @@ type SyntaxTree struct { requires []string typeSections map[string] *TypeSection + objtSections map[string] *ObjtSection dataSections map[string] *DataSection } @@ -161,19 +162,32 @@ type DataSection struct { value Argument } -// TypeNode represents a part of a type. -type TypeNode struct { +// TypeSection represents a blind type definition. +type TypeSection struct { + location file.Location + name string + + inherits Type + permission types.Permission + defaultValue Argument +} + +// ObjtMember represents a part of an object type definition. +type ObjtMember struct { location file.Location name string what Type permission types.Permission defaultValue Argument - children map[string] TypeNode } -// TypeSection represents a type definition. -type TypeSection struct { +// ObjtSection represents an object type definition +type ObjtSection struct { location file.Location - root TypeNode + name string + + inherits Type + permission types.Permission + members map[string] ObjtMember } diff --git a/parser/type.go b/parser/type.go index a954396..9691dcf 100644 --- a/parser/type.go +++ b/parser/type.go @@ -2,9 +2,10 @@ package parser import "git.tebibyte.media/sashakoshka/arf/types" import "git.tebibyte.media/sashakoshka/arf/lexer" -import "git.tebibyte.media/sashakoshka/arf/infoerr" +// import "git.tebibyte.media/sashakoshka/arf/infoerr" -// parseTypeSection parses a type definition. +// parseTypeSection parses a blind type definition, meaning it can inherit from +// anything including primitives, but cannot define structure. func (parser *ParsingOperation) parseTypeSection () ( section *TypeSection, err error, @@ -14,50 +15,33 @@ func (parser *ParsingOperation) parseTypeSection () ( section = &TypeSection { location: parser.token.Location() } - // parse root node - err = parser.nextToken() - if err != nil { return } - section.root, err = parser.parseTypeNode(0) - - return -} - -// parseTypeNode parses a single type definition node recursively. -func (parser *ParsingOperation) parseTypeNode ( - baseIndent int, -) ( - node TypeNode, - err error, -) { - node.children = make(map[string] TypeNode) - // get permission - err = parser.expect(lexer.TokenKindPermission) + err = parser.nextToken(lexer.TokenKindPermission) if err != nil { return } - node.permission = parser.token.Value().(types.Permission) + section.permission = parser.token.Value().(types.Permission) // get name err = parser.nextToken(lexer.TokenKindName) if err != nil { return } - node.name = parser.token.Value().(string) + section.name = parser.token.Value().(string) - // get inherited type + // parse inherited type err = parser.nextToken(lexer.TokenKindColon) if err != nil { return } err = parser.nextToken() if err != nil { return } - node.what, err = parser.parseType() + section.inherits, err = parser.parseType() if err != nil { return } - // get value, or child nodes + // parse default values if parser.token.Is(lexer.TokenKindNewline) { err = parser.nextToken() if err != nil { return } - err = parser.parseTypeNodeBlock(baseIndent, &node) + section.defaultValue, err = parser.parseInitializationValues(0) if err != nil { return } } else { - node.defaultValue, err = parser.parseArgument() + section.defaultValue, err = parser.parseArgument() if err != nil { return } err = parser.expect(lexer.TokenKindNewline) @@ -67,101 +51,3 @@ func (parser *ParsingOperation) parseTypeNode ( } return } - -// parseTypeNodeBlock starts on the line after a type node, and parses what -// could be either an array initialization, an object initialization, or more -// child nodes. It is similar to parseInitializationValues. If none of these -// things were found the parser stays at the beginning of the line and the -// method returns. -func (parser *ParsingOperation) parseTypeNodeBlock ( - baseIndent int, - parent *TypeNode, -) ( - err error, -) { - // check if line is indented one more than baseIndent - if !parser.token.Is(lexer.TokenKindIndent) { return } - if parser.token.Value().(int) != baseIndent + 1 { return } - - thingLocation := parser.token.Location() - - err = parser.nextToken() - if err != nil { return } - - if parser.token.Is(lexer.TokenKindDot) { - - // object initialization - parser.previousToken() - initializationArgument := Argument { location: thingLocation } - var initializationValues ObjectInitializationValues - initializationValues, err = parser.parseObjectInitializationValues() - initializationArgument.kind = ArgumentKindObjectInitializationValues - initializationArgument.value = &initializationValues - parent.defaultValue = initializationArgument - - } else if parser.token.Is(lexer.TokenKindPermission) { - - // child members - parser.previousToken() - err = parser.parseTypeNodeChildren(parent) - - } else { - - // array initialization - parser.previousToken() - initializationArgument := Argument { location: thingLocation } - var initializationValues ArrayInitializationValues - initializationValues, err = parser.parseArrayInitializationValues() - initializationArgument.kind = ArgumentKindArrayInitializationValues - initializationArgument.value = &initializationValues - parent.defaultValue = initializationArgument - } - - return -} - -// parseTypeNodeChildren parses child type nodes into a parent type node. -func (parser *ParsingOperation) parseTypeNodeChildren ( - parent *TypeNode, -) ( - err error, -) { - baseIndent := 0 - begin := true - - for { - // if there is no indent we can just stop parsing - if !parser.token.Is(lexer.TokenKindIndent) { break } - indent := parser.token.Value().(int) - - if begin == true { - baseIndent = indent - begin = false - } - - // do not parse any further if the indent has changed - if indent != baseIndent { break } - - // move on to the beginning of the line, which must contain - // a type node - err = parser.nextToken() - if err != nil { return } - var child TypeNode - child, err = parser.parseTypeNode(baseIndent) - - // if the member has already been listed, throw an error - _, exists := parent.children[child.name] - if exists { - err = parser.token.NewError ( - "duplicate member \"" + child.name + - "\" in object member initialization", - infoerr.ErrorKindError) - return - } - - // store in parent - parent.children[child.name] = child - } - - return -} diff --git a/tests/parser/objt/main.arf b/tests/parser/objt/main.arf new file mode 100644 index 0000000..a151314 --- /dev/null +++ b/tests/parser/objt/main.arf @@ -0,0 +1,21 @@ +:arf +--- +objt ro Basic:Obj + ro that:Basic + ro this:Basic + +objt ro Init:Obj + ro that:String "hello world" + ro this:Int 23 + +objt ro ComplexInit:Obj + ro whatever:{Int 3} + 230984 + 849 394580 + ro complex0:Bird + .that 98 + .this 2 + ro complex1:Bird + .that 98902 + .this 235 + ro basic:Int 87 diff --git a/tests/parser/type/main.arf b/tests/parser/type/main.arf index b93fc54..d7feb7e 100644 --- a/tests/parser/type/main.arf +++ b/tests/parser/type/main.arf @@ -8,20 +8,3 @@ type ro IntArray:{Int ..} type ro IntArrayInit:{Int 3} 3298 923 92 - -type ro Complex:Obj - ro that:Basic - ro this:Basic - -type ro ComplexInit:Obj - ro that:BasicInit - ro this:Basic 23 - -type ro ComplexWithComplexInit:Obj - ro complex0:Complex - .that 98 - .this 2 - ro complex1:Complex - .that 98902 - .this 235 - ro basic:Basic 87