diff options
Diffstat (limited to 'src/urubu/cmd/vartan/parse.go')
-rw-r--r-- | src/urubu/cmd/vartan/parse.go | 110 |
1 files changed, 110 insertions, 0 deletions
diff --git a/src/urubu/cmd/vartan/parse.go b/src/urubu/cmd/vartan/parse.go new file mode 100644 index 0000000..9c5fd9c --- /dev/null +++ b/src/urubu/cmd/vartan/parse.go @@ -0,0 +1,110 @@ +package main + +import ( + "encoding/json" + "fmt" + "io" + "os" + "strings" + + driver "urubu/driver/parser" + spec "urubu/spec/grammar" +) + + + +func runParse(args []string) error { + cg, err := readCompiledGrammar(args[0]) + if err != nil { + return fmt.Errorf("Cannot read a compiled grammar: %w", err) + } + + src := os.Stdin + gram := driver.NewGrammar(cg) + + tb := driver.NewDefaultSyntaxTreeBuilder() + treeAct := driver.NewCSTActionSet(gram, tb) + + opts := []driver.ParserOption{} + opts = append(opts, driver.SemanticAction(treeAct)) + + toks, err := driver.NewTokenStream(cg, src) + if err != nil { + return err + } + + p, err := driver.NewParser(toks, gram, opts...) + if err != nil { + return err + } + + err = p.Parse() + if err != nil { + return err + } + + // A parser can construct a parse tree even if syntax errors occur. + // When therer is a parse tree, print it. + if tree := tb.Tree(); tree != nil { + b, err := json.Marshal(tree) + if err != nil { + return err + } + fmt.Fprintln(os.Stdout, string(b)) + } + + if len(p.SyntaxErrors()) > 0 { + var b strings.Builder + synErrs := p.SyntaxErrors() + writeSyntaxErrorMessage(&b, cg, synErrs[0]) + for _, synErr := range synErrs[1:] { + fmt.Fprintf(&b, "\n") + writeSyntaxErrorMessage(&b, cg, synErr) + } + if b.Len() > 0 { + return fmt.Errorf(b.String()) + } + } + + return nil +} + +func readCompiledGrammar(path string) (*spec.CompiledGrammar, error) { + f, err := os.Open(path) + if err != nil { + return nil, err + } + data, err := io.ReadAll(f) + if err != nil { + return nil, err + } + cg := &spec.CompiledGrammar{} + err = json.Unmarshal(data, cg) + if err != nil { + return nil, err + } + return cg, nil +} + +func writeSyntaxErrorMessage(b *strings.Builder, cgram *spec.CompiledGrammar, synErr *driver.SyntaxError) { + fmt.Fprintf(b, "%v:%v: %v: ", synErr.Row+1, synErr.Col+1, synErr.Message) + + tok := synErr.Token + switch { + case tok.EOF(): + fmt.Fprintf(b, "<eof>") + case tok.Invalid(): + fmt.Fprintf(b, "'%v' (<invalid>)", string(tok.Lexeme())) + default: + if kind := cgram.Syntactic.Terminals[tok.TerminalID()]; kind != "" { + fmt.Fprintf(b, "'%v' (%v)", string(tok.Lexeme()), kind) + } else { + fmt.Fprintf(b, "'%v'", string(tok.Lexeme())) + } + } + + fmt.Fprintf(b, ": expected: %v", synErr.ExpectedTerminals[0]) + for _, t := range synErr.ExpectedTerminals[1:] { + fmt.Fprintf(b, ", %v", t) + } +} |