122 lines
2.3 KiB
Go
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)
|
|
}
|