diff --git a/src/cli/Build.go b/src/cli/Build.go index 206ef7c..5f78ccd 100644 --- a/src/cli/Build.go +++ b/src/cli/Build.go @@ -42,8 +42,12 @@ func buildWithArgs(args []string) (*build.Build, error) { case "-d", "--dry": config.Dry = true + case "-s", "--statistics": + config.Statistics = true + case "-v", "--verbose": config.Assembler = true + config.Statistics = true case "--arch": i++ @@ -105,6 +109,10 @@ func makeExecutable(b *build.Build) error { result.PrintInstructions() } + if config.Statistics { + result.PrintStatistics() + } + if config.Dry { return nil } diff --git a/src/cli/Help.go b/src/cli/Help.go index 8d4f0ef..0543ad8 100644 --- a/src/cli/Help.go +++ b/src/cli/Help.go @@ -7,19 +7,22 @@ import ( // Help shows the command line argument usage. func Help(w io.Writer, code int) int { - fmt.Fprintln(w, `Usage: q [command] [options] + fmt.Fprintln(w, `Usage: - commands: + q [command] [options] - build [directory | file] - run [directory | file] - help - system +Commands: - build options: + build [directory | file] build an executable from a file or directory + --arch [arch] cross-compile for a different CPU architecture (x86, arm or riscv) + --assembler, -a show assembler instructions + --dry, -d skip writing the executable to disk + --os [os] cross-compile for a different OS (linux, mac or windows) + --statistics, -s show statistics + --verbose, -v show everything - --assembler, -a Show assembler instructions. - --comments, -c Show assembler comments. - --verbose, -v Show everything.`) + run [directory | file] build and run the executable + system show system information + help show this help`) return code } diff --git a/src/compiler/Compile.go b/src/compiler/Compile.go index 2fdea38..ca3ef28 100644 --- a/src/compiler/Compile.go +++ b/src/compiler/Compile.go @@ -105,6 +105,7 @@ func Compile(files <-chan *fs.File, functions <-chan *core.Function, structs <-c result.Main = main result.Functions = allFunctions + result.finalize() return result, nil } diff --git a/src/compiler/Result.go b/src/compiler/Result.go index 722ab99..c4d739f 100644 --- a/src/compiler/Result.go +++ b/src/compiler/Result.go @@ -2,6 +2,7 @@ package compiler import ( "bufio" + "fmt" "io" "os" @@ -13,6 +14,7 @@ import ( "git.akyoto.dev/cli/q/src/macho" "git.akyoto.dev/cli/q/src/pe" "git.akyoto.dev/cli/q/src/x64" + "git.akyoto.dev/go/color/ansi" ) // Result contains all the compiled functions in a build. @@ -21,10 +23,13 @@ type Result struct { Functions map[string]*core.Function InstructionCount int DataCount int + Code []byte + Data []byte + DLLs dll.List } // finalize generates the final machine code. -func (r *Result) finalize() ([]byte, []byte, dll.List) { +func (r *Result) finalize() { // This will be the entry point of the executable. // The only job of the entry function is to call `main` and exit cleanly. // The reason we call `main` instead of using `main` itself is to place @@ -50,7 +55,7 @@ func (r *Result) finalize() ([]byte, []byte, dll.List) { final.DLLCall("kernel32.ExitProcess") } - dlls := dll.List{ + r.DLLs = dll.List{ {Name: "kernel32", Functions: []string{"ExitProcess"}}, } @@ -61,7 +66,7 @@ func (r *Result) finalize() ([]byte, []byte, dll.List) { for _, library := range f.DLLs { for _, fn := range library.Functions { - dlls = dlls.Append(library.Name, fn) + r.DLLs = r.DLLs.Append(library.Name, fn) } } }) @@ -82,8 +87,7 @@ func (r *Result) finalize() ([]byte, []byte, dll.List) { final.DLLCall("kernel32.ExitProcess") } - code, data := final.Finalize(dlls) - return code, data, dlls + r.Code, r.Data = final.Finalize(r.DLLs) } // eachFunction recursively finds all the calls to external functions. @@ -119,10 +123,17 @@ func (r *Result) PrintInstructions() { }) } +// PrintStatistics shows the statistics. +func (r *Result) PrintStatistics() { + ansi.Dim.Println("╭──────────────────────────────────────────────────────────────────────────────╮") + ansi.Dim.Printf("│ %-44s%-32s │\n", "Code:", fmt.Sprintf("%d bytes", len(r.Code))) + ansi.Dim.Printf("│ %-44s%-32s │\n", "Data:", fmt.Sprintf("%d bytes", len(r.Data))) + ansi.Dim.Println("╰──────────────────────────────────────────────────────────────────────────────╯") +} + // Write writes the executable to the given writer. func (r *Result) Write(writer io.Writer) error { - code, data, dlls := r.finalize() - return write(writer, code, data, dlls) + return write(writer, r.Code, r.Data, r.DLLs) } // Write writes an executable file to disk. diff --git a/src/config/config.go b/src/config/config.go index b012e55..89644c8 100644 --- a/src/config/config.go +++ b/src/config/config.go @@ -14,9 +14,12 @@ const ( ) var ( - // Shows the assembly instructions at the end of the compilation. + // Shows the assembly instructions at the end. Assembler bool + // Shows statistics at the end. + Statistics bool + // Calculates the result of operations on constants at compile time. ConstantFold bool @@ -33,6 +36,7 @@ var ( // Reset resets the configuration to its default values. func Reset() { Assembler = false + Statistics = false ConstantFold = true Dry = false TargetArch = runtime.GOARCH diff --git a/src/core/PrintInstructions.go b/src/core/PrintInstructions.go index 6020cf7..d777ff0 100644 --- a/src/core/PrintInstructions.go +++ b/src/core/PrintInstructions.go @@ -12,6 +12,15 @@ import ( func (f *Function) PrintInstructions() { ansi.Dim.Println("╭──────────────────────────────────────────────────────────────────────────────╮") + if len(f.Input) > 0 { + for i, input := range f.Input { + ansi.Dim.Printf("│ %-44s%-32s", input.Name, f.CPU.Input[i]) + ansi.Dim.Print(" │\n") + } + + ansi.Dim.Println("├──────────────────────────────────────────────────────────────────────────────┤") + } + for i, x := range f.Assembler.Instructions { ansi.Dim.Print("│ ")