aboutsummaryrefslogtreecommitdiff
path: root/src/urubu/cmd/vartan/parse.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/urubu/cmd/vartan/parse.go')
-rw-r--r--src/urubu/cmd/vartan/parse.go110
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)
+ }
+}