Cli now handles printing stuff
This commit is contained in:
parent
39f62a3d3f
commit
4987ed0b4d
88
cli/cli.go
88
cli/cli.go
|
@ -1,6 +1,7 @@
|
||||||
package cli
|
package cli
|
||||||
|
|
||||||
import "os"
|
import "os"
|
||||||
|
import "io"
|
||||||
import "fmt"
|
import "fmt"
|
||||||
import "sort"
|
import "sort"
|
||||||
import "errors"
|
import "errors"
|
||||||
|
@ -11,6 +12,8 @@ import "strconv"
|
||||||
// possible to create multiple Cli's to have so many sub-commands in one
|
// possible to create multiple Cli's to have so many sub-commands in one
|
||||||
// program.
|
// program.
|
||||||
type Cli struct {
|
type Cli struct {
|
||||||
|
Logger
|
||||||
|
|
||||||
// Description describes the application.
|
// Description describes the application.
|
||||||
Description string
|
Description string
|
||||||
|
|
||||||
|
@ -49,12 +52,13 @@ func New (description string, flags ...*Flag) *Cli {
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddSub adds a sub-command to this command. It automatically fills out the
|
// AddSub adds a sub-command to this command. It automatically fills out the
|
||||||
// sub-command's Super field.
|
// sub-command's Super field, and alters the prefix of its Logger to match.
|
||||||
func (this *Cli) AddSub (name string, sub *Cli) {
|
func (this *Cli) AddSub (name string, sub *Cli) {
|
||||||
super := this.Super
|
super := this.Super
|
||||||
if super != "" { super += " " }
|
if super != "" { super += " " }
|
||||||
super += name
|
super += name
|
||||||
sub.Super = super
|
sub.Super = super
|
||||||
|
sub.Prefix = os.Args[0] + " " + super
|
||||||
this.Sub[name] = sub
|
this.Sub[name] = sub
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,7 +71,7 @@ func (this *Cli) AddSub (name string, sub *Cli) {
|
||||||
// command that did the parsing.
|
// command that did the parsing.
|
||||||
func (this *Cli) Parse (args []string) (*Cli, error) {
|
func (this *Cli) Parse (args []string) (*Cli, error) {
|
||||||
// try to find a sub-command
|
// try to find a sub-command
|
||||||
if this.Sub != nil && len(args) > 0 {
|
if this.Sub != nil && len(args) > 1 {
|
||||||
if sub, ok := this.Sub[args[1]]; ok {
|
if sub, ok := this.Sub[args[1]]; ok {
|
||||||
return sub.Parse(args[1:])
|
return sub.Parse(args[1:])
|
||||||
}
|
}
|
||||||
|
@ -153,7 +157,7 @@ func (this *Cli) Parse (args []string) (*Cli, error) {
|
||||||
func (this *Cli) ParseOrExit (args []string) *Cli {
|
func (this *Cli) ParseOrExit (args []string) *Cli {
|
||||||
command, err := this.Parse(args)
|
command, err := this.Parse(args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "%s: %v\n\n", os.Args[0], err)
|
fmt.Fprintf(this, "%s: %v\n\n", os.Args[0], err)
|
||||||
this.Usage()
|
this.Usage()
|
||||||
os.Exit(2)
|
os.Exit(2)
|
||||||
}
|
}
|
||||||
|
@ -183,25 +187,25 @@ func (this *Cli) LongFlag (long string) (*Flag, bool) {
|
||||||
// Usage prints out usage/help information.
|
// Usage prints out usage/help information.
|
||||||
func (this *Cli) Usage () {
|
func (this *Cli) Usage () {
|
||||||
// syntax
|
// syntax
|
||||||
fmt.Fprint(os.Stderr, "Usage:")
|
fmt.Fprint(this, "Usage:")
|
||||||
fmt.Fprint(os.Stderr, " ", os.Args[0])
|
fmt.Fprint(this, " ", os.Args[0])
|
||||||
if this.Super != "" {
|
if this.Super != "" {
|
||||||
fmt.Fprint(os.Stderr, " ", this.Super)
|
fmt.Fprint(this, " ", this.Super)
|
||||||
}
|
}
|
||||||
hasSubCommands := this.Sub != nil && len(this.Sub) > 0
|
hasSubCommands := this.Sub != nil && len(this.Sub) > 0
|
||||||
if hasSubCommands {
|
if hasSubCommands {
|
||||||
fmt.Fprint(os.Stderr, " [COMMAND]")
|
fmt.Fprint(this, " [COMMAND]")
|
||||||
}
|
}
|
||||||
if this.Syntax == "" {
|
if this.Syntax == "" {
|
||||||
fmt.Fprint(os.Stderr, " [OPTION]...")
|
fmt.Fprint(this, " [OPTION]...")
|
||||||
} else {
|
} else {
|
||||||
fmt.Fprint(os.Stderr, " ", this.Syntax)
|
fmt.Fprint(this, " ", this.Syntax)
|
||||||
}
|
}
|
||||||
fmt.Fprint(os.Stderr, "\n\n")
|
fmt.Fprint(this, "\n\n")
|
||||||
|
|
||||||
// description
|
// description
|
||||||
if this.Description != "" {
|
if this.Description != "" {
|
||||||
fmt.Fprintf(os.Stderr, "%s\n\n", this.Description)
|
fmt.Fprintf(this, "%s\n\n", this.Description)
|
||||||
}
|
}
|
||||||
|
|
||||||
// fit the longest flag
|
// fit the longest flag
|
||||||
|
@ -228,12 +232,12 @@ func (this *Cli) Usage () {
|
||||||
shortLong += "--" + flag.Long
|
shortLong += "--" + flag.Long
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Fprintf(os.Stderr, format, shortLong, flag.Help)
|
fmt.Fprintf(this, format, shortLong, flag.Help)
|
||||||
}
|
}
|
||||||
|
|
||||||
// commands
|
// commands
|
||||||
if hasSubCommands {
|
if hasSubCommands {
|
||||||
fmt.Fprint(os.Stderr, "\nCommands:\n\n")
|
fmt.Fprint(this, "\nCommands:\n\n")
|
||||||
names := sortMapKeys(this.Sub)
|
names := sortMapKeys(this.Sub)
|
||||||
|
|
||||||
// fit the longest command
|
// fit the longest command
|
||||||
|
@ -246,11 +250,65 @@ func (this *Cli) Usage () {
|
||||||
format := fmt.Sprint("\t%-", longest + 2, "s%s\n")
|
format := fmt.Sprint("\t%-", longest + 2, "s%s\n")
|
||||||
|
|
||||||
for _, name := range names {
|
for _, name := range names {
|
||||||
fmt.Fprintf(os.Stderr, format, name, this.Sub[name].Description)
|
fmt.Fprintf(this, format, name, this.Sub[name].Description)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Logger prints messages to an output writer.
|
||||||
|
type Logger struct {
|
||||||
|
// Writer specifies the writer to output to. If it is nil, the Logger
|
||||||
|
// will output to os.Stderr. If you wish to silence the output, set it
|
||||||
|
// to io.Discard.
|
||||||
|
io.Writer
|
||||||
|
|
||||||
|
// Debug determines whether or not debug messages will be logged.
|
||||||
|
Debug bool
|
||||||
|
|
||||||
|
// Prefix is printed before all messages. If this is an empty string,
|
||||||
|
// os.Args[0] will be used.
|
||||||
|
Prefix string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Println logs a normal message.
|
||||||
|
func (this *Logger) Println (v ...any) {
|
||||||
|
fmt.Fprintf(this, "%s: %s", this.prefix(), fmt.Sprintln(v...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Errorln logs an error message.
|
||||||
|
func (this *Logger) Errorln (v ...any) {
|
||||||
|
fmt.Fprintf(this, "%s: %s", this.prefix(), fmt.Sprintln(v...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warnln logs a warning.
|
||||||
|
func (this *Logger) Warnln (v ...any) {
|
||||||
|
fmt.Fprintf(this, "%s: warning: %s", this.prefix(), fmt.Sprintln(v...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debugln logs debugging information. It will only print the message if the
|
||||||
|
// Debug field is set to true.
|
||||||
|
func (this *Logger) Debugln (v ...any) {
|
||||||
|
if !this.Debug { return }
|
||||||
|
fmt.Fprintf(this, "%s: debug: %s", this.prefix(), fmt.Sprintln(v...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Logger) prefix () string {
|
||||||
|
if this.Prefix == "" {
|
||||||
|
return os.Args[0]
|
||||||
|
} else {
|
||||||
|
return this.Prefix
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write writes to the Logger's writer. If it is nil, it writes to os.Stderr.
|
||||||
|
func (this *Logger) Write (data []byte) (n int, err error) {
|
||||||
|
if this.Writer == nil {
|
||||||
|
return os.Stderr.Write(data)
|
||||||
|
} else {
|
||||||
|
return this.Writer.Write(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Flag is a command line option.
|
// Flag is a command line option.
|
||||||
type Flag struct {
|
type Flag struct {
|
||||||
Short rune // The short form of the flag (-l)
|
Short rune // The short form of the flag (-l)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import "os"
|
import "os"
|
||||||
|
import "io"
|
||||||
import "path/filepath"
|
import "path/filepath"
|
||||||
import "git.tebibyte.media/fspl/fspl/cli"
|
import "git.tebibyte.media/fspl/fspl/cli"
|
||||||
import "git.tebibyte.media/fspl/fspl/entity"
|
import "git.tebibyte.media/fspl/fspl/entity"
|
||||||
|
@ -11,7 +12,7 @@ func main () {
|
||||||
// instantiate the compiler
|
// instantiate the compiler
|
||||||
// FIXME: perhaps we want different paths on Windows?
|
// FIXME: perhaps we want different paths on Windows?
|
||||||
comp := new(compiler.Compiler)
|
comp := new(compiler.Compiler)
|
||||||
comp.Stderr = os.Stderr
|
comp.Writer = os.Stderr
|
||||||
comp.Resolver = compiler.NewResolver (
|
comp.Resolver = compiler.NewResolver (
|
||||||
"/usr/local/src/fspl",
|
"/usr/local/src/fspl",
|
||||||
"/usr/src/fspl",
|
"/usr/src/fspl",
|
||||||
|
@ -68,7 +69,7 @@ func main () {
|
||||||
comp.Optimization = optimization.Value
|
comp.Optimization = optimization.Value
|
||||||
comp.Format = format.Value
|
comp.Format = format.Value
|
||||||
comp.Debug = debug.Value != ""
|
comp.Debug = debug.Value != ""
|
||||||
comp.Quiet = quiet.Value != ""
|
if quiet.Value != "" { comp.Writer = io.Discard }
|
||||||
|
|
||||||
err = comp.CompileUnit(entity.Address(application.Args[0]))
|
err = comp.CompileUnit(entity.Address(application.Args[0]))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package compiler
|
package compiler
|
||||||
|
|
||||||
import "io"
|
|
||||||
import "os"
|
import "os"
|
||||||
import "fmt"
|
import "fmt"
|
||||||
import "sort"
|
import "sort"
|
||||||
|
@ -8,6 +7,7 @@ import "errors"
|
||||||
import "os/exec"
|
import "os/exec"
|
||||||
import "path/filepath"
|
import "path/filepath"
|
||||||
import "github.com/google/uuid"
|
import "github.com/google/uuid"
|
||||||
|
import "git.tebibyte.media/fspl/fspl/cli"
|
||||||
import "git.tebibyte.media/fspl/fspl/llvm"
|
import "git.tebibyte.media/fspl/fspl/llvm"
|
||||||
import "git.tebibyte.media/fspl/fspl/lexer"
|
import "git.tebibyte.media/fspl/fspl/lexer"
|
||||||
import "git.tebibyte.media/fspl/fspl/entity"
|
import "git.tebibyte.media/fspl/fspl/entity"
|
||||||
|
@ -19,12 +19,11 @@ import "git.tebibyte.media/fspl/fspl/generator/native"
|
||||||
|
|
||||||
type Compiler struct {
|
type Compiler struct {
|
||||||
Resolver
|
Resolver
|
||||||
|
cli.Logger
|
||||||
|
|
||||||
Output string
|
Output string
|
||||||
Optimization string
|
Optimization string
|
||||||
Format string
|
Format string
|
||||||
Stderr io.Writer
|
|
||||||
Debug bool
|
|
||||||
Quiet bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *Compiler) CompileUnit (address entity.Address) error {
|
func (this *Compiler) CompileUnit (address entity.Address) error {
|
||||||
|
@ -353,25 +352,6 @@ func (this *Compiler) FindBackend (filetype string) (string, []string, error) {
|
||||||
"or 'clang' are accessable from your PATH")
|
"or 'clang' are accessable from your PATH")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *Compiler) Errorln (v ...any) {
|
|
||||||
if this.Quiet { return }
|
|
||||||
if this.Stderr == nil { return }
|
|
||||||
fmt.Fprintf(this.Stderr, "%s: %s", os.Args[0], fmt.Sprintln(v...))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *Compiler) Warnln (v ...any) {
|
|
||||||
if this.Quiet { return }
|
|
||||||
if this.Stderr == nil { return }
|
|
||||||
fmt.Fprintf(this.Stderr, "%s: warning: %s", os.Args[0], fmt.Sprintln(v...))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *Compiler) Debugln (v ...any) {
|
|
||||||
if this.Quiet { return }
|
|
||||||
if !this.Debug { return }
|
|
||||||
if this.Stderr == nil { return }
|
|
||||||
fmt.Fprintf(this.Stderr, "%s: debug: %s", os.Args[0], fmt.Sprintln(v...))
|
|
||||||
}
|
|
||||||
|
|
||||||
func sortMapKeys[T any] (unsorted map[string] T) []string {
|
func sortMapKeys[T any] (unsorted map[string] T) []string {
|
||||||
keys := make([]string, len(unsorted))
|
keys := make([]string, len(unsorted))
|
||||||
index := 0
|
index := 0
|
||||||
|
|
Loading…
Reference in New Issue