Skip to content

Commit

Permalink
Get "Hello interpreter!" working.
Browse files Browse the repository at this point in the history
  • Loading branch information
Kestred committed Mar 13, 2016
1 parent 4598b95 commit 057ac94
Show file tree
Hide file tree
Showing 7 changed files with 75 additions and 29 deletions.
3 changes: 3 additions & 0 deletions code/ast/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ var (

// Relaxed types
InferredType = BaseTyp("<inferrable>") // could be any type
InferredText = BaseTyp("<text>") // could only be char/text/bytes/etc
InferredNumber = BaseTyp("<number>") // could only be a number
InferredFloat = BaseTyp("<float>") // could only be a float number
InferredSigned = BaseTyp("<signed>") // could only be a signed number
Expand All @@ -20,6 +21,8 @@ var (

// Builtin types
BuiltinEmpty = BaseTyp("empty") // the 0-byte type
BuiltinText = BaseTyp("text")
BuiltinChar = BaseTyp("char")
BuiltinFloat = BaseTyp("float")
BuiltinFloat32 = BaseTyp("f32")
BuiltinFloat64 = BaseTyp("f64")
Expand Down
14 changes: 13 additions & 1 deletion code/bytecode/bytecode.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ func (p *Program) NewProcedure() *Procedure {
type Assembly struct {
Source string
Wrapper unsafe.Pointer // for interpreter
Tempdir string

HasOutput bool
OutputBinding ast.AsmBinding
Expand Down Expand Up @@ -309,6 +310,17 @@ func (p *Procedure) Extend(node ast.Node) {
}
}

case *ast.TextLiteral:
utils.Assert(n.Value != ast.UnparsedValue, "An unparsed value survived until bytecode generation")
register := p.AssignRegister()

constant := p.Program.AddConstant(Data(unsafe.Pointer(&n.Value.([]byte)[0])))
p.Program.AddMetadata(n.Value)

instruction := Instruction{Op: LOAD_CONST, Out: register, Left: Constant(constant)}
p.Instructions = append(p.Instructions, instruction)
endRegister = register

case *ast.NumberLiteral:
utils.Assert(n.Value != ast.UnparsedValue, "An unparsed value survived until bytecode generation")
register := p.AssignRegister()
Expand Down Expand Up @@ -417,7 +429,7 @@ func (p *Procedure) Extend(node ast.Node) {
proc.Extend(n.Block)

default:
panic("TODO: Unhandle node type in bytecode.Generate")
panic("TODO: Unhandled node type in bytecode.Generate")
}

// set the result of this expression
Expand Down
44 changes: 26 additions & 18 deletions code/interpreter/assembler.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,14 +112,14 @@ func Assemble(asm *bytecode.Assembly) {
if err != nil {
panic(err) //return nil, err
}
//os.RemoveAll(tmpdir) // TODO: Clean up the temp directory later (not at function exit)
defer os.RemoveAll(tmpdir)

objpath := tmpdir + "/" + label + ".o"
cmd := exec.Command("/usr/bin/as", "-o", objpath)
cmd.Stdin = strings.NewReader(source)
// TODO: Collect stderr and return it if err is not nil
// cmd.Stdout = os.Stdout
// cmd.Stderr = os.Stderr
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Run()
if err != nil {
panic(err) //return nil, err
Expand All @@ -136,9 +136,6 @@ func Assemble(asm *bytecode.Assembly) {
}

loaded := C.LoadCode(C.CString(libpath), C.CString(label))
//
fmt.Println(libpath, loaded.Fn, C.GoString(loaded.Err))
//
if loaded.Err != nil {
panic(C.GoString(loaded.Err)) //return nil, err
}
Expand All @@ -151,9 +148,18 @@ var nextLabel uint

// TODO: more intelligent asm generation
func generateAssembly(asm *bytecode.Assembly) (label string, source string) {
fmt.Println(asm)
source = asm.Source
var offset int
var parts []string
for i, binding := range asm.InputBindings {
// insert the output binding if it appears before this input
if asm.HasOutput && asm.OutputBinding.Offset < binding.Offset {
binding := asm.OutputBinding
register := "%rax"

parts = append(parts, asm.Source[offset:binding.Offset], register)
offset = binding.Offset + len(binding.Name.Literal)
}

var register string
switch i {
case 0:
Expand All @@ -170,22 +176,24 @@ func generateAssembly(asm *bytecode.Assembly) (label string, source string) {
register = "%r9"
}

a := binding.Offset
b := binding.Offset + len(binding.Name.Literal)
c := len(source)
source = strings.Join([]string{source[0:a], register, source[b:c]}, "")

parts = append(parts, asm.Source[offset:binding.Offset], register)
offset = binding.Offset + len(binding.Name.Literal)
}

if asm.HasOutput {
// insert the output binding if it has not yet been inserted
if asm.HasOutput && asm.OutputBinding.Offset >= offset {
binding := asm.OutputBinding
register := "%rax"

a := binding.Offset
b := binding.Offset + len(binding.Name.Literal)
c := len(source)
source = strings.Join([]string{source[0:a], register, source[b:c]}, "")
parts = append(parts, asm.Source[offset:binding.Offset], register)
offset = binding.Offset + len(binding.Name.Literal)
}

// combine the parts into an updated source
parts = append(parts, asm.Source[offset:len(asm.Source)])
source = strings.Join(parts, "")

nextLabel += 1
label = fmt.Sprintf("interpreter.wrapper%d", nextLabel)
source = fmt.Sprintf(`
Expand All @@ -206,7 +214,7 @@ func CallAsm(asm *bytecode.Assembly, registers []bytecode.Data) {
}

params := make([]bytecode.Data, len(asm.InputRegisters))
for i, register := range params {
for i, register := range asm.InputRegisters {
params[i] = registers[register]
}

Expand Down
8 changes: 4 additions & 4 deletions code/interpreter/assembler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ import (

func TestGenerate(t *testing.T) {
asm := &bytecode.Assembly{
Source: " mov output, input ",
Source: " mov output, input",
HasOutput: true,
OutputBinding: ast.AsmBinding{ast.Ident("output"), 5},
InputBindings: []ast.AsmBinding{{ast.Ident("input"), 13}},
OutputBinding: ast.AsmBinding{ast.Ident("output"), 6},
InputBindings: []ast.AsmBinding{{ast.Ident("input"), 14}},
}

label, source := generateAssembly(asm)
Expand All @@ -24,7 +24,7 @@ func TestGenerate(t *testing.T) {
.section .text
interpreter.wrapper1:
mov %rax, %rdi
mov %rax, %rdi
ret
`, source)
}
18 changes: 18 additions & 0 deletions code/semantics/inference.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package semantics

import (
"regexp"
"strconv"
"strings"

Expand Down Expand Up @@ -77,6 +78,9 @@ func inferTypesRecursive(node ast.Node) ast.Type {
case *ast.NumberLiteral:
n.Type, n.Value = parseNumber(n.Literal)
return n.Type
case *ast.TextLiteral:
n.Type = ast.InferredText
n.Value = parseString(n.Literal)
default:
utils.InvalidCodePath()
}
Expand Down Expand Up @@ -152,6 +156,20 @@ func inferInfixType(op *ast.OperatorDefn, left ast.Type, right ast.Type) ast.Typ
}
}

// TODO: Stop being lazy (using regexp) and replace escape sequences properly
var escNewline = regexp.MustCompile(`\\n`)
var escReturn = regexp.MustCompile(`\\r`)
var escTab = regexp.MustCompile(`\\t`)

func parseString(lit string) []byte {
utils.Assert(len(lit) >= 2, "Expected string literal to still be quoted in type inference")
result := []byte(lit[1 : len(lit)-1])
result = escNewline.ReplaceAll(result, []byte("\n"))
result = escReturn.ReplaceAll(result, []byte("\r"))
result = escTab.ReplaceAll(result, []byte("\t"))
return result
}

// TODO: Should literals continue to be parsed here, or elsewhere?
func parseNumber(num string) (ast.Type, interface{}) {
if len(num) > 2 && num[0:2] == "0x" {
Expand Down
14 changes: 10 additions & 4 deletions main.phi
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#!philomath

/*
unix_stdin :: 0
unix_stdout :: 1
unix_stderr :: 2
Expand All @@ -11,19 +12,24 @@ unix_close :: 3
unix_mmap :: 9
unix_mprotect :: 10
unix_munmap :: 11
*/

main :: () {
message := "Hello world!\n"
retval := 0
unix_write :: 1;
unix_stdout :: 1;
message :: "Hello world!\n";
length :: 14;

retval := 0;

#asm {
mov %rax, unix_write
mov %rdi, unix_stdout
mov %rsi, message
mov %rdx, 13
mov %rdx, length
syscall
mov retval, %rax
}

return retval
// return retval;
}
3 changes: 1 addition & 2 deletions philomath.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,5 @@ func doRun(args []string) {
log.Fatalf(`unable to find a procedure named "main"`)
}

tmp := interpreter.Run(program)
log.Println("result:", tmp)
interpreter.Run(program)
}

0 comments on commit 057ac94

Please sign in to comment.