acme/compiler.go

172 lines
3.5 KiB
Go

// lexer.go
package main
import (
"fmt"
"io/ioutil"
"os"
"os/exec"
)
func assemble(asm ASM, source, executable string) (err error) {
var srcFile *os.File
var e error
if source == "" {
srcFile, e = ioutil.TempFile("", "")
if e != nil {
err = fmt.Errorf("Creating temporary srcFile failed - %w", e)
return
}
defer os.Remove(srcFile.Name())
} else {
srcFile, e = os.Create(source)
if e != nil {
err = fmt.Errorf("Creating srcFile failed - %w", e)
return
}
}
objectFile, e := ioutil.TempFile("", "")
if e != nil {
err = fmt.Errorf("Creating temporary objectFile failed - %w", e)
return
}
objectFile.Close()
// Write assembly into tmp source file
defer os.Remove(objectFile.Name())
for _, v := range asm.header {
fmt.Fprintf(srcFile, "%v\n", v)
}
for k, v := range asm.constants {
fmt.Fprintf(srcFile, "%-12v%-10v%-15v\n", v, "equ", k)
}
for k, v := range asm.sysConstants {
fmt.Fprintf(srcFile, "%-12v%-10v%-15v\n", v, "equ", k)
}
for _, v := range asm.variables {
fmt.Fprintf(srcFile, "%-12v%-10v%-15v\n", v[0], v[1], v[2])
}
for _, v := range asm.sectionText {
fmt.Fprintf(srcFile, "%v\n", v)
}
for k, f := range asm.functions {
if !f.inline && f.used {
fmt.Fprintf(srcFile, "global %v\n", k)
fmt.Fprintf(srcFile, "%v:\n", k)
for _, v := range f.code {
fmt.Fprintf(srcFile, "%v%-10v%-10v\n", v[0], v[1], v[2])
}
}
}
for _, v := range asm.program {
fmt.Fprintf(srcFile, "%v%-10v%-10v\n", v[0], v[1], v[2])
}
srcFile.Close()
// Find yasm
yasm, e := exec.LookPath("yasm")
if e != nil {
err = fmt.Errorf("'yasm' not found. Please install - %w", e)
return
}
// Assemble
yasmCmd := &exec.Cmd{
Path: yasm,
Args: []string{yasm, "-f", "elf64", srcFile.Name(), "-o", objectFile.Name()},
Stdout: os.Stdout,
Stderr: os.Stderr,
}
if e := yasmCmd.Run(); e != nil {
err = fmt.Errorf("Error while assembling the source code - %w", e)
return
}
// Find ld
ld, e := exec.LookPath("ld")
if e != nil {
err = fmt.Errorf("'ld' not found. Please install - %w", e)
return
}
// Link
ldCmd := &exec.Cmd{
Path: ld,
Args: []string{ld, "-o", executable, objectFile.Name()},
Stdout: os.Stdout,
Stderr: os.Stderr,
}
if e := ldCmd.Run(); e != nil {
err = fmt.Errorf("Error while linking object file - %w", e)
return
}
return
}
func compile(program []byte, sourceFile, binFile string) bool {
tokenChan := make(chan Token, 1)
lexerErr := make(chan error, 1)
go tokenize(program, tokenChan, lexerErr)
ast, parseErr := parse(tokenChan)
// check error channel on incoming errors
// As we lex and parse simultaneously, there is most likely a parser error as well. But that should be ignored
// as long as we have token errors before!
select {
case e := <-lexerErr:
fmt.Println(e)
return false
default:
}
if parseErr != nil {
fmt.Println(parseErr)
return false
}
ast, semanticErr := semanticAnalysis(ast)
if semanticErr != nil {
fmt.Println(semanticErr)
return false
}
asm := ast.generateCode()
if asmErr := assemble(asm, sourceFile, binFile); asmErr != nil {
fmt.Println(asmErr)
return false
}
return true
}
func main() {
var program []byte = []byte(`
for i, e : [1,2,3,4,5,6,7,8,9,10] {
if e == 3 {
continue
}
if i >= 6 {
break
}
println(e)
}
`)
if len(os.Args) > 1 {
var err error
program, err = ioutil.ReadFile(os.Args[1])
if err != nil {
fmt.Println(err)
os.Exit(1)
}
}
if !compile(program, "source.asm", "executable") {
os.Exit(1)
}
}