From d904e8224505fbbc7ae6f4a412a14096dcb2fde8 Mon Sep 17 00:00:00 2001 From: Ryo Nihei Date: Thu, 2 Sep 2021 00:19:30 +0900 Subject: Rename describe command to show command --- README.md | 47 ++++++++- cmd/vartan/describe.go | 273 ------------------------------------------------- cmd/vartan/show.go | 273 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 319 insertions(+), 274 deletions(-) delete mode 100644 cmd/vartan/describe.go create mode 100644 cmd/vartan/show.go diff --git a/README.md b/README.md index 6c323e9..679d677 100644 --- a/README.md +++ b/README.md @@ -76,4 +76,49 @@ When `vartan parse` command successfully parses the input data, it prints a CST ## Debug -`vartan compile` command also generates a description file having `.desc` suffix along with a parsing table. This file describes each state in the parsing table in detail. If your grammar contains conflicts, see `Conflicts` section of this file. +`vartan compile` command also generates a description file having `-description.json` suffix along with a parsing table. This file describes each state in the parsing table in detail. If your grammar contains conflicts, see `Conflicts` and `States` sections of this file. Using `vartan show` command, you can see the description file in a readable format. + +```sh +$ vartan show expr-description.json +# Class + +LALR(1) + +# Conflicts + +No conflict was detected. + +# Terminals + + 1 - - + 2 - - error + 3 - - whitespaces + 4 - - number + 5 - - id + 6 - - add_op (+) + 7 - - mul_op (*) + +# Productions + + 1 - - expr' → expr + 2 - - expr → expr + term + 3 - - expr → term + 4 - - term → term * factor + 5 - - term → factor + 6 - - factor → number + 7 - - factor → id + +# States + +## State 0 + + 1 expr' → ・ expr + +shift 4 on number +shift 5 on id +goto 1 on expr +goto 2 on term +goto 3 on factor + +... +``` diff --git a/cmd/vartan/describe.go b/cmd/vartan/describe.go deleted file mode 100644 index 2dabf9f..0000000 --- a/cmd/vartan/describe.go +++ /dev/null @@ -1,273 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "io" - "io/ioutil" - "os" - "runtime/debug" - "strings" - "text/template" - - "github.com/nihei9/vartan/spec" - "github.com/spf13/cobra" -) - -var describeFlags = struct { -}{} - -func init() { - cmd := &cobra.Command{ - Use: "describe", - Short: "Print a description file in readable format", - Example: ` vartan describe grammar-description.json`, - Args: cobra.ExactArgs(1), - RunE: runDescribe, - } - rootCmd.AddCommand(cmd) -} - -func runDescribe(cmd *cobra.Command, args []string) (retErr error) { - defer func() { - panicked := false - v := recover() - if v != nil { - err, ok := v.(error) - if !ok { - retErr = fmt.Errorf("an unexpected error occurred: %v", v) - fmt.Fprintf(os.Stderr, "%v:\n%v", retErr, string(debug.Stack())) - return - } - - retErr = err - panicked = true - } - - if retErr != nil { - if panicked { - fmt.Fprintf(os.Stderr, "%v:\n%v", retErr, string(debug.Stack())) - } else { - fmt.Fprintf(os.Stderr, "%v\n", retErr) - } - } - }() - - desc, err := readDescription(args[0]) - if err != nil { - return err - } - - err = writeDescription(os.Stdout, desc) - if err != nil { - return err - } - - return nil -} - -func readDescription(path string) (*spec.Description, error) { - f, err := os.Open(path) - if err != nil { - return nil, fmt.Errorf("Cannot open the description file %s: %w", path, err) - } - defer f.Close() - - d, err := ioutil.ReadAll(f) - if err != nil { - return nil, err - } - - desc := &spec.Description{} - err = json.Unmarshal(d, desc) - if err != nil { - return nil, err - } - - return desc, nil -} - -const descTemplate = `# Class - -{{ .Class }} - -# Conflicts - -{{ printConflictSummary . }} - -# Terminals - -{{ range slice .Terminals 1 -}} -{{ printTerminal . }} -{{ end }} -# Productions - -{{ range slice .Productions 1 -}} -{{ printProduction . }} -{{ end }} -# States -{{ range .States }} -## State {{ .Number }} - -{{ range .Kernel -}} -{{ printItem . }} -{{ end }} -{{ range .Shift -}} -{{ printShift . }} -{{ end -}} -{{ range .Reduce -}} -{{ printReduce . }} -{{ end -}} -{{ range .GoTo -}} -{{ printGoTo . }} -{{ end }} -{{ range .SRConflict -}} -{{ printSRConflict . }} -{{ end -}} -{{ range .RRConflict -}} -{{ printRRConflict . }} -{{ end -}} -{{ end }}` - -func writeDescription(w io.Writer, desc *spec.Description) error { - termName := func(sym int) string { - if desc.Terminals[sym].Alias != "" { - return desc.Terminals[sym].Alias - } - return desc.Terminals[sym].Name - } - - nonTermName := func(sym int) string { - return desc.NonTerminals[sym].Name - } - - fns := template.FuncMap{ - "printConflictSummary": func(desc *spec.Description) string { - count := 0 - for _, s := range desc.States { - count += len(s.SRConflict) - count += len(s.RRConflict) - } - - if count == 1 { - return fmt.Sprintf("1 conflict was detected.") - } else if count > 1 { - return fmt.Sprintf("%v conflicts were detected.", count) - } - return "No conflict was detected." - }, - "printTerminal": func(term spec.Terminal) string { - var prec string - if term.Precedence != 0 { - prec = fmt.Sprintf("%2v", term.Precedence) - } else { - prec = " -" - } - - var assoc string - if term.Associativity != "" { - assoc = term.Associativity - } else { - assoc = "-" - } - - if term.Alias != "" { - return fmt.Sprintf("%4v %v %v %v (%v)", term.Number, prec, assoc, term.Name, term.Alias) - } - return fmt.Sprintf("%4v %v %v %v", term.Number, prec, assoc, term.Name) - }, - "printProduction": func(prod spec.Production) string { - var prec string - if prod.Precedence != 0 { - prec = fmt.Sprintf("%2v", prod.Precedence) - } else { - prec = " -" - } - - var assoc string - if prod.Associativity != "" { - assoc = prod.Associativity - } else { - assoc = "-" - } - - var b strings.Builder - fmt.Fprintf(&b, "%v →", nonTermName(prod.LHS)) - if len(prod.RHS) > 0 { - for _, e := range prod.RHS { - if e > 0 { - fmt.Fprintf(&b, " %v", termName(e)) - } else { - fmt.Fprintf(&b, " %v", nonTermName(e*-1)) - } - } - } else { - fmt.Fprintf(&b, " ε") - } - - return fmt.Sprintf("%4v %v %v %v", prod.Number, prec, assoc, b.String()) - }, - "printItem": func(item spec.Item) string { - prod := desc.Productions[item.Production] - - var b strings.Builder - fmt.Fprintf(&b, "%v →", nonTermName(prod.LHS)) - for i, e := range prod.RHS { - if i == item.Dot { - fmt.Fprintf(&b, " ・") - } - if e > 0 { - fmt.Fprintf(&b, " %v", termName(e)) - } else { - fmt.Fprintf(&b, " %v", nonTermName(e*-1)) - } - } - if item.Dot >= len(prod.RHS) { - fmt.Fprintf(&b, " ・") - } - - return fmt.Sprintf("%4v %v", prod.Number, b.String()) - }, - "printShift": func(tran spec.Transition) string { - return fmt.Sprintf("shift %4v on %v", tran.State, termName(tran.Symbol)) - }, - "printReduce": func(reduce spec.Reduce) string { - var b strings.Builder - { - fmt.Fprintf(&b, "%v", termName(reduce.LookAhead[0])) - for _, a := range reduce.LookAhead[1:] { - fmt.Fprintf(&b, ", %v", termName(a)) - } - } - return fmt.Sprintf("reduce %4v on %v", reduce.Production, b.String()) - }, - "printGoTo": func(tran spec.Transition) string { - return fmt.Sprintf("goto %4v on %v", tran.State, nonTermName(tran.Symbol)) - }, - "printSRConflict": func(sr spec.SRConflict) string { - var adopted string - switch { - case sr.AdoptedState != nil: - adopted = fmt.Sprintf("shift %v", *sr.AdoptedState) - case sr.AdoptedProduction != nil: - adopted = fmt.Sprintf("reduce %v", *sr.AdoptedProduction) - } - return fmt.Sprintf("shift/reduce conflict (shift %v, reduce %v) on %v: %v adopted", sr.State, sr.Production, termName(sr.Symbol), adopted) - }, - "printRRConflict": func(rr spec.RRConflict) string { - return fmt.Sprintf("reduce/reduce conflict (%v, %v) on %v: reduce %v adopted", rr.Production1, rr.Production2, termName(rr.Symbol), rr.AdoptedProduction) - }, - } - - tmpl, err := template.New("").Funcs(fns).Parse(descTemplate) - if err != nil { - return err - } - - err = tmpl.Execute(w, desc) - if err != nil { - return err - } - - return nil -} diff --git a/cmd/vartan/show.go b/cmd/vartan/show.go new file mode 100644 index 0000000..920ba66 --- /dev/null +++ b/cmd/vartan/show.go @@ -0,0 +1,273 @@ +package main + +import ( + "encoding/json" + "fmt" + "io" + "io/ioutil" + "os" + "runtime/debug" + "strings" + "text/template" + + "github.com/nihei9/vartan/spec" + "github.com/spf13/cobra" +) + +var showFlags = struct { +}{} + +func init() { + cmd := &cobra.Command{ + Use: "show", + Short: "Print a description file in readable format", + Example: ` vartan show grammar-description.json`, + Args: cobra.ExactArgs(1), + RunE: runShow, + } + rootCmd.AddCommand(cmd) +} + +func runShow(cmd *cobra.Command, args []string) (retErr error) { + defer func() { + panicked := false + v := recover() + if v != nil { + err, ok := v.(error) + if !ok { + retErr = fmt.Errorf("an unexpected error occurred: %v", v) + fmt.Fprintf(os.Stderr, "%v:\n%v", retErr, string(debug.Stack())) + return + } + + retErr = err + panicked = true + } + + if retErr != nil { + if panicked { + fmt.Fprintf(os.Stderr, "%v:\n%v", retErr, string(debug.Stack())) + } else { + fmt.Fprintf(os.Stderr, "%v\n", retErr) + } + } + }() + + desc, err := readDescription(args[0]) + if err != nil { + return err + } + + err = writeDescription(os.Stdout, desc) + if err != nil { + return err + } + + return nil +} + +func readDescription(path string) (*spec.Description, error) { + f, err := os.Open(path) + if err != nil { + return nil, fmt.Errorf("Cannot open the description file %s: %w", path, err) + } + defer f.Close() + + d, err := ioutil.ReadAll(f) + if err != nil { + return nil, err + } + + desc := &spec.Description{} + err = json.Unmarshal(d, desc) + if err != nil { + return nil, err + } + + return desc, nil +} + +const descTemplate = `# Class + +{{ .Class }} + +# Conflicts + +{{ printConflictSummary . }} + +# Terminals + +{{ range slice .Terminals 1 -}} +{{ printTerminal . }} +{{ end }} +# Productions + +{{ range slice .Productions 1 -}} +{{ printProduction . }} +{{ end }} +# States +{{ range .States }} +## State {{ .Number }} + +{{ range .Kernel -}} +{{ printItem . }} +{{ end }} +{{ range .Shift -}} +{{ printShift . }} +{{ end -}} +{{ range .Reduce -}} +{{ printReduce . }} +{{ end -}} +{{ range .GoTo -}} +{{ printGoTo . }} +{{ end }} +{{ range .SRConflict -}} +{{ printSRConflict . }} +{{ end -}} +{{ range .RRConflict -}} +{{ printRRConflict . }} +{{ end -}} +{{ end }}` + +func writeDescription(w io.Writer, desc *spec.Description) error { + termName := func(sym int) string { + if desc.Terminals[sym].Alias != "" { + return desc.Terminals[sym].Alias + } + return desc.Terminals[sym].Name + } + + nonTermName := func(sym int) string { + return desc.NonTerminals[sym].Name + } + + fns := template.FuncMap{ + "printConflictSummary": func(desc *spec.Description) string { + count := 0 + for _, s := range desc.States { + count += len(s.SRConflict) + count += len(s.RRConflict) + } + + if count == 1 { + return fmt.Sprintf("1 conflict was detected.") + } else if count > 1 { + return fmt.Sprintf("%v conflicts were detected.", count) + } + return "No conflict was detected." + }, + "printTerminal": func(term spec.Terminal) string { + var prec string + if term.Precedence != 0 { + prec = fmt.Sprintf("%2v", term.Precedence) + } else { + prec = " -" + } + + var assoc string + if term.Associativity != "" { + assoc = term.Associativity + } else { + assoc = "-" + } + + if term.Alias != "" { + return fmt.Sprintf("%4v %v %v %v (%v)", term.Number, prec, assoc, term.Name, term.Alias) + } + return fmt.Sprintf("%4v %v %v %v", term.Number, prec, assoc, term.Name) + }, + "printProduction": func(prod spec.Production) string { + var prec string + if prod.Precedence != 0 { + prec = fmt.Sprintf("%2v", prod.Precedence) + } else { + prec = " -" + } + + var assoc string + if prod.Associativity != "" { + assoc = prod.Associativity + } else { + assoc = "-" + } + + var b strings.Builder + fmt.Fprintf(&b, "%v →", nonTermName(prod.LHS)) + if len(prod.RHS) > 0 { + for _, e := range prod.RHS { + if e > 0 { + fmt.Fprintf(&b, " %v", termName(e)) + } else { + fmt.Fprintf(&b, " %v", nonTermName(e*-1)) + } + } + } else { + fmt.Fprintf(&b, " ε") + } + + return fmt.Sprintf("%4v %v %v %v", prod.Number, prec, assoc, b.String()) + }, + "printItem": func(item spec.Item) string { + prod := desc.Productions[item.Production] + + var b strings.Builder + fmt.Fprintf(&b, "%v →", nonTermName(prod.LHS)) + for i, e := range prod.RHS { + if i == item.Dot { + fmt.Fprintf(&b, " ・") + } + if e > 0 { + fmt.Fprintf(&b, " %v", termName(e)) + } else { + fmt.Fprintf(&b, " %v", nonTermName(e*-1)) + } + } + if item.Dot >= len(prod.RHS) { + fmt.Fprintf(&b, " ・") + } + + return fmt.Sprintf("%4v %v", prod.Number, b.String()) + }, + "printShift": func(tran spec.Transition) string { + return fmt.Sprintf("shift %4v on %v", tran.State, termName(tran.Symbol)) + }, + "printReduce": func(reduce spec.Reduce) string { + var b strings.Builder + { + fmt.Fprintf(&b, "%v", termName(reduce.LookAhead[0])) + for _, a := range reduce.LookAhead[1:] { + fmt.Fprintf(&b, ", %v", termName(a)) + } + } + return fmt.Sprintf("reduce %4v on %v", reduce.Production, b.String()) + }, + "printGoTo": func(tran spec.Transition) string { + return fmt.Sprintf("goto %4v on %v", tran.State, nonTermName(tran.Symbol)) + }, + "printSRConflict": func(sr spec.SRConflict) string { + var adopted string + switch { + case sr.AdoptedState != nil: + adopted = fmt.Sprintf("shift %v", *sr.AdoptedState) + case sr.AdoptedProduction != nil: + adopted = fmt.Sprintf("reduce %v", *sr.AdoptedProduction) + } + return fmt.Sprintf("shift/reduce conflict (shift %v, reduce %v) on %v: %v adopted", sr.State, sr.Production, termName(sr.Symbol), adopted) + }, + "printRRConflict": func(rr spec.RRConflict) string { + return fmt.Sprintf("reduce/reduce conflict (%v, %v) on %v: reduce %v adopted", rr.Production1, rr.Production2, termName(rr.Symbol), rr.AdoptedProduction) + }, + } + + tmpl, err := template.New("").Funcs(fns).Parse(descTemplate) + if err != nil { + return err + } + + err = tmpl.Execute(w, desc) + if err != nil { + return err + } + + return nil +} -- cgit v1.2.3