diff options
Diffstat (limited to 'misc/goplay/goplay.go')
-rw-r--r-- | misc/goplay/goplay.go | 280 |
1 files changed, 280 insertions, 0 deletions
diff --git a/misc/goplay/goplay.go b/misc/goplay/goplay.go new file mode 100644 index 000000000..bbc388ba4 --- /dev/null +++ b/misc/goplay/goplay.go @@ -0,0 +1,280 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "exec" + "flag" + "http" + "io" + "io/ioutil" + "log" + "os" + "runtime" + "strconv" + "template" +) + +var ( + httpListen = flag.String("http", "127.0.0.1:3999", "host:port to listen on") + htmlOutput = flag.Bool("html", false, "render program output as HTML") +) + +var ( + // a source of numbers, for naming temporary files + uniq = make(chan int) + // the architecture-identifying character of the tool chain, 5, 6, or 8 + archChar string +) + +func main() { + flag.Parse() + + // set archChar + switch runtime.GOARCH { + case "arm": + archChar = "5" + case "amd64": + archChar = "6" + case "386": + archChar = "8" + default: + log.Fatalln("unrecognized GOARCH:", runtime.GOARCH) + } + + // source of unique numbers + go func() { + for i := 0; ; i++ { + uniq <- i + } + }() + + http.HandleFunc("/", FrontPage) + http.HandleFunc("/compile", Compile) + log.Fatal(http.ListenAndServe(*httpListen, nil)) +} + +// FrontPage is an HTTP handler that renders the goplay interface. +// If a filename is supplied in the path component of the URI, +// its contents will be put in the interface's text area. +// Otherwise, the default "hello, world" program is displayed. +func FrontPage(w http.ResponseWriter, req *http.Request) { + data, err := ioutil.ReadFile(req.URL.Path[1:]) + if err != nil { + data = helloWorld + } + frontPage.Execute(w, data) +} + +// Compile is an HTTP handler that reads Go source code from the request, +// compiles and links the code (returning any errors), runs the program, +// and sends the program's output as the HTTP response. +func Compile(w http.ResponseWriter, req *http.Request) { + // x is the base name for .go, .6, executable files + x := os.TempDir() + "/compile" + strconv.Itoa(<-uniq) + src := x + ".go" + obj := x + "." + archChar + bin := x + if runtime.GOOS == "windows" { + bin += ".exe" + } + + // write request Body to x.go + f, err := os.Create(src) + if err != nil { + error(w, nil, err) + return + } + defer os.Remove(src) + defer f.Close() + _, err = io.Copy(f, req.Body) + if err != nil { + error(w, nil, err) + return + } + f.Close() + + // build x.go, creating x.6 + out, err := run(archChar+"g", "-o", obj, src) + defer os.Remove(obj) + if err != nil { + error(w, out, err) + return + } + + // link x.6, creating x (the program binary) + out, err = run(archChar+"l", "-o", bin, obj) + defer os.Remove(bin) + if err != nil { + error(w, out, err) + return + } + + // run x + out, err = run(bin) + if err != nil { + error(w, out, err) + } + + // write the output of x as the http response + if *htmlOutput { + w.Write(out) + } else { + output.Execute(w, out) + } +} + +// error writes compile, link, or runtime errors to the HTTP connection. +// The JavaScript interface uses the 404 status code to identify the error. +func error(w http.ResponseWriter, out []byte, err os.Error) { + w.WriteHeader(404) + if out != nil { + output.Execute(w, out) + } else { + output.Execute(w, err.String()) + } +} + +// run executes the specified command and returns its output and an error. +func run(cmd ...string) ([]byte, os.Error) { + return exec.Command(cmd[0], cmd[1:]...).CombinedOutput() +} + +var frontPage = template.Must(template.New("frontPage").Parse(frontPageText)) // HTML template +var output = template.Must(template.New("output").Parse(outputText)) // HTML template + +var outputText = `<pre>{{html .}}</pre>` + +var frontPageText = `<!doctype html> +<html> +<head> +<style> +pre, textarea { + font-family: Monaco, 'Courier New', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; + font-size: 100%; +} +.hints { + font-size: 0.8em; + text-align: right; +} +#edit, #output, #errors { width: 100%; text-align: left; } +#edit { height: 500px; } +#output { color: #00c; } +#errors { color: #c00; } +</style> +<script> + +function insertTabs(n) { + // find the selection start and end + var cont = document.getElementById("edit"); + var start = cont.selectionStart; + var end = cont.selectionEnd; + // split the textarea content into two, and insert n tabs + var v = cont.value; + var u = v.substr(0, start); + for (var i=0; i<n; i++) { + u += "\t"; + } + u += v.substr(end); + // set revised content + cont.value = u; + // reset caret position after inserted tabs + cont.selectionStart = start+n; + cont.selectionEnd = start+n; +} + +function autoindent(el) { + var curpos = el.selectionStart; + var tabs = 0; + while (curpos > 0) { + curpos--; + if (el.value[curpos] == "\t") { + tabs++; + } else if (tabs > 0 || el.value[curpos] == "\n") { + break; + } + } + setTimeout(function() { + insertTabs(tabs); + }, 1); +} + +function keyHandler(event) { + var e = window.event || event; + if (e.keyCode == 9) { // tab + insertTabs(1); + e.preventDefault(); + return false; + } + if (e.keyCode == 13) { // enter + if (e.shiftKey) { // +shift + compile(e.target); + e.preventDefault(); + return false; + } else { + autoindent(e.target); + } + } + return true; +} + +var xmlreq; + +function autocompile() { + if(!document.getElementById("autocompile").checked) { + return; + } + compile(); +} + +function compile() { + var prog = document.getElementById("edit").value; + var req = new XMLHttpRequest(); + xmlreq = req; + req.onreadystatechange = compileUpdate; + req.open("POST", "/compile", true); + req.setRequestHeader("Content-Type", "text/plain; charset=utf-8"); + req.send(prog); +} + +function compileUpdate() { + var req = xmlreq; + if(!req || req.readyState != 4) { + return; + } + if(req.status == 200) { + document.getElementById("output").innerHTML = req.responseText; + document.getElementById("errors").innerHTML = ""; + } else { + document.getElementById("errors").innerHTML = req.responseText; + document.getElementById("output").innerHTML = ""; + } +} +</script> +</head> +<body> +<table width="100%"><tr><td width="60%" valign="top"> +<textarea autofocus="true" id="edit" spellcheck="false" onkeydown="keyHandler(event);" onkeyup="autocompile();">{{html .}}</textarea> +<div class="hints"> +(Shift-Enter to compile and run.) +<input type="checkbox" id="autocompile" value="checked" /> Compile and run after each keystroke +</div> +<td width="3%"> +<td width="27%" align="right" valign="top"> +<div id="output"></div> +</table> +<div id="errors"></div> +</body> +</html> +` + +var helloWorld = []byte(`package main + +import "fmt" + +func main() { + fmt.Println("hello, world") +} +`) |