Skip to content

Commit 50cd487

Browse files
committed
Implement a web based REPL using gpython
This implements a wasm and gopherjs REPL frontend for gpython.
1 parent 08903fc commit 50cd487

File tree

12 files changed

+729
-2
lines changed

12 files changed

+729
-2
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ It includes:
1414
* lexer
1515
* parser
1616
* compiler
17-
* interactive mode (REPL)
17+
* interactive mode (REPL) ([try online!](https://gpython.org))
1818

1919
It does not include very many python modules as many of the core
2020
modules are written in C not python. The converted modules are:

go.mod

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
module github.com/go-python/gpython
22

3-
require github.com/peterh/liner v1.1.0
3+
require (
4+
github.com/gopherjs/gopherwasm v1.0.0 // indirect
5+
github.com/peterh/liner v1.1.0
6+
)

go.sum

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
github.com/gopherjs/gopherjs v0.0.0-20180825215210-0210a2f0f73c h1:16eHWuMGvCjSfgRJKqIzapE78onvvTbdi1rMkU00lZw=
2+
github.com/gopherjs/gopherjs v0.0.0-20180825215210-0210a2f0f73c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
3+
github.com/gopherjs/gopherwasm v1.0.0 h1:32nge/RlujS1Im4HNCJPp0NbBOAeBXFuT1KonUuLl+Y=
4+
github.com/gopherjs/gopherwasm v1.0.0/go.mod h1:SkZ8z7CWBz5VXbhJel8TxCmAcsQqzgWGR/8nMhyhZSI=
15
github.com/mattn/go-runewidth v0.0.3 h1:a+kO+98RDGEfo6asOGMmpodZq4FNtnGP54yps8BzLR4=
26
github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
37
github.com/peterh/liner v1.1.0 h1:f+aAedNJA6uk7+6rXsYBnhdo4Xux7ESLe+kcuVUF5os=

repl/web/.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
gpython.wasm
2+
gpython.js
3+
gpython.js.map

repl/web/Makefile

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
build:
2+
GOARCH=wasm GOOS=js go build -o gpython.wasm
3+
gopherjs build -m -o gpython.js
4+
5+
serve: build
6+
go run serve.go
7+
8+
upload: build
9+
rclone -P copy --include="*.{wasm,js,css,html}" . gpythonweb:
10+
@echo See https://gpython.org/

repl/web/README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Gpython Web
2+
3+
This implements a web viewable version of the gpython REPL.
4+
5+
This is done by compiling gpython into wasm and running that in the
6+
browser.
7+
8+
[Try it online.](https://www.craig-wood.com/nick/gpython/)
9+
10+
## Build and run
11+
12+
`make build` will build with go wasm (you'll need go1.11 minimum)
13+
14+
`make serve` will run a local webserver you can see the results on
15+
16+
## Thanks
17+
18+
Thanks to [jQuery Terminal](https://terminal.jcubic.pl/) for the
19+
terminal emulator and the go team for great [wasm
20+
support](https://github.com/golang/go/wiki/WebAssembly).

repl/web/index.html

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<!doctype html>
2+
<html>
3+
<head>
4+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
5+
<script src="https://code.jquery.com/jquery-latest.js"></script>
6+
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.terminal/1.23.2/js/jquery.terminal.min.js"></script>
7+
<link href="https://cdnjs.cloudflare.com/ajax/libs/jquery.terminal/1.23.2/css/jquery.terminal.min.css" rel="stylesheet"/>
8+
<style>
9+
/* position the outer div filling the whole browser window */
10+
#outer {
11+
position: fixed;
12+
display: flex;
13+
flex-direction: column;
14+
top: 0; right: 0; bottom: 0; left: 0;
15+
}
16+
17+
/* Terminal output at the top should grow to fill the space */
18+
#term {
19+
order: 0;
20+
flex-grow: 1;
21+
}
22+
23+
/* Navigation bar is fixed size at the bottom */
24+
#navbar {
25+
order: 1;
26+
flex-grow: 0;
27+
background-color: #333;
28+
}
29+
30+
/* Style the links inside the navigation bar */
31+
#navbar a {
32+
float: left;
33+
color: #fff;
34+
text-align: center;
35+
padding: 4px 12px;
36+
text-decoration: none;
37+
font-size: 17px;
38+
}
39+
40+
/* Change the color of links on hover */
41+
#navbar a:hover {
42+
background-color: #ddd;
43+
color: black;
44+
}
45+
46+
/* Add a color to the active/current link */
47+
#navbar a.active {
48+
background-color: #4CAF50;
49+
color: white;
50+
}
51+
</style>
52+
</head>
53+
<body>
54+
<div id="outer">
55+
<div id="term">Loading...</div>
56+
<div id="navbar">
57+
<a id="gopherjs" href=".">Gopherjs</a>
58+
<a id="go/wasm" href="?wasm">Go/wasm</a>
59+
<a href="https://github.com/go-python/gpython" target="_blank">About</a>
60+
</div>
61+
</div>
62+
<script src="loader.js"></script>
63+
</body>
64+
</html>

