diff --git a/examples/full/main.arf b/examples/full/main.arf index 8990b62..9db675a 100644 --- a/examples/full/main.arf +++ b/examples/full/main.arf @@ -9,7 +9,7 @@ data pv helloText:String "Hello, world!" # this is a struct definition objt ro Greeter:Obj - wr text:String "Hi." + rw text:String "Hi." # this is a function func ro main diff --git a/parser/body.go b/parser/body.go index a5bf767..3174e13 100644 --- a/parser/body.go +++ b/parser/body.go @@ -39,6 +39,14 @@ func (parser *ParsingOperation) parseBody () (err error) { parser.tree.objtSections[section.name] = section if err != 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 } case "enum": var section *EnumSection section, err = parser.parseEnumSection() diff --git a/parser/face.go b/parser/face.go new file mode 100644 index 0000000..3a53381 --- /dev/null +++ b/parser/face.go @@ -0,0 +1,132 @@ +package parser + +import "git.tebibyte.media/sashakoshka/arf/types" +import "git.tebibyte.media/sashakoshka/arf/lexer" +import "git.tebibyte.media/sashakoshka/arf/infoerr" + +// parseFaceSection parses an interface section. +func (parser *ParsingOperation) parseFaceSection () ( + section *FaceSection, + err error, +) { + err = parser.expect(lexer.TokenKindName) + if err != nil { return } + + section = &FaceSection { + location: parser.token.Location(), + behaviors: make(map[string] FaceBehavior), + } + + // 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 interface + err = parser.nextToken(lexer.TokenKindColon) + if err != nil { return } + err = parser.nextToken(lexer.TokenKindName) + if err != nil { return } + section.inherits = parser.token.Value().(string) + if err != nil { return } + err = parser.nextToken(lexer.TokenKindNewline) + if err != nil { return } + err = parser.nextToken() + if err != nil { return } + + // parse members + for { + // if we've left the block, stop parsing + if !parser.token.Is(lexer.TokenKindIndent) { return } + if parser.token.Value().(int) != 1 { return } + + // parse behavior + behaviorBeginning := parser.token.Location() + var behavior FaceBehavior + behavior, err = parser.parseFaceBehavior() + + // add to section + _, exists := section.behaviors[behavior.name] + if exists { + err = infoerr.NewError ( + behaviorBeginning, + "multiple behaviors named " + behavior.name + + " in this interface", + infoerr.ErrorKindError) + return + } + section.behaviors[behavior.name] = behavior + + if err != nil { return } + } + return +} + +// parseFaceBehavior parses a single interface behavior. Indentation level is +// assumed. +func (parser *ParsingOperation) parseFaceBehavior () ( + behavior FaceBehavior, + err error, +) { + err = parser.expect(lexer.TokenKindIndent) + if err != nil { return } + + // get name + err = parser.nextToken(lexer.TokenKindName) + if err != nil { return } + behavior.name = parser.token.Value().(string) + + err = parser.nextToken(lexer.TokenKindNewline) + if err != nil { return } + err = parser.nextToken() + if err != nil { return } + + for { + // if we've left the block, stop parsing + if !parser.token.Is(lexer.TokenKindIndent) { return } + if parser.token.Value().(int) != 2 { return } + + // get preceding symbol + err = parser.nextToken ( + lexer.TokenKindGreaterThan, + lexer.TokenKindLessThan) + if err != nil { return } + kind := parser.token.Kind() + + var declaration Declaration + + // get name + err = parser.nextToken(lexer.TokenKindName) + if err != nil { return } + declaration.name = parser.token.Value().(string) + + // parse inherited type + err = parser.nextToken(lexer.TokenKindColon) + if err != nil { return } + err = parser.nextToken() + if err != nil { return } + declaration.what, err = parser.parseType() + if err != nil { return } + err = parser.expect(lexer.TokenKindNewline) + if err != nil { return } + err = parser.nextToken() + if err != nil { return } + + if kind == lexer.TokenKindGreaterThan { + behavior.inputs = append ( + behavior.inputs, + declaration) + } else { + behavior.outputs = append ( + behavior.outputs, + declaration) + } + } + + return +} diff --git a/parser/parser_test.go b/parser/parser_test.go index ca4b7a0..cf25d54 100644 --- a/parser/parser_test.go +++ b/parser/parser_test.go @@ -192,3 +192,21 @@ enum ro Weekday:Int wednesday `, test) } + +func Test (test *testing.T) { + checkTree ("../tests/parser/face", +`:arf +--- +face ro Destroyer:Face + destroy +face ro ReadWriter:Face + read + > into:{Byte ..} + < read:Int + < err:Error + write + > data:{Byte ..} + < wrote:Int + < err:Error +`, test) +} diff --git a/parser/tree-tostring.go b/parser/tree-tostring.go index 8c67798..df8a9b1 100644 --- a/parser/tree-tostring.go +++ b/parser/tree-tostring.go @@ -61,6 +61,11 @@ func (tree *SyntaxTree) ToString (indent int) (output string) { 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) @@ -353,3 +358,32 @@ func (section *EnumSection) ToString (indent int) (output string) { } return } + +func (section *FaceSection) ToString (indent int) (output string) { + output += doIndent ( + indent, + "face ", + section.permission.ToString(), " ", + section.name, ":", + section.inherits, "\n") + + for _, name := range sortMapKeysAlphabetically(section.behaviors) { + behavior := section.behaviors[name] + output += behavior.ToString(indent + 1) + } + return +} + +func (behavior *FaceBehavior) ToString (indent int) (output string) { + output += doIndent(indent, behavior.name, "\n") + + for _, inputItem := range behavior.inputs { + output += doIndent(indent + 1, "> ", inputItem.ToString(), "\n") + } + + for _, outputItem := range behavior.outputs { + output += doIndent(indent + 1, "< ", outputItem.ToString(), "\n") + } + + return +} diff --git a/parser/tree.go b/parser/tree.go index 559dbc8..f6e6e55 100644 --- a/parser/tree.go +++ b/parser/tree.go @@ -14,6 +14,7 @@ type SyntaxTree struct { typeSections map[string] *TypeSection objtSections map[string] *ObjtSection enumSections map[string] *EnumSection + faceSections map[string] *FaceSection dataSections map[string] *DataSection } @@ -203,3 +204,22 @@ type EnumSection struct { // TODO: order matters here we need to store these in an array members map[string] Argument } + +// FaceBehavior represents a behavior of an interface section. +type FaceBehavior struct { + location file.Location + name string + + inputs []Declaration + outputs []Declaration +} + +// FaceSection represents an interface type section. +type FaceSection struct { + location file.Location + name string + inherits string + + permission types.Permission + behaviors map[string] FaceBehavior +} diff --git a/tests/parser/face/main.arf b/tests/parser/face/main.arf new file mode 100644 index 0000000..4a78a7c --- /dev/null +++ b/tests/parser/face/main.arf @@ -0,0 +1,15 @@ +:arf +--- + +face ro ReadWriter:Face + write + > data:{Byte ..} + < wrote:Int + < err:Error + read + > into:{Byte ..} + < read:Int + < err:Error + +face ro Destroyer:Face + destroy