mirror of
https://github.com/RoBaertschi/tt.git
synced 2025-04-16 05:53:30 +00:00
begin term lib for build system
This commit is contained in:
parent
b175ee2bee
commit
dbaa77aa1b
@ -12,11 +12,17 @@ func _() {
|
|||||||
_ = x[Subtract-1]
|
_ = x[Subtract-1]
|
||||||
_ = x[Multiply-2]
|
_ = x[Multiply-2]
|
||||||
_ = x[Divide-3]
|
_ = x[Divide-3]
|
||||||
|
_ = x[Equal-4]
|
||||||
|
_ = x[NotEqual-5]
|
||||||
|
_ = x[LessThan-6]
|
||||||
|
_ = x[LessThanEqual-7]
|
||||||
|
_ = x[GreaterThan-8]
|
||||||
|
_ = x[GreaterThanEqual-9]
|
||||||
}
|
}
|
||||||
|
|
||||||
const _BinaryOperator_name = "AddSubtractMultiplyDivide"
|
const _BinaryOperator_name = "AddSubtractMultiplyDivideEqualNotEqualLessThanLessThanEqualGreaterThanGreaterThanEqual"
|
||||||
|
|
||||||
var _BinaryOperator_index = [...]uint8{0, 3, 11, 19, 25}
|
var _BinaryOperator_index = [...]uint8{0, 3, 11, 19, 25, 30, 38, 46, 59, 70, 86}
|
||||||
|
|
||||||
func (i BinaryOperator) String() string {
|
func (i BinaryOperator) String() string {
|
||||||
if i < 0 || i >= BinaryOperator(len(_BinaryOperator_index)-1) {
|
if i < 0 || i >= BinaryOperator(len(_BinaryOperator_index)-1) {
|
||||||
|
@ -3,13 +3,14 @@ package build
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"robaertschi.xyz/robaertschi/tt/asm"
|
"robaertschi.xyz/robaertschi/tt/asm"
|
||||||
|
"robaertschi.xyz/robaertschi/tt/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ToPrintFlags int
|
type ToPrintFlags int
|
||||||
@ -38,14 +39,14 @@ func NewSourceProgram(inputFile string, outputFile string) *SourceProgram {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (sp *SourceProgram) Build(backend asm.Backend, emitAsmOnly bool, toPrint ToPrintFlags) error {
|
func (sp *SourceProgram) Build(backend asm.Backend, emitAsmOnly bool, toPrint ToPrintFlags) error {
|
||||||
l := log.New(os.Stderr, "[build] ", log.Lshortfile)
|
l := utils.NewLogger(os.Stderr, "[build] ", utils.Info)
|
||||||
|
|
||||||
nodes := make(map[int]*node)
|
nodes := make(map[int]*node)
|
||||||
rootNodes := []int{}
|
rootNodes := []int{}
|
||||||
id := 0
|
id := 0
|
||||||
|
|
||||||
addRootNode := func(task task) int {
|
addRootNode := func(task task) int {
|
||||||
l.Printf("registering root task %d", id)
|
l.Debugf("registering root task %d", id)
|
||||||
node := &node{task: task}
|
node := &node{task: task}
|
||||||
nodes[id] = node
|
nodes[id] = node
|
||||||
rootNodes = append(rootNodes, id)
|
rootNodes = append(rootNodes, id)
|
||||||
@ -54,7 +55,7 @@ func (sp *SourceProgram) Build(backend asm.Backend, emitAsmOnly bool, toPrint To
|
|||||||
}
|
}
|
||||||
|
|
||||||
addNode := func(task task, deps ...int) int {
|
addNode := func(task task, deps ...int) int {
|
||||||
l.Printf("registering task %d", id)
|
l.Debugf("registering task %d", id)
|
||||||
if len(deps) <= 0 {
|
if len(deps) <= 0 {
|
||||||
panic("node without dep is useless")
|
panic("node without dep is useless")
|
||||||
}
|
}
|
||||||
@ -91,12 +92,14 @@ func (sp *SourceProgram) buildFasm(addRootNode func(task) int, addNode func(task
|
|||||||
|
|
||||||
mainAsmOutput := strings.TrimSuffix(sp.InputFile, filepath.Ext(sp.InputFile)) + ".asm"
|
mainAsmOutput := strings.TrimSuffix(sp.InputFile, filepath.Ext(sp.InputFile)) + ".asm"
|
||||||
|
|
||||||
asmFile := addRootNode(NewFuncTask(func() error {
|
asmFile := addRootNode(NewFuncTask("generating assembly for "+sp.InputFile, func(output io.Writer) error {
|
||||||
return build(sp.InputFile, mainAsmOutput, toPrint)
|
return build(output, sp.InputFile, mainAsmOutput, toPrint)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
if !emitAsmOnly {
|
if !emitAsmOnly {
|
||||||
fasmTask := addNode(NewProcessTask(fasmPath, mainAsmOutput, sp.OutputFile), asmFile)
|
task := NewProcessTask(fasmPath, mainAsmOutput, sp.OutputFile)
|
||||||
|
task.WithName("assembling " + mainAsmOutput)
|
||||||
|
fasmTask := addNode(task, asmFile)
|
||||||
|
|
||||||
// Cleanup
|
// Cleanup
|
||||||
|
|
||||||
|
@ -4,10 +4,10 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"slices"
|
"slices"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"robaertschi.xyz/robaertschi/tt/asm/amd64"
|
"robaertschi.xyz/robaertschi/tt/asm/amd64"
|
||||||
"robaertschi.xyz/robaertschi/tt/lexer"
|
"robaertschi.xyz/robaertschi/tt/lexer"
|
||||||
@ -19,27 +19,35 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type task interface {
|
type task interface {
|
||||||
Run(id int, doneChan chan taskResult)
|
Run(id int, output io.Writer, doneChan chan taskResult)
|
||||||
|
Name() string
|
||||||
|
WithName(string)
|
||||||
}
|
}
|
||||||
|
|
||||||
type processTask struct {
|
type processTask struct {
|
||||||
name string
|
taskName string
|
||||||
args []string
|
name string
|
||||||
|
args []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewProcessTask(name string, args ...string) task {
|
func NewProcessTask(name string, args ...string) task {
|
||||||
return &processTask{
|
return &processTask{
|
||||||
name: name,
|
taskName: name,
|
||||||
args: args,
|
name: name,
|
||||||
|
args: args,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pt *processTask) Run(id int, doneChan chan taskResult) {
|
func (pt *processTask) WithName(name string) {
|
||||||
|
pt.taskName = name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pt *processTask) Run(id int, output io.Writer, doneChan chan taskResult) {
|
||||||
cmd := exec.Command(pt.name, pt.args...)
|
cmd := exec.Command(pt.name, pt.args...)
|
||||||
cmd.Stdout = utils.NewPrefixWriterString(os.Stdout, pt.name+" output: ")
|
cmd.Stdout = utils.NewPrefixWriterString(output, pt.name+" output: ")
|
||||||
cmd.Stderr = cmd.Stdout
|
cmd.Stderr = cmd.Stdout
|
||||||
|
|
||||||
fmt.Printf("starting %q %v\n", pt.name, pt.args)
|
io.WriteString(output, fmt.Sprintf("starting %q %v\n", pt.name, pt.args))
|
||||||
err := cmd.Run()
|
err := cmd.Run()
|
||||||
var exitError error
|
var exitError error
|
||||||
if cmd.ProcessState.ExitCode() != 0 {
|
if cmd.ProcessState.ExitCode() != 0 {
|
||||||
@ -51,15 +59,20 @@ func (pt *processTask) Run(id int, doneChan chan taskResult) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (pt *processTask) Name() string {
|
||||||
|
return pt.taskName
|
||||||
|
}
|
||||||
|
|
||||||
type removeFileTask struct {
|
type removeFileTask struct {
|
||||||
file string
|
file string
|
||||||
|
name string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRemoveFileTask(file string) task {
|
func NewRemoveFileTask(file string) task {
|
||||||
return &removeFileTask{file: file}
|
return &removeFileTask{file: file, name: fmt.Sprintf("removing file %q", file)}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rft *removeFileTask) Run(id int, doneChan chan taskResult) {
|
func (rft *removeFileTask) Run(id int, output io.Writer, doneChan chan taskResult) {
|
||||||
err := os.Remove(rft.file)
|
err := os.Remove(rft.file)
|
||||||
doneChan <- taskResult{
|
doneChan <- taskResult{
|
||||||
Id: id,
|
Id: id,
|
||||||
@ -67,22 +80,35 @@ func (rft *removeFileTask) Run(id int, doneChan chan taskResult) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (rft *removeFileTask) Name() string { return rft.name }
|
||||||
|
|
||||||
|
func (rft *removeFileTask) WithName(name string) { rft.name = name }
|
||||||
|
|
||||||
type funcTask struct {
|
type funcTask struct {
|
||||||
f func() error
|
f func(io.Writer) error
|
||||||
|
name string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFuncTask(f func() error) task {
|
func NewFuncTask(taskName string, f func(io.Writer) error) task {
|
||||||
return &funcTask{f: f}
|
return &funcTask{f: f, name: taskName}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rft *funcTask) Run(id int, doneChan chan taskResult) {
|
func (rft *funcTask) Run(id int, output io.Writer, doneChan chan taskResult) {
|
||||||
doneChan <- taskResult{
|
doneChan <- taskResult{
|
||||||
Id: id,
|
Id: id,
|
||||||
Err: rft.f(),
|
Err: rft.f(output),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func build(input string, output string, toPrint ToPrintFlags) error {
|
func (rft *funcTask) Name() string {
|
||||||
|
return rft.name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rft *funcTask) WithName(name string) {
|
||||||
|
rft.name = name
|
||||||
|
}
|
||||||
|
|
||||||
|
func build(outputWriter io.Writer, input string, output string, toPrint ToPrintFlags) error {
|
||||||
file, err := os.Open(input)
|
file, err := os.Open(input)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("could not open file %q because: %v", input, err)
|
return fmt.Errorf("could not open file %q because: %v", input, err)
|
||||||
@ -114,7 +140,8 @@ func build(input string, output string, toPrint ToPrintFlags) error {
|
|||||||
return fmt.Errorf("parser encountered 1 or more errors")
|
return fmt.Errorf("parser encountered 1 or more errors")
|
||||||
}
|
}
|
||||||
if (toPrint & PrintAst) != 0 {
|
if (toPrint & PrintAst) != 0 {
|
||||||
fmt.Printf("AST:\n%s\n%+#v\n", program.String(), program)
|
io.WriteString(outputWriter,
|
||||||
|
fmt.Sprintf("AST:\n%s\n%+#v\n", program.String(), program))
|
||||||
}
|
}
|
||||||
|
|
||||||
tprogram, err := typechecker.New().CheckProgram(program)
|
tprogram, err := typechecker.New().CheckProgram(program)
|
||||||
@ -122,12 +149,14 @@ func build(input string, output string, toPrint ToPrintFlags) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if (toPrint & PrintTAst) != 0 {
|
if (toPrint & PrintTAst) != 0 {
|
||||||
fmt.Printf("TAST:\n%s\n%+#v\n", tprogram.String(), tprogram)
|
io.WriteString(outputWriter,
|
||||||
|
fmt.Sprintf("TAST:\n%s\n%+#v\n", tprogram.String(), tprogram))
|
||||||
}
|
}
|
||||||
|
|
||||||
ir := ttir.EmitProgram(tprogram)
|
ir := ttir.EmitProgram(tprogram)
|
||||||
if (toPrint & PrintIr) != 0 {
|
if (toPrint & PrintIr) != 0 {
|
||||||
fmt.Printf("TTIR:\n%s\n%+#v\n", ir.String(), ir)
|
io.WriteString(outputWriter,
|
||||||
|
fmt.Sprintf("TTIR:\n%s\n%+#v\n", ir.String(), ir))
|
||||||
}
|
}
|
||||||
asm := amd64.CgProgram(ir)
|
asm := amd64.CgProgram(ir)
|
||||||
|
|
||||||
@ -167,9 +196,10 @@ const (
|
|||||||
failed
|
failed
|
||||||
)
|
)
|
||||||
|
|
||||||
func runTasks(nodes map[int]*node, rootNodes []int, l *log.Logger) error {
|
func runTasks(nodes map[int]*node, rootNodes []int, l *utils.Logger) error {
|
||||||
|
|
||||||
done := make(map[int]executionState)
|
done := make(map[int]executionState)
|
||||||
|
output := make(map[int]*strings.Builder)
|
||||||
running := []int{}
|
running := []int{}
|
||||||
doneChan := make(chan taskResult)
|
doneChan := make(chan taskResult)
|
||||||
errs := []error{}
|
errs := []error{}
|
||||||
@ -178,9 +208,10 @@ func runTasks(nodes map[int]*node, rootNodes []int, l *log.Logger) error {
|
|||||||
if done[id] != notStarted {
|
if done[id] != notStarted {
|
||||||
panic(fmt.Sprintf("tried starting task %d twice", id))
|
panic(fmt.Sprintf("tried starting task %d twice", id))
|
||||||
}
|
}
|
||||||
// fmt.Printf("executing task %d\n", id)
|
l.Debugf("executing task %d", id)
|
||||||
node := nodes[id]
|
node := nodes[id]
|
||||||
go node.task.Run(id, doneChan)
|
output[id] = &strings.Builder{}
|
||||||
|
go node.task.Run(id, output[id], doneChan)
|
||||||
running = append(running, id)
|
running = append(running, id)
|
||||||
done[id] = executing
|
done[id] = executing
|
||||||
}
|
}
|
||||||
@ -206,7 +237,7 @@ func runTasks(nodes map[int]*node, rootNodes []int, l *log.Logger) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// fmt.Printf("starting rootNodes %v\n", rootNodes)
|
l.Debugf("starting rootNodes %v", rootNodes)
|
||||||
for _, rootNode := range rootNodes {
|
for _, rootNode := range rootNodes {
|
||||||
startTask(rootNode)
|
startTask(rootNode)
|
||||||
}
|
}
|
||||||
@ -216,7 +247,7 @@ func runTasks(nodes map[int]*node, rootNodes []int, l *log.Logger) error {
|
|||||||
for !allFinished {
|
for !allFinished {
|
||||||
select {
|
select {
|
||||||
case result := <-doneChan:
|
case result := <-doneChan:
|
||||||
// fmt.Printf("task %d is done with err: %v\n", result.Id, result.Err)
|
l.Debugf("task %d is done with err: %v", result.Id, result.Err)
|
||||||
for i, id := range running {
|
for i, id := range running {
|
||||||
if id == result.Id {
|
if id == result.Id {
|
||||||
running = slices.Delete(running, i, i+1)
|
running = slices.Delete(running, i, i+1)
|
||||||
@ -252,6 +283,12 @@ func runTasks(nodes map[int]*node, rootNodes []int, l *log.Logger) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for id, node := range nodes {
|
||||||
|
if output[id].Len() > 0 {
|
||||||
|
l.Infof("task %q output: %s", node.task.Name(), output[id])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return errors.Join(errs...)
|
return errors.Join(errs...)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
6
main.go
6
main.go
@ -10,9 +10,13 @@ import (
|
|||||||
|
|
||||||
"robaertschi.xyz/robaertschi/tt/asm"
|
"robaertschi.xyz/robaertschi/tt/asm"
|
||||||
"robaertschi.xyz/robaertschi/tt/build"
|
"robaertschi.xyz/robaertschi/tt/build"
|
||||||
|
"robaertschi.xyz/robaertschi/tt/term"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
r, c, err := term.GetCursorPosition()
|
||||||
|
fmt.Printf("%d, %d, %v\n", r, c, err)
|
||||||
|
|
||||||
flag.Usage = func() {
|
flag.Usage = func() {
|
||||||
fmt.Fprintf(flag.CommandLine.Output(), "Usage of %s [flags] input\nPossible flags:\n", os.Args[0])
|
fmt.Fprintf(flag.CommandLine.Output(), "Usage of %s [flags] input\nPossible flags:\n", os.Args[0])
|
||||||
flag.PrintDefaults()
|
flag.PrintDefaults()
|
||||||
@ -51,7 +55,7 @@ func main() {
|
|||||||
|
|
||||||
logger := log.New(os.Stderr, "", log.Lshortfile)
|
logger := log.New(os.Stderr, "", log.Lshortfile)
|
||||||
|
|
||||||
err := build.NewSourceProgram(input, output).Build(asm.Fasm, *emitAsmOnly, build.ToPrintFlags(toPrint))
|
err = build.NewSourceProgram(input, output).Build(asm.Fasm, *emitAsmOnly, build.ToPrintFlags(toPrint))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Fatalln(err)
|
logger.Fatalln(err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
186
term/term.go
Normal file
186
term/term.go
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
package term
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"internal/syscall/unix"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
const ESC = "\x1b"
|
||||||
|
const CSI = ESC + "["
|
||||||
|
const Reset = CSI + "0m"
|
||||||
|
|
||||||
|
// Colors
|
||||||
|
|
||||||
|
const (
|
||||||
|
BlackFg = "30"
|
||||||
|
BlackBg = "40"
|
||||||
|
RedFg = "31"
|
||||||
|
RedBg = "41"
|
||||||
|
GreenFg = "32"
|
||||||
|
GreenBg = "42"
|
||||||
|
YellowFg = "33"
|
||||||
|
YellowBg = "43"
|
||||||
|
BlueFg = "34"
|
||||||
|
BlueBg = "44"
|
||||||
|
MagentaFg = "35"
|
||||||
|
MagentaBg = "35"
|
||||||
|
CyanFg = "36"
|
||||||
|
CyanBg = "46"
|
||||||
|
WhiteFg = "37"
|
||||||
|
WhiteBg = "47"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Color(col string) string {
|
||||||
|
return CSI + col + "m"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Other CSI
|
||||||
|
|
||||||
|
func CursorUp(amount int) string {
|
||||||
|
return fmt.Sprintf("%s%dA", CSI, amount)
|
||||||
|
}
|
||||||
|
|
||||||
|
func CursorDown(amount int) string {
|
||||||
|
return fmt.Sprintf("%s%dB", CSI, amount)
|
||||||
|
}
|
||||||
|
|
||||||
|
func CursorForward(amount int) string {
|
||||||
|
return fmt.Sprintf("%s%dC", CSI, amount)
|
||||||
|
}
|
||||||
|
|
||||||
|
func CursorBack(amount int) string {
|
||||||
|
return fmt.Sprintf("%s%dD", CSI, amount)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not ANSI.SYS
|
||||||
|
func CursorNextLine(amount int) string {
|
||||||
|
return fmt.Sprintf("%s%dE", CSI, amount)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not ANSI.SYS
|
||||||
|
func CursorPreviousLine(amount int) string {
|
||||||
|
return fmt.Sprintf("%s%dF", CSI, amount)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not ANSI.SYS
|
||||||
|
// Moves cursor to column amount
|
||||||
|
func CursorHorizontalAbsolute(amount int) string {
|
||||||
|
return fmt.Sprintf("%s%dG", CSI, amount)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Moves the cursor on the 1-based grid in the terminal
|
||||||
|
func CursorPostition(x, y int) string {
|
||||||
|
return fmt.Sprintf("%s%d;%dH", CSI, x, y)
|
||||||
|
}
|
||||||
|
|
||||||
|
type EraseInDisplayMode int
|
||||||
|
|
||||||
|
const (
|
||||||
|
ClearFromCursor EraseInDisplayMode = iota // clear from cursor to the end of the screen
|
||||||
|
ClearToCursor // clear to cursor from the begining of the screen
|
||||||
|
ClearEntireScreen
|
||||||
|
ClearEntireScreenAndScrollbackBuffer // xterm extension
|
||||||
|
)
|
||||||
|
|
||||||
|
func EraseInDisplay(mode EraseInDisplayMode) string {
|
||||||
|
return fmt.Sprintf("%s%dJ", CSI, mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
type EraseInLineMode int
|
||||||
|
|
||||||
|
const (
|
||||||
|
FromCursorToEnd EraseInLineMode = iota
|
||||||
|
FromCursorToBegin
|
||||||
|
EntireLine
|
||||||
|
)
|
||||||
|
|
||||||
|
func EraseInLine(mode EraseInLineMode) string {
|
||||||
|
return fmt.Sprintf("%s%dK", CSI, mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not ANSI.SYS
|
||||||
|
func ScrollUp(amount int) string {
|
||||||
|
return fmt.Sprintf("%s%dS", CSI, amount)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not ANSI.SYS
|
||||||
|
func ScrollDown(amount int) string {
|
||||||
|
return fmt.Sprintf("%s%dT", CSI, amount)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Moves the cursor on the 1-based grid in the terminal
|
||||||
|
// Same as Cursor Position but a format effector, not a editor function
|
||||||
|
// see https://en.wikipedia.org/wiki/ANSI_escape_code#Control_Sequence_Introducer_commands
|
||||||
|
func HorizontalVerticalPosition(x, y int) string {
|
||||||
|
return fmt.Sprintf("%s%d;%dH", CSI, x, y)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gets the cursor position by transmitting CSIn;mR n = row, m = column
|
||||||
|
// see https://en.wikipedia.org/wiki/ANSI_escape_code#Control_Sequence_Introducer_commands
|
||||||
|
// Use GetCursorPosition to get x and y
|
||||||
|
func DeviceStatusReport() string {
|
||||||
|
return CSI + "6n"
|
||||||
|
}
|
||||||
|
|
||||||
|
var DidNotGetCsi = errors.New("could not get csi from Device Status Report sequence")
|
||||||
|
|
||||||
|
func GetCursorPosition() (row, column int, err error) {
|
||||||
|
// _, err = os.Stdin.Seek(0, 2)
|
||||||
|
// if err != nil {
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
os.Stdout.Write([]byte(DeviceStatusReport()))
|
||||||
|
// CSI is ESC and [
|
||||||
|
csiBuffer := [2]byte{}
|
||||||
|
os.Stdin.Read(csiBuffer[:])
|
||||||
|
|
||||||
|
if string(csiBuffer[:]) != CSI {
|
||||||
|
err = DidNotGetCsi
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
miniBuff := [1]byte{}
|
||||||
|
for {
|
||||||
|
_, err = os.Stdin.Read(miniBuff[:])
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if miniBuff[0] == ';' {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if '0' <= miniBuff[0] && miniBuff[0] <= '9' {
|
||||||
|
row *= 10
|
||||||
|
row += int(miniBuff[0] - '0')
|
||||||
|
} else {
|
||||||
|
err = fmt.Errorf("invalid byte for number %b", miniBuff[0])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
_, err = os.Stdin.Read(miniBuff[:])
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if miniBuff[0] == 'R' {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if '0' <= miniBuff[0] && miniBuff[0] <= '9' {
|
||||||
|
column *= 10
|
||||||
|
column += int(miniBuff[0] - '0')
|
||||||
|
} else {
|
||||||
|
err = fmt.Errorf("invalid byte for number %b", miniBuff[0])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
27
utils/level_string.go
Normal file
27
utils/level_string.go
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
// Code generated by "stringer -type=Level -linecomment"; DO NOT EDIT.
|
||||||
|
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||||
|
// Re-run the stringer command to generate them again.
|
||||||
|
var x [1]struct{}
|
||||||
|
_ = x[Debug-0]
|
||||||
|
_ = x[Info-1]
|
||||||
|
_ = x[Warn-2]
|
||||||
|
_ = x[Error-3]
|
||||||
|
_ = x[Fatal-4]
|
||||||
|
}
|
||||||
|
|
||||||
|
const _Level_name = "DEBUGINFOWARNERRORFATAL"
|
||||||
|
|
||||||
|
var _Level_index = [...]uint8{0, 5, 9, 13, 18, 23}
|
||||||
|
|
||||||
|
func (i Level) String() string {
|
||||||
|
if i < 0 || i >= Level(len(_Level_index)-1) {
|
||||||
|
return "Level(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||||
|
}
|
||||||
|
return _Level_name[_Level_index[i]:_Level_index[i+1]]
|
||||||
|
}
|
122
utils/utils.go
122
utils/utils.go
@ -2,7 +2,13 @@ package utils
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"robaertschi.xyz/robaertschi/tt/term"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Prefix writer writes a prefix before each new line from another io.Writer
|
// Prefix writer writes a prefix before each new line from another io.Writer
|
||||||
@ -53,3 +59,119 @@ func (w *PrefixWriter) Write(p []byte) (n int, err error) {
|
|||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:generate stringer -type=Level -linecomment
|
||||||
|
type Level int
|
||||||
|
|
||||||
|
const (
|
||||||
|
Debug Level = iota // DEBUG
|
||||||
|
Info // INFO
|
||||||
|
Warn // WARN
|
||||||
|
Error // ERROR
|
||||||
|
Fatal // FATAL
|
||||||
|
)
|
||||||
|
|
||||||
|
type LoggerFormatFunc func(prefix string, level Level, msg string) string
|
||||||
|
|
||||||
|
type Logger struct {
|
||||||
|
outMu sync.Mutex
|
||||||
|
out io.Writer
|
||||||
|
prefix string
|
||||||
|
format LoggerFormatFunc
|
||||||
|
filter Level
|
||||||
|
}
|
||||||
|
|
||||||
|
func DefaultLoggerFormatFunc(prefix string, level Level, msg string) string {
|
||||||
|
|
||||||
|
colorString := ""
|
||||||
|
switch level {
|
||||||
|
case Debug:
|
||||||
|
colorString = term.CSI + "90m"
|
||||||
|
case Info:
|
||||||
|
case Warn:
|
||||||
|
colorString = term.Color(term.YellowFg)
|
||||||
|
case Error:
|
||||||
|
colorString = term.Color(term.RedFg)
|
||||||
|
case Fatal:
|
||||||
|
colorString = term.CSI + term.WhiteFg + term.RedBg + "m"
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("%s%s[%s] %s%s", colorString, prefix, level, msg, term.Reset)
|
||||||
|
}
|
||||||
|
|
||||||
|
// filter filters anything below that level out, it does not stop a os.Exit() from a fatal
|
||||||
|
func NewLogger(output io.Writer, prefix string, filter Level) *Logger {
|
||||||
|
l := new(Logger)
|
||||||
|
l.SetPrefix(prefix)
|
||||||
|
l.SetOutput(output)
|
||||||
|
l.format = DefaultLoggerFormatFunc
|
||||||
|
l.filter = filter
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Logger) SetPrefix(prefix string) {
|
||||||
|
l.prefix = prefix
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Logger) SetOutput(output io.Writer) {
|
||||||
|
// NOTE(Robin): Do some research/testing if we need to look the mutex for this
|
||||||
|
l.out = output
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Logger) Msg(level Level, msg string) {
|
||||||
|
if level >= l.filter {
|
||||||
|
l.outMu.Lock()
|
||||||
|
result := l.format(l.prefix, level, strings.TrimRight(msg, " \n\t"))
|
||||||
|
io.WriteString(l.out, result)
|
||||||
|
io.WriteString(l.out, "\n")
|
||||||
|
l.outMu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
if level == Fatal {
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Logger) Msgf(level Level, msg string, args ...any) {
|
||||||
|
l.Msg(level, fmt.Sprintf(msg, args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Logger) Debug(msg string) {
|
||||||
|
l.Msg(Debug, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Logger) Debugf(msg string, args ...any) {
|
||||||
|
l.Msgf(Debug, msg, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Logger) Info(msg string) {
|
||||||
|
l.Msg(Info, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Logger) Infof(msg string, args ...any) {
|
||||||
|
l.Msgf(Info, msg, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Logger) Warn(msg string) {
|
||||||
|
l.Msg(Warn, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Logger) Warnf(msg string, args ...any) {
|
||||||
|
l.Msgf(Warn, msg, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Logger) Error(msg string) {
|
||||||
|
l.Msg(Error, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Logger) Errorf(msg string, args ...any) {
|
||||||
|
l.Msgf(Error, msg, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Logger) Fatal(msg string) {
|
||||||
|
l.Msg(Fatal, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Logger) Fatalf(msg string, args ...any) {
|
||||||
|
l.Msgf(Fatal, msg, args...)
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user