Skip to content

Commit

Permalink
Implement a stateless REPL
Browse files Browse the repository at this point in the history
  • Loading branch information
lorenzleutgeb committed Jun 24, 2015
1 parent 8857870 commit 68edd32
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 3 deletions.
19 changes: 19 additions & 0 deletions Makefile
@@ -0,0 +1,19 @@
VERSION=$(shell git rev-parse --short HEAD)

# UTC time in ISO 8601
NOW=$(shell date --iso-8601=seconds)

OUTPUT?='gothon'

all: test
@go env
@go version

go build -work -x -v -o ${OUTPUT} \
-ldflags "-X main.Version '${VERSION}' -X main.BuildTime '${NOW}'"

test: get
go test

get:
go get -d
109 changes: 107 additions & 2 deletions gothon.go
Expand Up @@ -2,26 +2,42 @@ package main

import (
"bufio"
"bytes"
"flag"
"fmt"
"io"
"io/ioutil"
"os"
"os/exec"
"path"
"runtime"
"strconv"
)

var (
debug = flag.Bool("d", true, "debug. if set, gothon will tell precicesly what it is doing.")
)

var Version string
var BuildTime string

func main() {
flag.Usage = func() {
fmt.Fprintf(os.Stderr, "Usage: %s [parameters] <filename>\n\nAvailable parameters:\n", os.Args[0])
fmt.Fprintf(os.Stderr, "Usage: \n")
fmt.Fprintf(os.Stderr, " %s [flags] <filename>\n", os.Args[0])
fmt.Fprintf(os.Stderr, " for interpreting Python code in <filename>\n")
fmt.Fprintf(os.Stderr, " %s [flags]\n", os.Args[0])
fmt.Fprintf(os.Stderr, " for running a REPL\n\n")
fmt.Fprintf(os.Stderr, "Available flags:\n")
flag.PrintDefaults()
fmt.Fprint(os.Stderr, "\nThis is an experimental interpreter for Python written by Lorenz Leutgeb.\n")
}

flag.Parse()

if len(flag.Args()) == 0 {
repl()
}

if len(flag.Args()) != 1 {
flag.Usage()
os.Exit(1)
Expand Down Expand Up @@ -71,3 +87,92 @@ func resolve(target string) (file *os.File, err error) {
file, err = os.Open(target)
return
}

// Takes some Python source code and compiles it by passing it
// to an external Python compiler.
func compile(code string) (output []byte, err error) {
inject := `
import marshal
print(marshal.dumps(compile("` + code + `", "<repl>", "exec")))
`

cmd := exec.Command("python3.4")
stdin, err := cmd.StdinPipe()
if err != nil {
return
}
stdout, err := cmd.StdoutPipe()
if err != nil {
return
}
cmd.Stderr = os.Stderr

err = cmd.Start()
if err != nil {
return
}

stdin.Write([]byte(inject))
stdin.Close()

output, err = ioutil.ReadAll(stdout)
if err != nil {
return
}
stdout.Close()

err = cmd.Wait()

if err != nil {
return
}
s := "\"" + string(output[2:len(output)-2]) + "\""
t, err := strconv.Unquote(s)
if err != nil {
return
}
output = []byte(t)
return
}

// Loops over user input until EOF on standard input.
func repl() {
fmt.Println("gothon " + Version + " (" + BuildTime + ")")
fmt.Println("[" + runtime.Version() + "]")
fmt.Println("Type \"copyright\" for more information.")

for {
print(">>> ")
bio := bufio.NewReader(os.Stdin)
raw, _, err := bio.ReadLine()
if err != nil {
if err == io.EOF {
fmt.Println()
os.Exit(0)
}
panic(err)
}

input := string(raw)

if input == "copyright" {
fmt.Println("Copyright (c) 2015 Lorenz Leutgeb.")
continue
}

raw, err = compile(input)
if err != nil {
if _, ok := err.(*exec.ExitError); ok {
continue
}
panic(err)
}

module := &Module{}
reader := Reader{*bufio.NewReader(bytes.NewReader(raw)), *module}
code := reader.ReadCode()

frame := NewFrame(code)
frame.Execute()
}
}
6 changes: 5 additions & 1 deletion reader.go
Expand Up @@ -5,6 +5,7 @@ import (
"encoding/binary"
"fmt"
"log"
"os"
)

type Reader struct {
Expand Down Expand Up @@ -84,7 +85,10 @@ func (reader *Reader) ReadObject() Object {
/* case '>'
result := &FrozenSet{} */
default:
panic(fmt.Sprintf("Reached bad type specifier '%c'", c))
// NOTE: Newline is here because we're right in the middle of
// short-printing all read types.
fmt.Fprintf(os.Stderr, "\nReached unimplemented or bad type specifier '%c'¸\n", c)
os.Exit(1)
}

// TODO(flowlo): Prevent references to null by checking here
Expand Down

0 comments on commit 68edd32

Please sign in to comment.