diff --git a/parser/body.go b/parser/body.go index dfa27f7..5def988 100644 --- a/parser/body.go +++ b/parser/body.go @@ -21,6 +21,14 @@ func (parser *ParsingOperation) parseBody () (err error) { parser.tree.dataSections[section.name] = section if err != 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.root.name] = section + if err != nil { return } case "face": case "enum": case "func": diff --git a/parser/data.go b/parser/data.go index a131d24..47c90c4 100644 --- a/parser/data.go +++ b/parser/data.go @@ -94,9 +94,6 @@ func (parser *ParsingOperation) parseObjectInitializationValues () ( initializationValues ObjectInitializationValues, err error, ) { - println("BEGIN") - defer println("END") - initializationValues.attributes = make(map[string] Argument) baseIndent := 0 @@ -116,8 +113,6 @@ func (parser *ParsingOperation) parseObjectInitializationValues () ( // do not parse any further if the indent has changed if indent != baseIndent { break } - println("HIT") - // move on to the beginning of the line, which must contain // a member initialization value err = parser.nextToken(lexer.TokenKindDot) diff --git a/parser/parser_test.go b/parser/parser_test.go index 221084f..d628e91 100644 --- a/parser/parser_test.go +++ b/parser/parser_test.go @@ -109,3 +109,31 @@ data ro object:Obj `, test) } +func TestType (test *testing.T) { + checkTree ("../tests/parser/type", +`:arf +--- +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 + 923 + 92 +`, test) +} + diff --git a/parser/tree-tostring.go b/parser/tree-tostring.go index 75698ca..6dcb2ca 100644 --- a/parser/tree-tostring.go +++ b/parser/tree-tostring.go @@ -46,6 +46,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) + } + dataSectionKeys := sortMapKeysAlphabetically(tree.dataSections) for _, name := range dataSectionKeys { output += tree.dataSections[name].ToString(indent) @@ -246,3 +251,41 @@ func (section *DataSection) ToString (indent int) (output string) { } return } + +func (section *TypeSection) ToString (indent int) (output string) { + output += section.root.ToString(indent, true) + return +} + +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 { + 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) + } else { + output += " " + node.defaultValue.ToString(0, false) + output += "\n" + } + + return +} diff --git a/parser/tree.go b/parser/tree.go index 9ef87e7..63f885d 100644 --- a/parser/tree.go +++ b/parser/tree.go @@ -11,6 +11,7 @@ type SyntaxTree struct { author string requires []string + typeSections map[string] *TypeSection dataSections map[string] *DataSection } @@ -156,6 +157,23 @@ type DataSection struct { name string what Type - value Argument permission types.Permission + value Argument +} + +// TypeNode represents a part of a type. +type TypeNode 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 { + location file.Location + root TypeNode } diff --git a/parser/type.go b/parser/type.go new file mode 100644 index 0000000..a954396 --- /dev/null +++ b/parser/type.go @@ -0,0 +1,167 @@ +package parser + +import "git.tebibyte.media/sashakoshka/arf/types" +import "git.tebibyte.media/sashakoshka/arf/lexer" +import "git.tebibyte.media/sashakoshka/arf/infoerr" + +// parseTypeSection parses a type definition. +func (parser *ParsingOperation) parseTypeSection () ( + section *TypeSection, + err error, +) { + err = parser.expect(lexer.TokenKindName) + if err != nil { return } + + 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) + if err != nil { return } + node.permission = parser.token.Value().(types.Permission) + + // get name + err = parser.nextToken(lexer.TokenKindName) + if err != nil { return } + node.name = parser.token.Value().(string) + + // get inherited type + err = parser.nextToken(lexer.TokenKindColon) + if err != nil { return } + err = parser.nextToken() + if err != nil { return } + node.what, err = parser.parseType() + if err != nil { return } + + // get value, or child nodes + if parser.token.Is(lexer.TokenKindNewline) { + err = parser.nextToken() + if err != nil { return } + + err = parser.parseTypeNodeBlock(baseIndent, &node) + if err != nil { return } + } else { + node.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 +} + +// 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/type/main.arf b/tests/parser/type/main.arf new file mode 100644 index 0000000..b93fc54 --- /dev/null +++ b/tests/parser/type/main.arf @@ -0,0 +1,27 @@ +:arf +--- +type ro Basic:Int + +type ro BasicInit:Int 6 + +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