diff --git a/src/asm/Assembler.go b/src/asm/Assembler.go index 4ed7552..0fb1c49 100644 --- a/src/asm/Assembler.go +++ b/src/asm/Assembler.go @@ -62,6 +62,11 @@ func (a *Assembler) Finalize(verbose bool) ([]byte, []byte) { return code, data } +// Merge combines the contents of this assembler with another one. +func (a *Assembler) Merge(b *Assembler) { + a.Instructions = append(a.Instructions, b.Instructions...) +} + // MoveRegisterData moves a data section address into the given register. func (a *Assembler) MoveRegisterData(reg register.ID, data []byte) { a.Instructions = append(a.Instructions, Instruction{ diff --git a/src/build/Build.go b/src/build/Build.go index 80a0e66..a77bcf2 100644 --- a/src/build/Build.go +++ b/src/build/Build.go @@ -5,12 +5,8 @@ import ( "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. @@ -30,7 +26,17 @@ func New(directory string) *Build { // Run parses the input files and generates an executable file. func (build *Build) Run() error { - code, data, err := build.Compile() + stat, err := os.Stat(build.Directory) + + if err != nil { + return err + } + + if !stat.IsDir() { + return &errors.InvalidDirectory{Path: build.Directory} + } + + functions, err := build.Compile() if err != nil { return err @@ -40,60 +46,10 @@ func (build *Build) Run() error { return nil } + code, data := build.Finalize(functions) 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)) diff --git a/src/build/Compile.go b/src/build/Compile.go new file mode 100644 index 0000000..0fd5f9a --- /dev/null +++ b/src/build/Compile.go @@ -0,0 +1,40 @@ +package build + +import "sync" + +// Compile compiles all the functions. +func (build *Build) Compile() (map[string]*Function, error) { + functions, errors := Scan(build.Directory) + wg := sync.WaitGroup{} + allFunctions := map[string]*Function{} + + for functions != nil || errors != nil { + select { + case err, ok := <-errors: + if !ok { + errors = nil + continue + } + + return nil, err + + case function, ok := <-functions: + if !ok { + functions = nil + continue + } + + wg.Add(1) + + go func() { + defer wg.Done() + function.Compile() + }() + + allFunctions[function.Name] = function + } + } + + wg.Wait() + return allFunctions, nil +} diff --git a/src/build/Finalize.go b/src/build/Finalize.go new file mode 100644 index 0000000..0f7a247 --- /dev/null +++ b/src/build/Finalize.go @@ -0,0 +1,23 @@ +package build + +import ( + "git.akyoto.dev/cli/q/src/asm" + "git.akyoto.dev/cli/q/src/linux" + "git.akyoto.dev/cli/q/src/register" +) + +// Finalize generates the final machine code. +func (build *Build) Finalize(functions map[string]*Function) ([]byte, []byte) { + a := asm.New() + + for _, f := range functions { + a.Merge(&f.Assembler) + } + + a.MoveRegisterNumber(register.Syscall0, linux.Exit) + a.MoveRegisterNumber(register.Syscall1, 0) + a.Syscall() + + code, data := a.Finalize(build.Verbose) + return code, data +} diff --git a/src/build/Function.go b/src/build/Function.go index 8bb5108..902b2c5 100644 --- a/src/build/Function.go +++ b/src/build/Function.go @@ -1,7 +1,35 @@ package build -import "git.akyoto.dev/cli/q/src/token" +import ( + "git.akyoto.dev/cli/q/src/asm" + "git.akyoto.dev/cli/q/src/linux" + "git.akyoto.dev/cli/q/src/register" + "git.akyoto.dev/cli/q/src/token" +) +// Function represents a function. type Function struct { - Tokens token.List + Name string + Head token.List + Body token.List + Assembler asm.Assembler +} + +// Compile turns a function into machine code. +func (f *Function) Compile() { + for i, t := range f.Body { + if t.Kind == token.Identifier && t.String() == "print" { + message := f.Body[i+2].Bytes + f.Assembler.MoveRegisterNumber(register.Syscall0, linux.Write) + f.Assembler.MoveRegisterNumber(register.Syscall1, 1) + f.Assembler.MoveRegisterData(register.Syscall2, message) + f.Assembler.MoveRegisterNumber(register.Syscall3, uint64(len(message))) + f.Assembler.Syscall() + } + } +} + +// String returns the function name. +func (f *Function) String() string { + return f.Name } diff --git a/src/build/Scan.go b/src/build/Scan.go index d1cba26..f7b5c51 100644 --- a/src/build/Scan.go +++ b/src/build/Scan.go @@ -60,16 +60,17 @@ func scanFile(path string, functions chan<- *Function) error { tokens := token.Tokenize(contents) var ( - groupLevel = 0 - blockLevel = 0 - functionStart = -1 + groupLevel = 0 + blockLevel = 0 + headerStart = -1 + bodyStart = -1 ) for i, t := range tokens { switch t.Kind { case token.Identifier: if blockLevel == 0 && groupLevel == 0 { - functionStart = i + headerStart = i } case token.GroupStart: @@ -81,12 +82,18 @@ func scanFile(path string, functions chan<- *Function) error { case token.BlockStart: blockLevel++ + if blockLevel == 1 { + bodyStart = i + } + case token.BlockEnd: blockLevel-- if blockLevel == 0 { function := &Function{ - Tokens: tokens[functionStart : i+1], + Name: tokens[headerStart].String(), + Head: tokens[headerStart:bodyStart], + Body: tokens[bodyStart : i+1], } functions <- function