repl/web/loader.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Set the default global log for use by wasm_exec.js
2+
go_log = console.log;
3+
4+
var useWasm = location.href.includes("?wasm");
5+
6+
console.log("useWasm =", useWasm);
7+
8+
var script = document.createElement('script');
9+
if (useWasm) {
10+
script.src = "wasm_exec.js";
11+
script.onload = function () {
12+
// polyfill
13+
if (!WebAssembly.instantiateStreaming) {
14+
WebAssembly.instantiateStreaming = async (resp, importObject) => {
15+
const source = await (await resp).arrayBuffer();
16+
return await WebAssembly.instantiate(source, importObject);
17+
};
18+
}
19+
20+
const go = new Go();
21+
let mod, inst;
22+
WebAssembly.instantiateStreaming(fetch("gpython.wasm"), go.importObject).then((result) => {
23+
mod = result.module;
24+
inst = result.instance;
25+
run();
26+
});
27+
28+
async function run() {
29+
console.clear();
30+
await go.run(inst);
31+
inst = await WebAssembly.instantiate(mod, go.importObject); // reset instance
32+
}
33+
};
34+
} else {
35+
script.src = "gpython.js";
36+
}
37+
document.head.appendChild(script);

repl/web/main.go

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
// An online REPL for gpython using wasm
2+
3+
// Copyright 2018 The go-python Authors. All rights reserved.
4+
// Use of this source code is governed by a BSD-style
5+
// license that can be found in the LICENSE file.
6+
7+
// +build js
8+
9+
package main
10+
11+
import (
12+
"log"
13+
"runtime"
14+
15+
"github.com/gopherjs/gopherwasm/js" // gopherjs to wasm converter shim
16+
17+
// import required modules
18+
_ "github.com/go-python/gpython/builtin"
19+
_ "github.com/go-python/gpython/math"
20+
"github.com/go-python/gpython/repl"
21+
_ "github.com/go-python/gpython/sys"
22+
_ "github.com/go-python/gpython/time"
23+
)
24+
25+
// Implement the replUI interface
26+
type termIO struct {
27+
js.Value
28+
}
29+
30+
// SetPrompt sets the UI prompt
31+
func (t *termIO) SetPrompt(prompt string) {
32+
t.Call("set_prompt", prompt)
33+
}
34+
35+
// Print outputs the string to the output
36+
func (t *termIO) Print(out string) {
37+
t.Call("echo", out)
38+
}
39+
40+
var document js.Value
41+
42+
func isUndefined(node js.Value) bool {
43+
return node == js.Undefined()
44+
}
45+
46+
func getElementById(name string) js.Value {
47+
node := document.Call("getElementById", name)
48+
if isUndefined(node) {
49+
log.Fatalf("Couldn't find element %q", name)
50+
}
51+
return node
52+
}
53+
54+
func running() string {
55+
switch {
56+
case runtime.GOOS == "js" && runtime.GOARCH == "wasm":
57+
return "go/wasm"
58+
case runtime.GOARCH == "js":
59+
return "gopherjs"
60+
}
61+
return "unknown"
62+
}
63+
64+
func main() {
65+
document = js.Global().Get("document")
66+
if isUndefined(document) {
67+
log.Fatalf("Didn't find document - not running in browser")
68+
}
69+
70+
// Clear the loading text
71+
termNode := getElementById("term")
72+
termNode.Set("innerHTML", "")
73+
74+
// work out what we are running on and mark active
75+
tech := running()
76+
node := getElementById(tech)
77+
node.Get("classList").Call("add", "active")
78+
79+
// Make a repl referring to an empty term for the moment
80+
REPL := repl.New()
81+
cb := js.NewCallback(func(args []js.Value) {
82+
REPL.Run(args[0].String())
83+
})
84+
85+
// Create a jquery terminal instance
86+
opts := js.ValueOf(map[string]interface{}{
87+
"greetings": "Gpython 3.4.0 running in your browser with " + tech,
88+
"name": "gpython",
89+
"prompt": repl.NormalPrompt,
90+
})
91+
terminal := js.Global().Call("$", "#term").Call("terminal", cb, opts)
92+
93+
// Send the console log direct to the terminal
94+
js.Global().Get("console").Set("log", terminal.Get("echo"))
95+
96+
// Set the implementation of term
97+
REPL.SetUI(&termIO{terminal})
98+
99+
// wait for callbacks
100+
select {}
101+
}

repl/web/serve.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//+build none
2+
3+
package main
4+
5+
import (
6+
"fmt"
7+
"log"
8+
"mime"
9+
"net/http"
10+
)
11+
12+
func main() {
13+
mime.AddExtensionType(".wasm", "application/wasm")
14+
mime.AddExtensionType(".js", "application/javascript")
15+
mux := http.NewServeMux()
16+
mux.Handle("/", http.FileServer(http.Dir(".")))
17+
fmt.Printf("Serving on http://localhost:3000/\n")
18+
log.Fatal(http.ListenAndServe(":3000", mux))
19+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy