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 } output := &spec.Output{} err = json.Unmarshal(data, output) if err != nil { return nil, err } return &output.Grammar, 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, "") case tok.Invalid(): fmt.Fprintf(b, "'%v' ()", 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) } }