135 lines
3.2 KiB
Go
135 lines
3.2 KiB
Go
package metaParser
|
|
|
|
import "io"
|
|
import "fmt"
|
|
import "github.com/google/uuid"
|
|
import "git.tebibyte.media/fspl/fspl/lexer"
|
|
import "git.tebibyte.media/fspl/fspl/errors"
|
|
import "git.tebibyte.media/fspl/fspl/parser"
|
|
import "git.tebibyte.media/fspl/fspl/entity"
|
|
|
|
// Tree represents a parsed metadata file. It has no constructor and its zero
|
|
// value can be used safely.
|
|
type Tree struct {
|
|
entity.Metadata
|
|
}
|
|
|
|
// String returns a string representation of the tree. The result of this will
|
|
// be syntactically valid.
|
|
func (this *Tree) String () string {
|
|
out := entity.Quote(this.UUID.String()) + "\n"
|
|
for _, dependency := range this.Dependencies {
|
|
out += dependency.String() + "\n"
|
|
}
|
|
return out
|
|
}
|
|
|
|
// Parse parses the output of the given lexer into the tree.
|
|
func (this *Tree) Parse (lx lexer.Lexer) error {
|
|
parser, err := newParser(lx)
|
|
if err != nil { return err }
|
|
return parser.parseInto(this)
|
|
}
|
|
|
|
// addDirective adds a metadata directive to the tree.
|
|
func (this *Tree) addDirective (directive entity.Directive) {
|
|
switch directive := directive.(type) {
|
|
case *entity.Dependency:
|
|
this.Dependencies = append(this.Dependencies, directive)
|
|
default:
|
|
panic(fmt.Sprintf("unknown directive %T", directive))
|
|
}
|
|
}
|
|
|
|
type treeParser struct {
|
|
parser.Parser
|
|
tree *Tree
|
|
lexer lexer.Lexer
|
|
}
|
|
|
|
func newParser (lx lexer.Lexer) (*treeParser, error) {
|
|
return &treeParser {
|
|
Parser: parser.Parser {
|
|
Lexer: lx,
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
func (this *treeParser) parseInto (tree *Tree) error {
|
|
this.tree = tree
|
|
err := this.parse()
|
|
if err == io.EOF { err = nil }
|
|
return err
|
|
}
|
|
|
|
func (this *treeParser) parse () error {
|
|
err := this.Next()
|
|
if err != nil { return err }
|
|
this.tree.Position = this.Pos()
|
|
|
|
// String: UUID
|
|
err = this.Expect(lexer.String)
|
|
if err != nil { return err }
|
|
id, err := uuid.Parse(this.Value())
|
|
if err != nil {
|
|
return errors.Errorf(this.Pos(), "%v", err.Error())
|
|
}
|
|
this.tree.UUID = id
|
|
this.Next()
|
|
|
|
// parse directives
|
|
for !this.EOF() {
|
|
err := this.parseDirective()
|
|
if err != nil { return err }
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (this *treeParser) bug () string {
|
|
return fmt.Sprintln (
|
|
"Bug detected in the compiler!\n" +
|
|
"The metadata parser has taken an unexpected control path.",
|
|
"This could be due to an un-implemented feature.\n" +
|
|
"Please submit a report with this info and stack trace to:",
|
|
"https://git.tebibyte.media/sashakoshka/fspl/issues\n" +
|
|
"The token being parsed was:", this.Token)
|
|
}
|
|
|
|
func (this *treeParser) parseDirective () error {
|
|
err := this.ExpectValueDesc("directive", lexer.Symbol, "+")
|
|
if err != nil { return err }
|
|
|
|
var directive entity.Directive
|
|
switch this.Value() {
|
|
case "+":
|
|
// Symbol "+": Dependency
|
|
directive, err = this.parseDependency()
|
|
if err != nil { return err }
|
|
default: panic(this.bug())
|
|
}
|
|
this.tree.addDirective(directive)
|
|
return nil
|
|
}
|
|
|
|
func (this *treeParser) parseDependency () (*entity.Dependency, error) {
|
|
err := this.ExpectValueDesc("dependency", lexer.Symbol, "+")
|
|
if err != nil { return nil, err }
|
|
|
|
err = this.ExpectNextDesc("address", lexer.String)
|
|
if err != nil { return nil, err }
|
|
dependency := &entity.Dependency {
|
|
Position: this.Pos(),
|
|
Address: entity.Address(this.Value()),
|
|
}
|
|
|
|
this.Next()
|
|
if this.Is(lexer.Ident) {
|
|
// Ident: Nickname
|
|
dependency.Nickname = this.Value()
|
|
this.Next()
|
|
}
|
|
|
|
return dependency, nil
|
|
}
|