begin term lib for build system

This commit is contained in:
Robin Bärtschi 2025-01-28 14:29:08 +01:00
parent b175ee2bee
commit dbaa77aa1b
7 changed files with 420 additions and 35 deletions

View File

@ -12,11 +12,17 @@ func _() {
_ = x[Subtract-1]
_ = x[Multiply-2]
_ = 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 {
if i < 0 || i >= BinaryOperator(len(_BinaryOperator_index)-1) {

View File

@ -3,13 +3,14 @@ package build
import (
"fmt"
"log"
"io"
"os"
"os/exec"
"path/filepath"
"strings"
"robaertschi.xyz/robaertschi/tt/asm"
"robaertschi.xyz/robaertschi/tt/utils"
)
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 {
l := log.New(os.Stderr, "[build] ", log.Lshortfile)
l := utils.NewLogger(os.Stderr, "[build] ", utils.Info)
nodes := make(map[int]*node)
rootNodes := []int{}
id := 0
addRootNode := func(task task) int {
l.Printf("registering root task %d", id)
l.Debugf("registering root task %d", id)
node := &node{task: task}
nodes[id] = node
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 {
l.Printf("registering task %d", id)
l.Debugf("registering task %d", id)
if len(deps) <= 0 {
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"
asmFile := addRootNode(NewFuncTask(func() error {
return build(sp.InputFile, mainAsmOutput, toPrint)
asmFile := addRootNode(NewFuncTask("generating assembly for "+sp.InputFile, func(output io.Writer) error {
return build(output, sp.InputFile, mainAsmOutput, toPrint)
}))
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

View File

@ -4,10 +4,10 @@ import (
"errors"
"fmt"
"io"
"log"
"os"
"os/exec"
"slices"
"strings"
"robaertschi.xyz/robaertschi/tt/asm/amd64"
"robaertschi.xyz/robaertschi/tt/lexer"
@ -19,27 +19,35 @@ import (
)
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 {
taskName string
name string
args []string
}
func NewProcessTask(name string, args ...string) task {
return &processTask{
taskName: name,
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.Stdout = utils.NewPrefixWriterString(os.Stdout, pt.name+" output: ")
cmd.Stdout = utils.NewPrefixWriterString(output, pt.name+" output: ")
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()
var exitError error
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 {
file string
name string
}
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)
doneChan <- taskResult{
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 {
f func() error
f func(io.Writer) error
name string
}
func NewFuncTask(f func() error) task {
return &funcTask{f: f}
func NewFuncTask(taskName string, f func(io.Writer) error) task {
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{
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)
if err != nil {
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")
}
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)
@ -122,12 +149,14 @@ func build(input string, output string, toPrint ToPrintFlags) error {
return err
}
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)
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)
@ -167,9 +196,10 @@ const (
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)
output := make(map[int]*strings.Builder)
running := []int{}
doneChan := make(chan taskResult)
errs := []error{}
@ -178,9 +208,10 @@ func runTasks(nodes map[int]*node, rootNodes []int, l *log.Logger) error {
if done[id] != notStarted {
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]
go node.task.Run(id, doneChan)
output[id] = &strings.Builder{}
go node.task.Run(id, output[id], doneChan)
running = append(running, id)
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 {
startTask(rootNode)
}
@ -216,7 +247,7 @@ func runTasks(nodes map[int]*node, rootNodes []int, l *log.Logger) error {
for !allFinished {
select {
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 {
if id == result.Id {
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...)
}

View File

@ -10,9 +10,13 @@ import (
"robaertschi.xyz/robaertschi/tt/asm"
"robaertschi.xyz/robaertschi/tt/build"
"robaertschi.xyz/robaertschi/tt/term"
)
func main() {
r, c, err := term.GetCursorPosition()
fmt.Printf("%d, %d, %v\n", r, c, err)
flag.Usage = func() {
fmt.Fprintf(flag.CommandLine.Output(), "Usage of %s [flags] input\nPossible flags:\n", os.Args[0])
flag.PrintDefaults()
@ -51,7 +55,7 @@ func main() {
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 {
logger.Fatalln(err)
os.Exit(1)

186
term/term.go Normal file
View 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
View 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]]
}

View File

@ -2,7 +2,13 @@ package utils
import (
"bytes"
"fmt"
"io"
"os"
"strings"
"sync"
"robaertschi.xyz/robaertschi/tt/term"
)
// 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
}
//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...)
}