q/src/build/Build.go
2023-10-30 16:17:41 +01:00

122 lines
2.3 KiB
Go

package build
import (
"bufio"
"os"
"path/filepath"
"git.akyoto.dev/cli/q/src/asm"
"git.akyoto.dev/cli/q/src/elf"
"git.akyoto.dev/cli/q/src/errors"
"git.akyoto.dev/cli/q/src/linux"
"git.akyoto.dev/cli/q/src/log"
"git.akyoto.dev/cli/q/src/register"
)
// Build describes a compiler build.
type Build struct {
Directory string
Verbose bool
WriteExecutable bool
}
// New creates a new build.
func New(directory string) *Build {
return &Build{
Directory: directory,
WriteExecutable: true,
}
}
// Run parses the input files and generates an executable file.
func (build *Build) Run() error {
code, data, err := build.Compile()
if err != nil {
return err
}
if !build.WriteExecutable {
return nil
}
return writeToDisk(build.Executable(), code, data)
}
// Compile compiles all the functions.
func (build *Build) Compile() ([]byte, []byte, error) {
stat, err := os.Stat(build.Directory)
if err != nil {
return nil, nil, err
}
if !stat.IsDir() {
return nil, nil, &errors.InvalidDirectory{Path: build.Directory}
}
functions, errors := Scan(build.Directory)
for functions != nil || errors != nil {
select {
case err, ok := <-errors:
if !ok {
errors = nil
continue
}
log.Info.Println(err)
case function, ok := <-functions:
if !ok {
functions = nil
continue
}
log.Info.Println(function)
}
}
a := asm.New()
hello := []byte("Hello\n")
a.MoveRegisterNumber(register.Syscall0, linux.Write)
a.MoveRegisterNumber(register.Syscall1, 1)
a.MoveRegisterData(register.Syscall2, hello)
a.MoveRegisterNumber(register.Syscall3, uint64(len(hello)))
a.Syscall()
a.MoveRegisterNumber(register.Syscall0, linux.Exit)
a.MoveRegisterNumber(register.Syscall1, 0)
a.Syscall()
code, data := a.Finalize(build.Verbose)
return code, data, nil
}
// Executable returns the path to the executable.
func (build *Build) Executable() string {
return filepath.Join(build.Directory, filepath.Base(build.Directory))
}
// writeToDisk writes the executable file to disk.
func writeToDisk(filePath string, code []byte, data []byte) error {
file, err := os.Create(filePath)
if err != nil {
return err
}
buffer := bufio.NewWriter(file)
executable := elf.New(code, data)
executable.Write(buffer)
buffer.Flush()
err = file.Close()
if err != nil {
return err
}
return os.Chmod(filePath, 0755)
}