├── public ├── .nokjekyll ├── std.zip ├── favicon.ico └── images │ ├── ast.jpeg │ ├── vm_addn.jpeg │ ├── vm_mul.jpeg │ └── goscript-logo.jpeg ├── posts ├── goscript_internals_IV_use_cases_and_roadmap.md ├── goscript_internals_III_the_runtime_b.md ├── goscript_internals_I_overview_zh.md ├── goscript_internals_II_the_runtime_a_en.md └── goscript_internals_I_overview_en.md ├── README.md ├── goscript ├── pkg │ ├── goscript_playground_bg.wasm │ ├── package.json │ ├── goscript_playground_bg.wasm.d.ts │ ├── goscript_playground.d.ts │ └── goscript_playground.js ├── cargo.toml ├── .gitignore └── src │ └── lib.rs ├── next.config.js ├── go-code ├── closure2.gos ├── declare.gos ├── closure1.gos ├── blankid.gos ├── closure3.gos ├── strconv.gos ├── initorder.gos ├── array.gos ├── strings.gos ├── math.gos ├── bytes.gos ├── fmt.gos ├── goto.gos ├── if.gos ├── init_func.gos ├── case1.gos ├── display.gos ├── complex.gos ├── defer.gos ├── recover.gos ├── async.gos ├── closure4.gos ├── nil.gos ├── switch.gos ├── time.gos ├── func1.gos ├── basictypes.gos ├── sync_rwmutex.gos ├── channel.gos ├── sort.gos ├── interface2.gos ├── map1.gos ├── select.gos ├── sync_mutex.gos ├── typeswitch.gos ├── type1.gos ├── composite.gos ├── path.gos ├── pkg.gos ├── interface1.gos ├── conversion.gos ├── pointer.gos ├── slice1.gos ├── operations.gos ├── reflect.gos └── for.gos ├── components ├── date.js ├── layout.module.css ├── layout.js └── playground.js ├── pages ├── _app.js ├── go │ └── [id].js ├── index.js └── posts │ └── [id].js ├── .gitignore ├── lib ├── runGo.js ├── goCode.js ├── posts.js └── embedded.js ├── package.json ├── .github └── workflows │ └── main.yml └── styles ├── utils.module.css ├── global.css └── utils.prism.css /public/.nokjekyll: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /posts/goscript_internals_IV_use_cases_and_roadmap.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/std.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oxfeeefeee/goscript-dev-site/HEAD/public/std.zip -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | soruce code of goscript.dev 2 | 3 | git subtree push --prefix out origin gh-pages 4 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oxfeeefeee/goscript-dev-site/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /public/images/ast.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oxfeeefeee/goscript-dev-site/HEAD/public/images/ast.jpeg -------------------------------------------------------------------------------- /public/images/vm_addn.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oxfeeefeee/goscript-dev-site/HEAD/public/images/vm_addn.jpeg -------------------------------------------------------------------------------- /public/images/vm_mul.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oxfeeefeee/goscript-dev-site/HEAD/public/images/vm_mul.jpeg -------------------------------------------------------------------------------- /public/images/goscript-logo.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oxfeeefeee/goscript-dev-site/HEAD/public/images/goscript-logo.jpeg -------------------------------------------------------------------------------- /goscript/pkg/goscript_playground_bg.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oxfeeefeee/goscript-dev-site/HEAD/goscript/pkg/goscript_playground_bg.wasm -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | // next.config.js 2 | 3 | module.exports = { 4 | images: { 5 | loader: 'akamai', 6 | path: '', 7 | }, 8 | } -------------------------------------------------------------------------------- /go-code/closure2.gos: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | a := 44 5 | b := func() int { 6 | a = 43 7 | return a + 10 8 | }() 9 | c := b + 10 10 | assert(c == 63) 11 | } -------------------------------------------------------------------------------- /go-code/declare.gos: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | 5 | const i, j int = 1.0, 2 6 | var _,_,c int = 30,40, 50 7 | var l int 8 | k := i + j 9 | k = k + c + l 10 | assert(k == 53) 11 | } 12 | -------------------------------------------------------------------------------- /go-code/closure1.gos: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | var a = 43 4 | 5 | func main() { 6 | //i := 0 7 | a := 44 8 | b := func() int { 9 | return a + 10 10 | } 11 | c := b() + 10 12 | assert(c == 64) 13 | } -------------------------------------------------------------------------------- /components/date.js: -------------------------------------------------------------------------------- 1 | import { parseISO, format } from 'date-fns'; 2 | 3 | export default function Date({ dateString }) { 4 | const date = parseISO(dateString); 5 | return ; 6 | } -------------------------------------------------------------------------------- /go-code/blankid.gos: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func a() (int, int, int) { 4 | return 1, 2, 3 5 | } 6 | 7 | func main() { 8 | i, _, _ := a() 9 | _, j, _ := a() 10 | _, _, k := a() 11 | assert(i == 1) 12 | assert(j == 2) 13 | assert(k == 3) 14 | } -------------------------------------------------------------------------------- /go-code/closure3.gos: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func a(v int) func(int) int { 4 | i := 100 5 | i = i + v 6 | return func(j int) int { 7 | return i + j 8 | } 9 | } 10 | 11 | func main() { 12 | b := a(30) 13 | c := b(2) 14 | assert(c == 132) 15 | } -------------------------------------------------------------------------------- /go-code/strconv.gos: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "strconv" 5 | "fmt" 6 | ) 7 | 8 | 9 | func main() { 10 | r, err := strconv.ParseFloat("1.000000000000000111022302462515654042363166809082031251", 64) 11 | fmt.Println(r, err) 12 | } -------------------------------------------------------------------------------- /pages/_app.js: -------------------------------------------------------------------------------- 1 | import 'bootstrap/dist/css/bootstrap.min.css'; 2 | import '../styles/global.css'; 3 | import "prismjs/themes/prism.css"; 4 | import '../styles/utils.prism.css'; 5 | 6 | export default function App({ Component, pageProps }) { 7 | return ; 8 | } -------------------------------------------------------------------------------- /components/layout.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | max-width: 46rem; 3 | padding: 0 1rem; 4 | margin: 3rem auto 6rem; 5 | } 6 | 7 | .header { 8 | display: flex; 9 | flex-direction: column; 10 | align-items: center; 11 | } 12 | 13 | .backToHome { 14 | margin: 3rem 0 0; 15 | } -------------------------------------------------------------------------------- /go-code/initorder.gos: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | 4 | //import "math" 5 | 6 | 7 | 8 | const k = i + 1 9 | 10 | const i = 7 11 | 12 | var b = a + k 13 | var a = 8 14 | 15 | 16 | 17 | func main() { 18 | //var pi = math.Pi 19 | //_ = pi 20 | //var i = 1 21 | assert(b == 16) 22 | } -------------------------------------------------------------------------------- /go-code/array.gos: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | //import "fmt" 4 | 5 | 6 | type Node struct{ 7 | i int 8 | j string 9 | } 10 | 11 | func ttt() { 12 | 13 | 14 | var c [3]Node 15 | assert(c[2].i == 0) 16 | 17 | } 18 | 19 | func main() { 20 | //ttt() 21 | ttt() 22 | 23 | //testb() 24 | } -------------------------------------------------------------------------------- /go-code/strings.gos: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | func main() { 9 | 10 | var b strings.Builder 11 | b.WriteString("3.......") 12 | b.WriteString("2.......") 13 | b.WriteString("1.......") 14 | b.WriteString("ignition") 15 | fmt.Println("xxxx", b.String()) 16 | 17 | } 18 | -------------------------------------------------------------------------------- /goscript/cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "goscript-playground" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [lib] 7 | crate-type = ["cdylib"] 8 | 9 | [dependencies] 10 | wasm-bindgen = "0.2" 11 | 12 | 13 | [dependencies.goscript-engine] 14 | path = "../../goscript/engine" 15 | version = "0.1.0" 16 | features = ["read_zip", "wasm"] -------------------------------------------------------------------------------- /goscript/pkg/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "goscript-playground", 3 | "version": "0.1.0", 4 | "files": [ 5 | "goscript_playground_bg.wasm", 6 | "goscript_playground.js", 7 | "goscript_playground.d.ts" 8 | ], 9 | "module": "goscript_playground.js", 10 | "types": "goscript_playground.d.ts", 11 | "sideEffects": [ 12 | "./snippets/*" 13 | ] 14 | } -------------------------------------------------------------------------------- /go-code/math.gos: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "math" 5 | "fmt" 6 | ) 7 | 8 | const k = i + 1 9 | 10 | const i = 7 11 | 12 | var b = a + k 13 | var a = 8 14 | 15 | 16 | func main() { 17 | p := math.Pi 18 | 19 | fmt.Println(p, math.Log10(1000), math.Sin(p), math.Sin(p/2), math.Sin(1)) 20 | 21 | fmt.Println(math.Pow(3,20.2), math.Pow(3,-20.2)) 22 | } -------------------------------------------------------------------------------- /posts/goscript_internals_III_the_runtime_b.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'Goscript Internals II: The Virtual Machine Design, part B' 3 | --- 4 | 5 | 6 | ## Data structure 7 | 8 | ## Memory management and GC 9 | 10 | ## Package initialization 11 | 12 | ## Interface 13 | 14 | ## Channel and select 15 | 16 | ## Deferred calls, panic and recovery 17 | 18 | ## FFI and libraries 19 | -------------------------------------------------------------------------------- /go-code/bytes.gos: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "bytes" 6 | ) 7 | 8 | func main() { 9 | var s string = "Hello World" 10 | sb := []byte(s) 11 | 12 | fmt.Println(bytes.Index(sb, []byte{111,114})) 13 | 14 | fmt.Println(sb) // [72 101 108 108 111 32 87 111 114 108 100] 15 | 16 | fmt.Println(string(sb)) // Hello World 17 | } -------------------------------------------------------------------------------- /go-code/fmt.gos: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "fmt" 6 | ) 7 | 8 | func main() { 9 | fmt.Printf("%d %d %#[1]x %#x \n", 16, 17) 10 | const name, age = "Kim", 22 11 | s := fmt.Sprintf("%d/%d", 16, 1,1) 12 | fmt.Println(s) 13 | fmt.Println(name, "is", age, "years old.") 14 | fmt.Println(name, "is", age, "years old.") 15 | 16 | 17 | 18 | 19 | } -------------------------------------------------------------------------------- /go-code/goto.gos: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | var a int = 0 7 | total := 0 8 | skip := 6 9 | 10 | LOOP: for a < 10 { 11 | if a == skip { 12 | a = a + 1 13 | goto LOOP 14 | } 15 | total += a 16 | fmt.Println("value of a: ", a) 17 | a++ 18 | } 19 | 20 | fmt.Println(total) 21 | assert(total == 45 - skip) 22 | } -------------------------------------------------------------------------------- /go-code/if.gos: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func f(i int) int { 4 | var j int 5 | if k := 666; i == 0 { 6 | j = 888 7 | } else if i == 1 { 8 | j = 1 9 | } else if i == 2 { 10 | j = k 11 | } else { 12 | j = k + 4 13 | } 14 | return j 15 | } 16 | 17 | func main() { 18 | assert(f(0) == 888) 19 | assert(f(1) == 1) 20 | assert(f(2) == 666) 21 | assert(f(3) == 670) 22 | } -------------------------------------------------------------------------------- /go-code/init_func.gos: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | 4 | import ( 5 | "fmt" 6 | ) 7 | 8 | var i = 10 9 | 10 | func init() { 11 | i++ 12 | assert(i == 11) 13 | } 14 | 15 | 16 | func init() { 17 | i++ 18 | assert(i == 12) 19 | } 20 | 21 | func init() { 22 | i++ 23 | assert(i == 13) 24 | } 25 | 26 | 27 | 28 | func main() { 29 | m := []byte{1,3} 30 | n := []byte{2,4} 31 | t := append(m, n...) 32 | fmt.Println(t, m, n, i) 33 | 34 | } -------------------------------------------------------------------------------- /go-code/case1.gos: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | 4 | import "fmt" 5 | import "unsafe" 6 | 7 | 8 | 9 | type geometry interface { 10 | area() int 11 | perim() int 12 | } 13 | 14 | func a() []int { 15 | a1 := []int{3} 16 | a2 := a1[1:] 17 | return a2 18 | } 19 | 20 | 21 | 22 | func main() { 23 | 24 | //var n = ffi(geometry, "test", 1, 2, 3, "haha", 5) 25 | //var i = n.area() 26 | //assert(i == 666) 27 | var p unsafe.Pointer 28 | fmt.Println(p, 666) 29 | 30 | } -------------------------------------------------------------------------------- /goscript/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | 12 | .DS_Store 13 | *.code-workspace 14 | 15 | backend/.vscode/ 16 | 17 | backup/ 18 | 19 | .VSCodeCounter/ 20 | .vscode/settings.json -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | 21 | # debug 22 | npm-debug.log* 23 | yarn-debug.log* 24 | yarn-error.log* 25 | 26 | # local env files 27 | .env.local 28 | .env.development.local 29 | .env.test.local 30 | .env.production.local 31 | -------------------------------------------------------------------------------- /lib/runGo.js: -------------------------------------------------------------------------------- 1 | import init, { run_zip_and_string } from "/goscript/pkg/goscript_playground.js"; 2 | 3 | let std_zip; 4 | 5 | async function initGoscript() { 6 | await init(); 7 | const res = await fetch('/std.zip'); 8 | const arrayBuffer = await res.arrayBuffer(); 9 | return new Uint8Array(arrayBuffer); 10 | } 11 | 12 | 13 | export async function runGo(code) { 14 | if (typeof std_zip === 'undefined') { 15 | std_zip = await initGoscript(); 16 | } 17 | return await run_zip_and_string(std_zip, code) 18 | } -------------------------------------------------------------------------------- /go-code/display.gos: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type III interface{} 8 | 9 | 10 | type A struct { 11 | i int 12 | j int 13 | } 14 | 15 | type B string 16 | 17 | func main() { 18 | i := 0 19 | var stru A 20 | var sli = []int{1,2,3} 21 | var m = map[A]interface{}{{1,2}:333, {1,3}:"fff", {2,2}:nil} 22 | m[A{1,3}] = "hahaxx" 23 | var b B 24 | b = "xxxx" 25 | var iii III 26 | iii = b 27 | fmt.Println(&i, stru, &stru, b, &b, sli, &sli, m, &m, m[A{1,3}], iii) 28 | } -------------------------------------------------------------------------------- /go-code/complex.gos: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | c := complex(1,2) 7 | assert(real(c) == 1) 8 | assert(imag(c) == 2) 9 | fmt.Println(c) 10 | 11 | 12 | var f float64 13 | f = 1.2345699999 14 | c2 := complex(8, f) 15 | 16 | var iface interface{} 17 | iface = real(c2) 18 | switch i := iface.(type) { 19 | case float32: 20 | j := 0 21 | assert(j == 1) 22 | case float64: 23 | assert(i == 8) 24 | fmt.Println("float64", i) 25 | } 26 | } -------------------------------------------------------------------------------- /go-code/defer.gos: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | 4 | import ( 5 | "fmt" 6 | ) 7 | 8 | type Mutex struct { 9 | i int 10 | } 11 | 12 | 13 | func (m *Mutex) Lock() { 14 | m.i += 85 15 | assert(m.i == 85) 16 | } 17 | 18 | 19 | 20 | func test2() { 21 | var m Mutex 22 | 23 | defer m.Lock() 24 | } 25 | 26 | 27 | func f() (result int) { 28 | defer func() { 29 | // result is accessed after it was set to 6 by the return statement 30 | result *= 7 31 | }() 32 | return 6 33 | } 34 | 35 | func main() { 36 | assert(f() == 42) 37 | fmt.Println(f()) 38 | 39 | for i := 0; i <= 3; i++ { 40 | defer fmt.Println(i) 41 | } 42 | 43 | test2() 44 | } 45 | 46 | 47 | -------------------------------------------------------------------------------- /go-code/recover.gos: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | 4 | import "fmt" 5 | 6 | func main() { 7 | f() 8 | fmt.Println("Returned normally from f.") 9 | } 10 | 11 | func f() { 12 | defer func() { 13 | if r := recover(); r != nil { 14 | fmt.Println("Recovered in f", r) 15 | } 16 | }() 17 | 18 | //panic("aaaaa") 19 | fmt.Println("Calling g.") 20 | g(0) 21 | fmt.Println("Returned normally from g.") 22 | } 23 | 24 | 25 | func g(i int) { 26 | if i > 3 { 27 | fmt.Println("Panicking!") 28 | panic("xxxxx") 29 | } 30 | 31 | defer fmt.Println("Defer in g", i) 32 | fmt.Println("Printing in g", i) 33 | g(i + 1) 34 | } 35 | 36 | -------------------------------------------------------------------------------- /go-code/async.gos: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func add(a int, b int, c int) { 8 | assert(a + b == c) 9 | fmt.Println(a, "+", b, "=", c) 10 | } 11 | 12 | func main() { 13 | go fmt.Println("hello world?") 14 | i := 1 15 | j := 2 16 | n := 888 17 | go add(i, j, 3) 18 | 19 | go func() { 20 | n = 666 21 | assert(n == 666) 22 | fmt.Println("----- n = ", n); 23 | }() 24 | 25 | assert(n == 888) 26 | fmt.Println("before waiting... n = ", n); 27 | for i := 1; i <= 10000; i++ { 28 | } 29 | assert(n == 666) 30 | fmt.Println("after waiting... n = ", n); 31 | 32 | k := i + j 33 | assert(k == 3) 34 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "dev": "next dev", 5 | "build": "next build && next export", 6 | "start": "next start" 7 | }, 8 | "dependencies": { 9 | "@monaco-editor/react": "^4.4.5", 10 | "bootstrap": "^5.1.3", 11 | "date-fns": "^2.28.0", 12 | "gray-matter": "^4.0.3", 13 | "next": "^12.2.0", 14 | "react": "17.0.2", 15 | "react-bootstrap": "^2.4.0", 16 | "react-dom": "17.0.2", 17 | "react-next-table": "^1.1.4", 18 | "remark": "^14.0.2", 19 | "remark-gfm": "^3.0.1", 20 | "remark-html": "^15.0.1", 21 | "remark-prism": "^1.3.6" 22 | }, 23 | "devDependencies": { 24 | "tailwindcss": "^3.1.4" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Deploy to Github Pages 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | workflow_dispatch: 9 | 10 | jobs: 11 | deployment: 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v3 16 | 17 | - name: Setup Node 18 | uses: actions/setup-node@v3 19 | with: 20 | node-version: "lts/*" 21 | cache: "npm" 22 | 23 | - name: Build 24 | run: | 25 | npm i 26 | npm run build 27 | - name: Deploy 28 | uses: peaceiris/actions-gh-pages@v3 29 | with: 30 | deploy_key: ${{ secrets.ACTIONS_DEPLOY_KEY }} 31 | publish_dir: ./out 32 | -------------------------------------------------------------------------------- /lib/goCode.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import path from 'path'; 3 | 4 | 5 | const codeDirectory = path.join(process.cwd(), 'go-code'); 6 | 7 | export function getAllCodeIds() { 8 | const fileNames = fs.readdirSync(codeDirectory).filter(s => s.endsWith('.gos') || s.endsWith('.go')); 9 | return fileNames.map((fileName) => { 10 | return { 11 | params: { 12 | id: fileName, 13 | }, 14 | }; 15 | }); 16 | } 17 | export async function getCodeData(id) { 18 | const fullPath = path.join(codeDirectory, id); 19 | const codeContent = fs.readFileSync(fullPath, 'utf8'); 20 | 21 | return { 22 | id, 23 | codeContent 24 | }; 25 | } 26 | 27 | -------------------------------------------------------------------------------- /go-code/closure4.gos: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func tt() { 4 | a := 44 5 | b := func() func() int { 6 | c := 3 7 | return func()int { 8 | d := 2 9 | return a + 1 + c + d 10 | } 11 | } 12 | e := func() int { 13 | c := b()() + 10 14 | return c + a 15 | } 16 | f := e() 17 | assert(f == 104) 18 | } 19 | 20 | 21 | func main() { 22 | tt() 23 | 24 | a := 44 25 | b := func() func() int { 26 | c := 3 27 | return func()int { 28 | d := 2 29 | return a + 1 + c + d 30 | } 31 | } 32 | e := func() int { 33 | c := b()() + 10 34 | return c + a 35 | } 36 | f := e() 37 | assert(f == 104) 38 | } 39 | -------------------------------------------------------------------------------- /go-code/nil.gos: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type III interface{} 8 | 9 | 10 | type A struct { 11 | i int 12 | } 13 | 14 | func main() { 15 | //var m []string 16 | 17 | var ia interface{} 18 | ia = 8 19 | 20 | var ib III 21 | ib = 9 22 | 23 | ib = ia 24 | 25 | var a A 26 | var b struct { 27 | i int 28 | } 29 | b.i = 99 30 | 31 | a = b 32 | b.i = 55 33 | 34 | fmt.Println("Hello, playground", ia, ia == nil, ib, ib == nil, 10, a.i, b.i) 35 | 36 | c := &a 37 | d := &b 38 | c.i = 111 39 | d.i = 222 40 | fmt.Println(a.i, b.i) 41 | *c = b 42 | fmt.Println(a.i, b.i) 43 | 44 | var e *int 45 | fmt.Println(e) 46 | i := 666 47 | e = &i 48 | fmt.Println(e, *e) 49 | } 50 | -------------------------------------------------------------------------------- /go-code/switch.gos: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | 4 | func test(v int) int { 5 | var a = 0 6 | switch v { 7 | default: 8 | a = 4 9 | case 100: 10 | a += 1 11 | case 200: 12 | a += 2 13 | } 14 | return a 15 | } 16 | 17 | func test2(v int) int { 18 | var a = 0 19 | switch v += 1; v { 20 | case 100, 101, 102: 21 | a += 1 22 | fallthrough 23 | case 200: 24 | a += 2 25 | default: 26 | a = 4 27 | } 28 | return a 29 | } 30 | 31 | func main() { 32 | assert(test(100) == 1) 33 | assert(test(200) == 2) 34 | assert(test(201) == 4) 35 | assert(test2(99) == 3) 36 | assert(test2(100) == 3) 37 | assert(test2(101) == 3) 38 | assert(test2(199) == 2) 39 | assert(test2(201) == 4) 40 | } -------------------------------------------------------------------------------- /go-code/time.gos: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | func main() { 9 | p := fmt.Println 10 | 11 | now := time.Now() 12 | p(now) 13 | 14 | then := time.Date( 15 | 2009, 11, 17, 20, 34, 58, 651387237, time.UTC) 16 | p(then) 17 | 18 | p(then.Year()) 19 | p(then.Month()) 20 | p(then.Day()) 21 | p(then.Hour()) 22 | p(then.Minute()) 23 | p(then.Second()) 24 | p(then.Nanosecond()) 25 | p(then.Location()) 26 | 27 | p(then.Weekday()) 28 | 29 | p(then.Before(now)) 30 | 31 | p(then.After(now)) 32 | p(then.Equal(now)) 33 | 34 | diff := now.Sub(then) 35 | p(diff) 36 | 37 | p(diff.Hours()) 38 | p(diff.Minutes()) 39 | p(diff.Seconds()) 40 | p(diff.Nanoseconds()) 41 | 42 | p(then.Add(diff)) 43 | p(then.Add(-diff)) 44 | } -------------------------------------------------------------------------------- /go-code/func1.gos: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func funca(i int) int { 4 | j := i + 1 5 | return j 6 | } 7 | 8 | 9 | func funcb(i int, iii int) (a int, b int) { 10 | fff := 4 11 | fff = 5 12 | fff = fff + 50 + i + funca(i) + iii 13 | b = fff 14 | return 15 | } 16 | 17 | 18 | func funcc(x ...int) (int, int, int) { 19 | i := []int{8} 20 | i = append(i, x...) 21 | m, n := funcb(i[0], i[1]) 22 | return m, n, i[2] 23 | } 24 | 25 | func named() (result int) { 26 | result = 6 27 | return 28 | } 29 | 30 | 31 | func main() { 32 | 33 | var v1, v2, v3 = funcc(100, 200, 300) 34 | var i1, i2, i3 = funcc([]int{1000, 2000, 3000}...) 35 | assert(v1 == 0) 36 | assert(v2 == 172) 37 | assert(v3 == 200) 38 | assert(i1 == 0) 39 | assert(i2 == 1072) 40 | assert(i3 == 2000) 41 | assert(named() == 6) 42 | } 43 | -------------------------------------------------------------------------------- /go-code/basictypes.gos: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | var i1 uint8 = 80 7 | j1 := i1 + 200 8 | assert(j1 == 24) 9 | fmt.Println(j1) 10 | 11 | var i2 uint8 = 0b0011 12 | var i3 uint8 = 0b0101 13 | assert(i2 & i3 == 0b0001) 14 | assert(i2 | i3 == 0b0111) 15 | assert(^i3 == 0b11111010) 16 | 17 | var i4 = 0b00110101 18 | assert(i4 << 2 == 0b11010100) 19 | assert(i4 >> 2 == 0b00001101) 20 | assert(i4 << 100 == 0) 21 | 22 | var ir1 uint8 = 2 23 | var ir2 uint16 = 2 24 | var ir3 uint16 = 2 25 | assert(i4 << ir1 == 0b11010100) 26 | assert(i4 >> ir1 == 0b00001101) 27 | assert(i4 << ir2 == 0b11010100) 28 | assert(i4 >> ir2 == 0b00001101) 29 | assert(i4 << ir3 == 0b11010100) 30 | assert(i4 >> ir3 == 0b00001101) 31 | 32 | assert(i4 << -ir1 == 0) 33 | assert(-ir1 == 254) 34 | 35 | //todo 36 | 37 | } -------------------------------------------------------------------------------- /styles/utils.module.css: -------------------------------------------------------------------------------- 1 | .heading2Xl { 2 | font-size: 2.5rem; 3 | line-height: 1.2; 4 | font-weight: 800; 5 | letter-spacing: -0.05rem; 6 | margin: 1rem 0; 7 | } 8 | 9 | .headingXl { 10 | font-size: 2rem; 11 | line-height: 1.3; 12 | font-weight: 800; 13 | letter-spacing: -0.05rem; 14 | margin: 1rem 0; 15 | } 16 | 17 | .headingLg { 18 | font-size: 1.3rem; 19 | line-height: 1.4; 20 | margin: 1rem 0; 21 | } 22 | 23 | .headingMd { 24 | font-size: 1.2rem; 25 | line-height: 1.5; 26 | } 27 | 28 | .borderCircle { 29 | border-radius: 9px; 30 | } 31 | 32 | .colorInherit { 33 | color: inherit; 34 | } 35 | 36 | .padding1px { 37 | padding-top: 1px; 38 | } 39 | 40 | .list { 41 | list-style: none; 42 | padding: 0; 43 | margin: 0; 44 | } 45 | 46 | .listItem { 47 | margin: 0 0 1.25rem; 48 | } 49 | 50 | .lightText { 51 | color: #666; 52 | } -------------------------------------------------------------------------------- /go-code/sync_rwmutex.gos: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | ) 7 | 8 | type Container struct { 9 | counters map[string]int 10 | mu sync.RWMutex 11 | } 12 | 13 | 14 | func (c *Container) inc(name string) { 15 | 16 | c.mu.Lock() 17 | defer c.mu.Unlock() 18 | c.counters[name] +=1 19 | } 20 | 21 | func (c *Container) read(name string) { 22 | 23 | c.mu.RLock() 24 | defer c.mu.RUnlock() 25 | fmt.Println(c.counters[name]) 26 | } 27 | 28 | 29 | func main() { 30 | 31 | c := Container{ 32 | 33 | counters: map[string]int{"a": 0, "b": 0}, 34 | } 35 | 36 | 37 | go func() { 38 | for i := 0; i < 12; i++ { 39 | c.inc("aaa") 40 | for i := 0; i < 100; i++ { 41 | } 42 | } 43 | }() 44 | 45 | go func() { 46 | for i := 0; i < 1000; i++{ 47 | go c.read("aaa") 48 | } 49 | }() 50 | } -------------------------------------------------------------------------------- /go-code/channel.gos: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func sum(s []int, c chan int) { 6 | sum := 0 7 | for _, v := range s { 8 | sum += v 9 | } 10 | c <- sum // send sum to c 11 | fmt.Println("c len: ", len(c)) 12 | } 13 | 14 | func testWithCap(capacity int) { 15 | s := []int{7, 2, 8, -9, 4, 0} 16 | 17 | c := make(chan int, capacity) 18 | go sum(s[:len(s)/2], c) 19 | go sum(s[len(s)/2:], c) 20 | 21 | x, y := <-c, <-c // receive from c 22 | 23 | assert(x + y == 12) 24 | fmt.Println(x, y, x+y) 25 | 26 | 27 | go func() {c <- 888}() 28 | nn, ok := <- c 29 | assert(nn == 888) 30 | assert(ok) 31 | 32 | close(c) 33 | assert(<-c == 0) 34 | fmt.Println(<-c) 35 | m := <- c 36 | assert(m == 0) 37 | n, ok := <- c 38 | assert(n == 0) 39 | assert(!ok) 40 | 41 | fmt.Println(m, n, nn) 42 | 43 | assert(cap(c) == capacity) 44 | } 45 | 46 | 47 | func main() { 48 | testWithCap(0) 49 | testWithCap(1) 50 | testWithCap(2) 51 | testWithCap(3) 52 | testWithCap(999) 53 | } 54 | -------------------------------------------------------------------------------- /goscript/pkg/goscript_playground_bg.wasm.d.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | export const memory: WebAssembly.Memory; 4 | export function __wbg_runresult_free(a: number): void; 5 | export function runresult_new(a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number): number; 6 | export function runresult_out(a: number, b: number): void; 7 | export function runresult_err(a: number, b: number): void; 8 | export function runresult_compile_err(a: number, b: number): void; 9 | export function runresult_debug(a: number, b: number): void; 10 | export function run_zip_and_string(a: number, b: number, c: number, d: number): number; 11 | export function __wbindgen_malloc(a: number): number; 12 | export function __wbindgen_realloc(a: number, b: number, c: number): number; 13 | export function __wbindgen_add_to_stack_pointer(a: number): number; 14 | export function __wbindgen_free(a: number, b: number): void; 15 | export function __wbindgen_exn_store(a: number): void; 16 | -------------------------------------------------------------------------------- /pages/go/[id].js: -------------------------------------------------------------------------------- 1 | import Layout from '../../components/layout'; 2 | import Head from 'next/head'; 3 | import { getAllCodeIds, getCodeData } from '../../lib/goCode'; 4 | import Playground from '../../components/playground'; 5 | 6 | 7 | 8 | export async function getStaticProps({ params }) { 9 | const codeData = await getCodeData(params.id); 10 | const codeIds = getAllCodeIds(); 11 | return { 12 | props: { 13 | codeData, 14 | codeIds, 15 | }, 16 | }; 17 | } 18 | 19 | export async function getStaticPaths() { 20 | const paths = getAllCodeIds(); 21 | return { 22 | paths, 23 | fallback: false, 24 | }; 25 | } 26 | 27 | export default function Post({ codeData, codeIds }) { 28 | return ( 29 | 30 | 31 | {codeData.id} 32 | 33 | 34 | 35 | ); 36 | } -------------------------------------------------------------------------------- /go-code/sort.gos: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "sort" 5 | "fmt" 6 | ) 7 | 8 | 9 | func main() { 10 | family := []struct { 11 | Name string 12 | Age int 13 | }{ 14 | {"Alice", 23}, 15 | {"David", 2}, 16 | {"Eve", 2}, 17 | {"Bob", 25}, 18 | } 19 | 20 | // Sort by age, keeping original order or equal elements. 21 | sort.SliceStable(family, func(i, j int) bool { 22 | return family[i].Age < family[j].Age 23 | }) 24 | assert(family[1].Name == "Eve") 25 | assert(family[3].Name == "Bob") 26 | fmt.Println(family) 27 | 28 | sort.Slice(family, func(i, j int) bool { 29 | return family[i].Name < family[j].Name 30 | }) 31 | assert(family[0].Name == "Alice") 32 | assert(family[3].Name == "Eve") 33 | fmt.Println(family) 34 | 35 | t := []string{"a", "b", "c","z", "y", "x"} 36 | sort.Strings(t) 37 | assert(t[0] == "a") 38 | assert(t[len(t)-1] == "z") 39 | fmt.Println(t) 40 | 41 | } -------------------------------------------------------------------------------- /go-code/interface2.gos: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type geometry interface { 6 | area() float64 7 | perim() float64 8 | } 9 | 10 | type rect struct { 11 | width, height float64 12 | } 13 | 14 | func (r rect) perim() float64 { 15 | return 2*r.width + 2*r.height 16 | } 17 | 18 | func (r rect) area() float64 { 19 | return r.width * r.height 20 | } 21 | 22 | func test1() { 23 | 24 | a := geometry(nil) 25 | var b geometry = (geometry)(nil) 26 | assert(a == nil) 27 | assert(b == nil) 28 | 29 | var r *rect 30 | b = r 31 | assert(b != nil) 32 | } 33 | 34 | 35 | //////////////////////////////////// 36 | 37 | type I interface { 38 | printVal() 39 | } 40 | 41 | type S struct { 42 | i int 43 | } 44 | 45 | func (s S) printVal() { 46 | assert(s.i == 0) 47 | fmt.Println(s.i) 48 | } 49 | 50 | func test2() { 51 | var i I 52 | 53 | var s S 54 | i = s 55 | s.i = 9 56 | i.printVal() 57 | } 58 | 59 | 60 | func main() { 61 | test1() 62 | test2() 63 | } 64 | -------------------------------------------------------------------------------- /go-code/map1.gos: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | 4 | func a () (int, int) { 5 | return 6,7 6 | } 7 | 8 | func b() int { 9 | return 8 10 | } 11 | 12 | func commaOk() { 13 | 14 | var i interface{} = "hello" 15 | 16 | 17 | s, ok := i.(string) 18 | assert(ok) 19 | assert(s == "hello") 20 | 21 | s1 := i.(string) 22 | assert(s1 == "hello") 23 | 24 | 25 | 26 | m := map[int]int{1:10, 2:20} 27 | int1, ok := m[1] 28 | assert(int1 == 10) 29 | assert(ok) 30 | int2 := m[2] 31 | assert(int2 == 20) 32 | 33 | t1, t2 := a() 34 | assert(t1 == 6) 35 | assert(t2 == 7) 36 | t3 := b() 37 | assert(t3 == 8) 38 | 39 | t01, t02 := 33, m[2] 40 | assert(t01 == 33) 41 | assert(t02 == 20) 42 | } 43 | 44 | func main() { 45 | var s1 = map[int]int{1:2, 3: 888} 46 | var v = s1[1] 47 | var v2 = s1[100] 48 | s1[0], v, v2 = 8, s1[100], s1[1] 49 | var v3 = s1[0] 50 | s1[2] = 3 51 | assert(v == 0) 52 | assert(v2 == 2) 53 | assert(v3 == 8) 54 | assert(s1[2] == 3) 55 | assert(s1[3] == 888) 56 | 57 | commaOk() 58 | } 59 | -------------------------------------------------------------------------------- /go-code/select.gos: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func fibonacci(c, quit chan int) { 6 | x, y := 0, 1 7 | var s = make([]int, 2) 8 | for { 9 | select { 10 | case c <- x: 11 | x, y = y, x+y 12 | case s[0] = <-quit: 13 | assert(s[0] == 999) 14 | fmt.Println("quit 拉", s) 15 | return 16 | } 17 | } 18 | } 19 | 20 | func main() { 21 | c := make(chan int) 22 | quit := make(chan int) 23 | go func() { 24 | for i := 0; i < 12; i++ { 25 | fmt.Println(<-c) 26 | } 27 | quit <- 999 28 | }() 29 | 30 | fibonacci(c, quit) 31 | 32 | 33 | var f = false 34 | 35 | go func() { 36 | quit <- 888 37 | }() 38 | 39 | select { 40 | case v, ok := <- c: 41 | assert(f) 42 | fmt.Println("c recv:", v, ok) 43 | case v, ok := <-quit: 44 | assert(v == 888) 45 | assert(ok) 46 | fmt.Println("quit recv:", v, ok) 47 | } 48 | 49 | go func() { 50 | close(c) 51 | }() 52 | 53 | select { 54 | case v, ok := <- c: 55 | assert(v == 0) 56 | assert(!ok) 57 | fmt.Println("c recv:", v, ok) 58 | break 59 | assert(f) 60 | case v, ok := <-quit: 61 | assert(f) 62 | fmt.Println("222 quit recv:", v, ok) 63 | 64 | } 65 | } -------------------------------------------------------------------------------- /go-code/sync_mutex.gos: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | ) 7 | 8 | type Container struct { 9 | counters map[string]int 10 | mu sync.Mutex 11 | } 12 | 13 | 14 | func (c *Container) inc(name string) { 15 | 16 | c.mu.Lock() 17 | defer c.mu.Unlock() 18 | c.counters[name] +=100 19 | } 20 | 21 | 22 | func sync_map() { 23 | var m sync.Map 24 | 25 | m.Store(2, "b") 26 | v, ok := m.LoadOrStore(1, "a") 27 | v2, ok2 := m.Load(1) 28 | assert(v == "a") 29 | assert(!ok) 30 | assert(v2 == "a") 31 | assert(ok2) 32 | fmt.Println(v, ok, v2, ok2) 33 | fmt.Println(m) 34 | m.Delete(2) 35 | v3, _ := m.Load(2) 36 | fmt.Println(m, v3) 37 | assert(v3 == nil) 38 | 39 | 40 | } 41 | 42 | 43 | func main() { 44 | 45 | sync_map() 46 | 47 | /* 48 | c := Container{ 49 | 50 | counters: map[string]int{"a": 0, "b": 0}, 51 | } 52 | c.inc("aaa") 53 | 54 | go c.inc("aaa") 55 | 56 | 57 | fmt.Println(c.counters) 58 | for i := 0; i < 10000; i++ { 59 | 60 | } 61 | fmt.Println(c.counters) 62 | 63 | fmt.Println() 64 | */ 65 | } -------------------------------------------------------------------------------- /go-code/typeswitch.gos: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | 8 | type S1 struct {i int; j int} 9 | 10 | func typeName(v interface{}) string { 11 | switch v.(type) { 12 | case int: 13 | return "int" 14 | case string: 15 | return "string" 16 | case *S1: 17 | return "S1" 18 | default: 19 | return "unknown" 20 | } 21 | } 22 | 23 | 24 | func typeName2(v interface{}) string { 25 | kk := 2 26 | switch i := v.(type) { 27 | case int: 28 | assert(i + kk == 890) 29 | return "int" 30 | case string: 31 | return "string" 32 | case *S1: 33 | _ = i 34 | return "S1" 35 | case []int: 36 | return "[]int" 37 | case map[string]int: 38 | return "map[string]int" 39 | case map[string][]int: 40 | return "map[string][]int" 41 | default: 42 | return "unknown" 43 | } 44 | return "int" 45 | } 46 | 47 | func main() { 48 | var s *S1; 49 | re := typeName(s) 50 | assert(re == "S1") 51 | re = typeName2(888) 52 | re2 := typeName2([]int{1}) 53 | re3 := typeName2(map[string]int{"a":1}) 54 | re4 := typeName2(map[string][]int{"a":{1}}) 55 | fmt.Println("typeswitch", re, re2, re3, re4) 56 | assert(re == "int") 57 | 58 | 59 | } -------------------------------------------------------------------------------- /styles/global.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | padding: 0; 4 | margin: 0; 5 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, Ubuntu, 6 | Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; 7 | line-height: 1.7; 8 | font-size: 18px; 9 | } 10 | 11 | textarea { 12 | line-height: 1.1; 13 | font-size: 14px; 14 | background-color: #d1d1d1; 15 | } 16 | 17 | * { 18 | box-sizing: border-box; 19 | } 20 | 21 | a { 22 | color: #0070f3; 23 | text-decoration: none; 24 | } 25 | 26 | a:hover { 27 | text-decoration: underline; 28 | } 29 | 30 | img { 31 | max-width: 100%; 32 | display: block; 33 | } 34 | 35 | table { 36 | display: table; 37 | border-collapse: collapse; 38 | } 39 | 40 | table td { 41 | padding: 15px; 42 | } 43 | 44 | table thead tr th { 45 | background-color: #dddfe1; 46 | color: #636363; 47 | font-weight: bold; 48 | font-size: 18px; 49 | border: 1px solid #dddfe1; 50 | } 51 | 52 | table tbody td { 53 | color: #636363; 54 | border: 1px solid #dddfe1; 55 | } 56 | 57 | table tbody tr { 58 | background-color: #f9fafb; 59 | } 60 | 61 | table tbody tr:nth-child(odd) { 62 | background-color: #ffffff; 63 | } -------------------------------------------------------------------------------- /go-code/type1.gos: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type S1 struct {i int; j int} 4 | 5 | 6 | func (s *S1) Inc() int { 7 | t := s.i 8 | s.i += 66 9 | assert(s.i - t == 66) 10 | return s.i 11 | } 12 | 13 | func (s S1) Inc2() int { 14 | assert(s.j == 0) 15 | s.j += 1 16 | assert(s.j == 1) 17 | return s.j 18 | } 19 | 20 | func typeAssert() { 21 | var i interface{} = "hello" 22 | 23 | s, ok := i.(string) 24 | assert(ok) 25 | assert(s == "hello") 26 | 27 | s1 := i.(string) 28 | assert(s1 == "hello") 29 | 30 | var j interface{} = "h" 31 | s, ok = j.(string) 32 | assert(ok) 33 | assert(s == "h") 34 | } 35 | 36 | type Add func(a int, b int) int 37 | 38 | 39 | func main() { 40 | type S2 struct {i int} 41 | var s1 S1 42 | //var s2 S2 43 | 44 | s1.Inc() 45 | s1.Inc2() 46 | assert(s1.i == 66) 47 | assert(s1.j == 0) 48 | 49 | p := &s1.i 50 | *p = 5 51 | assert(s1.i == 5) 52 | assert(s1.i == *p) 53 | 54 | f := s1.Inc 55 | f() 56 | assert(s1.i == 71) 57 | 58 | var afunc Add = func(i int, j int) int { return i + j + 1} 59 | d := afunc(s1.i, s1.j) 60 | assert(d == s1.i + s1.j + 1) 61 | 62 | 63 | typeAssert() 64 | typeAssert() 65 | typeAssert() 66 | 67 | } 68 | // 3, 8, 9 69 | -------------------------------------------------------------------------------- /go-code/composite.gos: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | 8 | type Point3D struct { x, y, z int } 9 | type Point3Df struct { x, y, z float32 } 10 | 11 | type acceptRange struct { 12 | lo uint8 13 | hi uint8 14 | } 15 | 16 | var t = [16]Point3D{{1,2,3},{y:4}} 17 | 18 | 19 | var acceptRanges = [16]acceptRange{ 20 | 0: {1, 2}, 21 | 1: {1, 2}, 22 | 2: {1, 0x9F}, 23 | 3: {0x90, 1}, 24 | 4: {1, 0x8F}, 25 | } 26 | 27 | 28 | 29 | func main() { 30 | 31 | var p = Point3D{8,9,10} 32 | assert(p.x == 8) 33 | 34 | var p2 = Point3D{y:88} 35 | assert(p2.y == 88) 36 | 37 | var s = []Point3D{{1,2,3},{y:4}} 38 | fmt.Println(s[0].x, s[0].y, s[0].z, s[1].x, s[1].y, s[1].z) 39 | 40 | assert(s[0].z == 3) 41 | assert(s[1].y == 4) 42 | 43 | var pf = Point3Df{1.1,20000000000000000000,3} 44 | assert(pf.x == 1.1) 45 | 46 | var t2 = []acceptRange{10:{1,1}, {2,2}, 1:{3,3}} 47 | fmt.Println(t2) 48 | assert(t2[0].lo == 0) 49 | assert(t2[1].lo == 3) 50 | assert(t2[11].lo == 2) 51 | assert(len(t2) == 12) 52 | 53 | var t3 = []acceptRange{1:{1,1}, {2,2}, 10:{3,3}} 54 | fmt.Println(t3) 55 | assert(t3[0].lo == 0) 56 | assert(t3[1].lo == 1) 57 | assert(t3[2].lo == 2) 58 | assert(len(t3) == 11) 59 | 60 | 61 | } 62 | -------------------------------------------------------------------------------- /go-code/path.gos: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "path" 5 | ) 6 | 7 | type PathTest struct { 8 | path, result string 9 | } 10 | 11 | var cleantests = []PathTest{ 12 | // Already clean 13 | {"", "."}, 14 | {"abc", "abc"}, 15 | {"abc/def", "abc/def"}, 16 | {"a/b/c", "a/b/c"}, 17 | {".", "."}, 18 | {"..", ".."}, 19 | {"../..", "../.."}, 20 | {"../../abc", "../../abc"}, 21 | {"/abc", "/abc"}, 22 | {"/", "/"}, 23 | 24 | // Remove trailing slash 25 | {"abc/", "abc"}, 26 | {"abc/def/", "abc/def"}, 27 | {"a/b/c/", "a/b/c"}, 28 | {"./", "."}, 29 | {"../", ".."}, 30 | {"../../", "../.."}, 31 | {"/abc/", "/abc"}, 32 | 33 | // Remove doubled slash 34 | {"abc//def//ghi", "abc/def/ghi"}, 35 | {"//abc", "/abc"}, 36 | {"///abc", "/abc"}, 37 | {"//abc//", "/abc"}, 38 | {"abc//", "abc"}, 39 | 40 | // Remove . elements 41 | {"abc/./def", "abc/def"}, 42 | {"/./abc/def", "/abc/def"}, 43 | {"abc/.", "abc"}, 44 | 45 | // Remove .. elements 46 | {"abc/def/ghi/../jkl", "abc/def/jkl"}, 47 | {"abc/def/../ghi/../jkl", "abc/jkl"}, 48 | {"abc/def/..", "abc"}, 49 | {"abc/def/../..", "."}, 50 | {"/abc/def/../..", "/"}, 51 | {"abc/def/../../..", ".."}, 52 | {"/abc/def/../../..", "/"}, 53 | {"abc/def/../../../ghi/jkl/../../../mno", "../../mno"}, 54 | 55 | // Combinations 56 | {"abc/./../def", "def"}, 57 | {"abc//./../def", "def"}, 58 | {"abc/../../././../def", "../../def"}, 59 | } 60 | 61 | func main() { 62 | 63 | for _, test := range cleantests { 64 | assert(path.Clean(test.path) == test.result) 65 | assert(path.Clean(test.result) == test.result) 66 | } 67 | 68 | } -------------------------------------------------------------------------------- /pages/index.js: -------------------------------------------------------------------------------- 1 | import Head from 'next/head'; 2 | import Link from 'next/link'; 3 | import Date from '../components/date'; 4 | import Layout, { siteTitle } from '../components/layout'; 5 | import Playground from '../components/playground'; 6 | import utilStyles from '../styles/utils.module.css'; 7 | import { getSortedPostsData } from '../lib/posts'; 8 | import { getAllCodeIds } from '../lib/goCode'; 9 | 10 | export async function getStaticProps() { 11 | const allPostsData = getSortedPostsData(); 12 | const codeIds = getAllCodeIds(); 13 | return { 14 | props: { 15 | allPostsData, 16 | codeIds, 17 | }, 18 | }; 19 | } 20 | 21 | export default function Home({ allPostsData, codeIds }) { 22 | const editor_options = { 23 | lineNumbers: false, 24 | }; 25 | return ( 26 | 27 | 28 | {siteTitle} 29 | 30 |
31 |

Playground

32 | 33 |
34 | 35 |
36 |

Blog

37 |
    38 | {allPostsData.map(({ id, date, title }) => ( 39 |
  • 40 | 41 | {title} 42 | 43 |
    44 | 45 | 46 | 47 |
  • 48 | ))} 49 |
50 |
51 |
52 | ); 53 | } -------------------------------------------------------------------------------- /go-code/pkg.gos: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | 4 | import ( 5 | . "fmt" 6 | ) 7 | 8 | 9 | const k = i + 1 10 | 11 | const i = 7 12 | 13 | var b = a + k 14 | var a = 8 15 | 16 | 17 | 18 | type Kind uint 19 | 20 | const ( 21 | Invalid Kind = iota 22 | Bool 23 | Int 24 | Int8 25 | Int16 26 | Int32 27 | Int64 28 | Uint 29 | Uint8 30 | Uint16 31 | Uint32 32 | Uint64 33 | Uintptr 34 | Float32 35 | Float64 36 | Complex64 37 | Complex128 38 | Array 39 | Chan 40 | Func 41 | Interface 42 | Map 43 | Ptr 44 | Slice 45 | String 46 | Struct 47 | UnsafePointer 48 | ) 49 | 50 | var kindNames = []string{ 51 | Invalid: "invalid", 52 | Bool: "bool", 53 | Int: "int", 54 | Int8: "int8", 55 | Int16: "int16", 56 | Int32: "int32", 57 | Int64: "int64", 58 | Uint: "uint", 59 | Uint8: "uint8", 60 | Uint16: "uint16", 61 | Uint32: "uint32", 62 | Uint64: "uint64", 63 | Uintptr: "uintptr", 64 | Float32: "float32", 65 | Float64: "float64", 66 | Complex64: "complex64", 67 | Complex128: "complex128", 68 | Array: "array", 69 | Chan: "chan", 70 | Func: "func", 71 | Interface: "interface", 72 | Map: "map", 73 | Ptr: "ptr", 74 | Slice: "slice", 75 | String: "string", 76 | Struct: "struct", 77 | UnsafePointer: "unsafe.Pointer", 78 | } 79 | 80 | func test1() { 81 | _, j := fa() 82 | assert(j == 7) 83 | 84 | assert(kindNames[2] == "int") 85 | } 86 | 87 | func fa () (int, int) { 88 | assert(8 == fb()) 89 | return 6,7 90 | } 91 | 92 | func fb() int { 93 | return 8 94 | } 95 | 96 | func test2() { 97 | Println("Hello, World!") 98 | } 99 | 100 | func main() { 101 | test1() 102 | test2() 103 | } 104 | -------------------------------------------------------------------------------- /pages/posts/[id].js: -------------------------------------------------------------------------------- 1 | import Layout from '../../components/layout'; 2 | import Head from 'next/head'; 3 | import Link from 'next/link'; 4 | import utilStyles from '../../styles/utils.module.css'; 5 | import { getAllPostIds, getPostData } from '../../lib/posts'; 6 | 7 | export async function getStaticProps({ params }) { 8 | const postData = await getPostData(params.id); 9 | return { 10 | props: { 11 | postData, 12 | }, 13 | }; 14 | } 15 | 16 | export async function getStaticPaths() { 17 | const paths = getAllPostIds(); 18 | return { 19 | paths, 20 | fallback: false, 21 | }; 22 | } 23 | 24 | export default function Post({ postData }) { 25 | return ( 26 | 27 | 28 | {postData.title} 29 | 30 | 49 | 50 | ); 51 | } -------------------------------------------------------------------------------- /go-code/interface1.gos: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | 4 | type geometry interface { 5 | area() float64 6 | perim() float64 7 | } 8 | 9 | 10 | type rect struct { 11 | width, height float64 12 | } 13 | 14 | func (r rect) perim() float64 { 15 | return 2*r.width + 2*r.height 16 | } 17 | 18 | func (r rect) area() float64 { 19 | return r.width * r.height 20 | } 21 | 22 | func measure(x geometry) float64 { 23 | return x.perim() 24 | } 25 | 26 | func measure2(x geometry, f float64, y geometry) float64 { 27 | return x.perim() + f + y.perim() 28 | } 29 | 30 | func measure3(x geometry, c float64, v... geometry) float64 { 31 | total := x.perim() + c 32 | for _, g := range v { 33 | total += g.perim() 34 | } 35 | return total 36 | } 37 | 38 | func measure4(x geometry, v... float64) float64 { 39 | total := x.perim() 40 | for _, g := range v { 41 | total += g 42 | } 43 | return total 44 | } 45 | 46 | /* 47 | type circle struct { 48 | radius float64 49 | } 50 | 51 | func (r rect) area() float64 { 52 | return r.width * r.height 53 | } 54 | func (r rect) perim() float64 { 55 | return 2*r.width + 2*r.height 56 | } 57 | 58 | func (c circle) area() float64 { 59 | return c.radius * c.radius 60 | } 61 | func (c circle) perim() float64 { 62 | return 1 * c.radius 63 | } 64 | 65 | func measure(g geometry) { 66 | assert(g.area() == 1) 67 | } 68 | */ 69 | 70 | func aaa() (int, *rect) { 71 | var r rect 72 | r2 := &r 73 | r.height = 2 74 | assert(2 == r2.height) 75 | return 1, r2 76 | } 77 | 78 | 79 | func main() { 80 | 81 | var r rect 82 | r.height = 8 83 | assert(8 == r.height) 84 | 85 | var g geometry 86 | f, g := -6, &r 87 | assert(f == -6) 88 | assert(16 == measure(g)) 89 | 90 | f, g = aaa() 91 | assert(f == 1) 92 | assert(4 == measure(g)) 93 | 94 | assert(37 == measure2(r, 5, r)) 95 | 96 | assert(16 * 3 + 5 == 53) 97 | 98 | //measure3(r, 2, r, r, r) 99 | assert(53 == measure3(r, 5, r, r)) 100 | 101 | assert(30 == measure4(r, 2, 3,4,5)) 102 | 103 | /* 104 | r := rect{width: 1, height: 1} 105 | c := circle{radius: 1} 106 | 107 | measure(r) 108 | measure(c) 109 | */ 110 | } -------------------------------------------------------------------------------- /goscript/pkg/goscript_playground.d.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | /* eslint-disable */ 3 | /** 4 | * @param {Uint8Array} zip 5 | * @param {string} source 6 | * @returns {RunResult} 7 | */ 8 | export function run_zip_and_string(zip: Uint8Array, source: string): RunResult; 9 | /** 10 | */ 11 | export class RunResult { 12 | free(): void; 13 | /** 14 | * @param {string} out 15 | * @param {string} err 16 | * @param {string} compile_err 17 | * @param {string} debug 18 | * @returns {RunResult} 19 | */ 20 | static new(out: string, err: string, compile_err: string, debug: string): RunResult; 21 | /** 22 | */ 23 | readonly compile_err: string; 24 | /** 25 | */ 26 | readonly debug: string; 27 | /** 28 | */ 29 | readonly err: string; 30 | /** 31 | */ 32 | readonly out: string; 33 | } 34 | 35 | export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module; 36 | 37 | export interface InitOutput { 38 | readonly memory: WebAssembly.Memory; 39 | readonly __wbg_runresult_free: (a: number) => void; 40 | readonly runresult_new: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number) => number; 41 | readonly runresult_out: (a: number, b: number) => void; 42 | readonly runresult_err: (a: number, b: number) => void; 43 | readonly runresult_compile_err: (a: number, b: number) => void; 44 | readonly runresult_debug: (a: number, b: number) => void; 45 | readonly run_zip_and_string: (a: number, b: number, c: number, d: number) => number; 46 | readonly __wbindgen_malloc: (a: number) => number; 47 | readonly __wbindgen_realloc: (a: number, b: number, c: number) => number; 48 | readonly __wbindgen_add_to_stack_pointer: (a: number) => number; 49 | readonly __wbindgen_free: (a: number, b: number) => void; 50 | readonly __wbindgen_exn_store: (a: number) => void; 51 | } 52 | 53 | export type SyncInitInput = BufferSource | WebAssembly.Module; 54 | /** 55 | * Instantiates the given `module`, which can either be bytes or 56 | * a precompiled `WebAssembly.Module`. 57 | * 58 | * @param {SyncInitInput} module 59 | * 60 | * @returns {InitOutput} 61 | */ 62 | export function initSync(module: SyncInitInput): InitOutput; 63 | 64 | /** 65 | * If `module_or_path` is {RequestInfo} or {URL}, makes a request and 66 | * for everything else, calls `WebAssembly.instantiate` directly. 67 | * 68 | * @param {InitInput | Promise} module_or_path 69 | * 70 | * @returns {Promise} 71 | */ 72 | export default function init (module_or_path?: InitInput | Promise): Promise; 73 | -------------------------------------------------------------------------------- /go-code/conversion.gos: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type type1 []struct { 6 | Field1 string 7 | Field2 int 8 | } 9 | type type2 []struct { 10 | Field1 string 11 | Field2 int 12 | } 13 | 14 | 15 | func main() { 16 | 17 | i := uint(42.0) 18 | f := float64(i) 19 | u := uint(f) 20 | assert(u == 42) 21 | 22 | { 23 | f2 := float64(-3.25) 24 | assert(f2 == -3.25) 25 | f3 := float32(f2) 26 | assert(f3 == -3.25) 27 | i := int(f3) 28 | assert(i == -3) 29 | } 30 | 31 | t1 := type1{{"A", 1}, {"B", 2}} 32 | t2 := type2(t1) 33 | assert(t2[1].Field2 == 2) 34 | 35 | 36 | s1 := string(100) 37 | assert(s1 == "d") 38 | fmt.Println(s1) 39 | 40 | s2 := string([]rune{100, 101}) 41 | assert(s2 == "de") 42 | fmt.Println(s2) 43 | 44 | data := []byte{'t','e','s','t'} 45 | s3 := string(data) 46 | assert(s3 == "test") 47 | fmt.Println(s3) 48 | 49 | b4 := []byte("dHello, 世界") 50 | r4 := []rune("dHello, 世界") 51 | assert(b4[0] == 100) 52 | assert(r4[0] == 100) 53 | s51 := string(b4) 54 | s52 := string(r4) 55 | assert(s51[0] == 'd') 56 | assert(s52[0] == 'd') 57 | shijie := "世界" 58 | assert(s51[8] == shijie[0]) 59 | assert(s52[9] == shijie[1]) 60 | fmt.Println(b4, r4, s51, s52) 61 | 62 | 63 | 64 | testNamed() 65 | testStruct() 66 | testChan() 67 | } 68 | 69 | 70 | type Duration int64 71 | 72 | func testNamed() { 73 | var minDuration Duration 74 | var i int32 = 4 75 | d := Duration(i) + minDuration 76 | j := Duration(i) 77 | assert(j==d) 78 | } 79 | 80 | type Person struct { 81 | Name string 82 | Address *struct { 83 | Street string 84 | City string 85 | } 86 | } 87 | 88 | var data struct { 89 | Name string `json:"name"` 90 | Address *struct { 91 | Street string `json:"street"` 92 | City string `json:"city"` 93 | } `json:"address"` 94 | } 95 | 96 | 97 | func testStruct() { 98 | var p Person = Person(data) 99 | 100 | var person = (*Person)(&data) // ignoring tags, the underlying types are identical 101 | assert(person != nil) 102 | person.Name = "aaaa" 103 | assert(data.Name == "aaaa") 104 | 105 | assert(p.Name == "") 106 | fmt.Println(p) 107 | } 108 | 109 | type C chan string 110 | 111 | func testChan() { 112 | var c C = make(C) 113 | d := (chan <- string)(c) 114 | fmt.Println(d) 115 | } -------------------------------------------------------------------------------- /go-code/pointer.gos: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | //import "math" 4 | 5 | type Node struct{ 6 | i int 7 | j string 8 | } 9 | 10 | var pkgVarA = 1 11 | 12 | func test() { 13 | a := 1 14 | b := &a 15 | *b = 2 16 | assert(2 == a) 17 | 18 | f := func() *int { 19 | return &a 20 | } 21 | 22 | d := f() 23 | e := *d 24 | assert(a == e) 25 | 26 | var n, n2 Node 27 | n.i = 1 28 | n2.i = 222 29 | m := &n 30 | *m = n2 31 | assert(n.i == 222) 32 | assert(m.i == 222) 33 | m.i = 333 34 | m.j = "abc" 35 | assert(n.i == 333) 36 | assert(n.j == "abc") 37 | 38 | pfield := &n.i 39 | *pfield += 1 40 | assert(n.i == 334) 41 | 42 | sl1 := []int{1,2} 43 | sl2 := sl1 44 | assert(sl2[0] == 1) 45 | i := 3 46 | psl := &sl2[i-3] 47 | *psl = 3 48 | assert(sl1[0] == 3) 49 | 50 | *psl += 2 51 | assert(sl1[0] == 5) 52 | 53 | sl1p := &sl1 54 | (*sl1p)[0] = 8 55 | assert(sl1[0] == 8) 56 | 57 | *sl1p = []int{111,222} 58 | assert(sl1[0] == 111) 59 | assert(sl1[1] == 222) 60 | 61 | { 62 | sl1 := [2]int{1,2} 63 | sl2 := sl1 64 | assert(sl2[0] == 1) 65 | i := 3 66 | psl := &sl2[i-3] 67 | *psl = 3 68 | assert(sl1[0] == 1) 69 | assert(sl2[0] == 3) 70 | 71 | *psl += 2 72 | assert(sl2[0] == 5) 73 | 74 | sl1p := &sl1 75 | (*sl1p)[0] = 8 76 | assert(sl1[0] == 8) 77 | 78 | *sl1p = [2]int{111,222} 79 | assert(sl1[0] == 111) 80 | assert(sl1[1] == 222) 81 | 82 | } 83 | 84 | var m1 = map[int]int{1:2, 3: 888} 85 | m1p := &m1 86 | (*m1p)[3] = 88 87 | assert(m1[3] == 88) 88 | 89 | { 90 | var d = &pkgVarA 91 | *d = 2 92 | assert(pkgVarA == 2) 93 | 94 | /* 95 | var e = &math.PI 96 | assert(math.V1 == 1) 97 | *e = 2 98 | assert(math.V1 == 2) 99 | */ 100 | } 101 | } 102 | 103 | 104 | func ret() *int { 105 | i := 666 106 | return &i 107 | } 108 | 109 | 110 | func ret2() (*int, *int) { 111 | i := 666 112 | return &i, &i 113 | } 114 | 115 | 116 | func test_ret2() { 117 | i, j := ret2() 118 | *i = 888 119 | assert(*j == 888) 120 | } 121 | 122 | 123 | func main() { 124 | a := 1 125 | b := &a 126 | assert(*b + 1 == 2) 127 | 128 | assert(*ret() + 1 == 667) 129 | 130 | test() 131 | 132 | test_ret2() 133 | } -------------------------------------------------------------------------------- /components/layout.js: -------------------------------------------------------------------------------- 1 | import Head from 'next/head'; 2 | import Image from 'next/image'; 3 | import styles from './layout.module.css'; 4 | import utilStyles from '../styles/utils.module.css'; 5 | import Link from 'next/link'; 6 | import Navbar from 'react-bootstrap/Navbar' 7 | import Nav from 'react-bootstrap/Nav' 8 | import Container from 'react-bootstrap/Container' 9 | 10 | const name = 'Goscript'; 11 | export const siteTitle = 'Goscript offical website'; 12 | 13 | export default function Layout({ children, home }) { 14 | return ( 15 |
16 | 17 | 18 | 22 | 28 | 29 | 30 | 31 | 32 | 33 | goscript logo 40 | 41 | 42 | 47 | 48 | 49 | 50 |
{children}
51 | {!home && ( 52 | <> 53 |
54 | 55 | ← Back to home 56 | 57 |
58 | 59 | ) 60 | } 61 |
62 | ); 63 | } -------------------------------------------------------------------------------- /lib/posts.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import path from 'path'; 3 | import matter from 'gray-matter'; 4 | import { remark } from 'remark'; 5 | import prism from 'remark-prism'; 6 | import gfm from 'remark-gfm' 7 | import html from 'remark-html'; 8 | 9 | const postsDirectory = path.join(process.cwd(), 'posts'); 10 | 11 | export function getSortedPostsData() { 12 | // Get file names under /posts 13 | const fileNames = fs.readdirSync(postsDirectory).filter(s => s.endsWith('_en.md')); 14 | const allPostsData = fileNames.map((fileName) => { 15 | // Remove ".md" from file name to get id 16 | const id = fileName.replace(/\.md$/, ''); 17 | 18 | // Read markdown file as string 19 | const fullPath = path.join(postsDirectory, fileName); 20 | const fileContents = fs.readFileSync(fullPath, 'utf8'); 21 | 22 | // Use gray-matter to parse the post metadata section 23 | const matterResult = matter(fileContents); 24 | 25 | // Combine the data with the id 26 | return { 27 | id, 28 | ...matterResult.data, 29 | }; 30 | }); 31 | // Sort posts by date 32 | return allPostsData.sort(({ date: a }, { date: b }) => { 33 | if (a < b) { 34 | return 1; 35 | } else if (a > b) { 36 | return -1; 37 | } else { 38 | return 0; 39 | } 40 | }); 41 | } 42 | 43 | export function getAllPostIds() { 44 | const fileNames = fs.readdirSync(postsDirectory); 45 | 46 | // Returns an array that looks like this: 47 | // [ 48 | // { 49 | // params: { 50 | // id: 'ssg-ssr' 51 | // } 52 | // }, 53 | // { 54 | // params: { 55 | // id: 'pre-rendering' 56 | // } 57 | // } 58 | // ] 59 | return fileNames.map((fileName) => { 60 | return { 61 | params: { 62 | id: fileName.replace(/\.md$/, ''), 63 | }, 64 | }; 65 | }); 66 | } 67 | 68 | export async function getPostData(id) { 69 | const fullPath = path.join(postsDirectory, `${id}.md`); 70 | const fileContents = fs.readFileSync(fullPath, 'utf8'); 71 | 72 | // Use gray-matter to parse the post metadata section 73 | const matterResult = matter(fileContents); 74 | 75 | // Use remark to convert markdown into HTML string 76 | const processedContent = await remark() 77 | .use(prism) 78 | .use(gfm) 79 | .use(html, { sanitize: false }) 80 | .process(matterResult.content); 81 | const contentHtml = processedContent.toString(); 82 | 83 | // Combine the data with the id and contentHtml 84 | return { 85 | id, 86 | contentHtml, 87 | ...matterResult.data, 88 | }; 89 | } -------------------------------------------------------------------------------- /go-code/slice1.gos: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | 4 | func a() bool { 5 | s := []int{8,8} 6 | s[1] -= 9 7 | k := s[1] == -1 8 | return k 9 | } 10 | 11 | func f2() { 12 | var j int 13 | var s = []int{10, 20} 14 | var s2 = []int{100, 200} 15 | for _, v := range s { 16 | j += v 17 | for _, v2 := range s2{ 18 | j += v2 19 | for _, v := range s { 20 | j += v 21 | } 22 | } 23 | } 24 | assert(j == 750) 25 | } 26 | 27 | 28 | func slice_slice() { 29 | s := []int{1,2,3,4} 30 | s1 := s[:1] 31 | assert(len(s1) == 1) 32 | assert(cap(s1) == 4) 33 | 34 | s2 := s[1:] 35 | assert(len(s2) == 3) 36 | assert(cap(s2) == 3) 37 | 38 | s3 := s[1:2:2] 39 | assert(s3[0] == 2) 40 | assert(len(s3) == 1) 41 | assert(cap(s3) == 1) 42 | 43 | s4 := s[1:2:3] 44 | assert(cap(s4) == 2) 45 | 46 | // index out of range 47 | //s4 = s[1:2:11] 48 | //assert(cap(s4) == 10) 49 | } 50 | 51 | func append_slice() { 52 | m := []byte{1,3} 53 | n := []byte{2,4} 54 | t := append(m, n...) 55 | assert(t[0] == 1) 56 | assert(t[2] == 2) 57 | 58 | s := "what" 59 | s1 := append(m, s...) 60 | assert(s1[2] == 'w') 61 | assert(s1[3] == 'h') 62 | assert(s1[4] == 'a') 63 | assert(s1[5] == 't') 64 | } 65 | 66 | func copy_slice() { 67 | m := []byte{1,2,3,4} 68 | n := []byte{66,77} 69 | t := m[1:4] 70 | count := copy(t, n) 71 | assert(count == 2) 72 | assert(t[0] == 66) 73 | assert(t[1] == 77) 74 | assert(t[2] == 4) 75 | 76 | t2 := m[:1] 77 | count = copy(t2, n) 78 | assert(count == 1) 79 | assert(t2[0] == 66) 80 | assert(t2[1] == 66) 81 | assert(t2[2] == 77) 82 | 83 | count = copy(t2, "what") 84 | assert(count == 1) 85 | assert(t2[0] == 'w') 86 | assert(t2[1] == 66) 87 | } 88 | 89 | 90 | 91 | func copy_no_return() { 92 | s := "/a" 93 | buf := make([]byte, 3) 94 | copy(buf, s[:1]) 95 | assert(buf[0] == '/') 96 | } 97 | 98 | 99 | func appendToNil() { 100 | var a []int 101 | b := []int{6,6,6} 102 | a = append(a, b...) 103 | a[0] = 123 104 | assert(a[0] == 123) 105 | assert(b[0] == 6) 106 | } 107 | 108 | 109 | 110 | func main() { 111 | var s1 = [][]int{{0},{99},{2}} 112 | var s2 = []int{0,100,2} 113 | i := s1[1][0] + s2[1] - 1 114 | s2[0] = 8 115 | j := s2[0] 116 | assert(i == 198) 117 | assert(j == 8) 118 | assert(a()) 119 | 120 | f2() 121 | 122 | slice_slice() 123 | 124 | append_slice() 125 | 126 | copy_slice() 127 | 128 | copy_no_return() 129 | 130 | appendToNil() 131 | } -------------------------------------------------------------------------------- /goscript/src/lib.rs: -------------------------------------------------------------------------------- 1 | use goscript_engine as engine; 2 | use std::borrow::Cow; 3 | use std::io::prelude::*; 4 | use std::io::{Cursor, Result}; 5 | use std::path::PathBuf; 6 | use std::sync::{Arc, Mutex}; 7 | use wasm_bindgen::prelude::*; 8 | 9 | #[wasm_bindgen] 10 | pub struct RunResult { 11 | out: String, 12 | err: String, 13 | compile_err: String, 14 | debug: String, 15 | } 16 | 17 | #[wasm_bindgen] 18 | impl RunResult { 19 | pub fn new(out: String, err: String, compile_err: String, debug: String) -> RunResult { 20 | RunResult { 21 | out, 22 | err, 23 | compile_err, 24 | debug, 25 | } 26 | } 27 | 28 | #[wasm_bindgen(getter)] 29 | pub fn out(&self) -> String { 30 | self.out.clone() 31 | } 32 | 33 | #[wasm_bindgen(getter)] 34 | pub fn err(&self) -> String { 35 | self.err.clone() 36 | } 37 | 38 | #[wasm_bindgen(getter)] 39 | pub fn compile_err(&self) -> String { 40 | self.compile_err.clone() 41 | } 42 | 43 | #[wasm_bindgen(getter)] 44 | pub fn debug(&self) -> String { 45 | self.debug.clone() 46 | } 47 | } 48 | 49 | #[wasm_bindgen] 50 | pub fn run_zip_and_string(zip: Vec, source: &str) -> RunResult { 51 | let mut cfg = engine::Config::default(); 52 | let out_buf = WriteBuf::new(); 53 | let err_buf = WriteBuf::new(); 54 | cfg.std_out = Some(Box::new(out_buf.clone())); 55 | cfg.std_err = Some(Box::new(err_buf.clone())); 56 | let (sr, path) = engine::SourceReader::zip_lib_and_string( 57 | std::borrow::Cow::Owned(zip), 58 | PathBuf::from("std/"), 59 | Cow::Owned(source.to_owned()), 60 | ); 61 | let result = engine::run(cfg, &sr, &path); 62 | let debug = "".to_owned(); 63 | match result { 64 | Ok(_) => RunResult::new( 65 | out_buf.into_string(), 66 | err_buf.into_string(), 67 | "".to_owned(), 68 | debug, 69 | ), 70 | Err(e) => RunResult::new("".to_owned(), "".to_owned(), format!("{}", e), debug), 71 | } 72 | } 73 | 74 | #[derive(Clone)] 75 | struct WriteBuf { 76 | buffer: Arc>>>, 77 | } 78 | 79 | impl WriteBuf { 80 | fn new() -> WriteBuf { 81 | WriteBuf { 82 | buffer: Arc::new(Mutex::new(Cursor::new(vec![]))), 83 | } 84 | } 85 | 86 | fn into_string(self) -> String { 87 | String::from_utf8_lossy(self.buffer.lock().unwrap().get_ref()).into_owned() 88 | } 89 | } 90 | 91 | impl Write for WriteBuf { 92 | fn write(&mut self, buf: &[u8]) -> Result { 93 | log(&format!("write: {}", String::from_utf8_lossy(buf))); 94 | self.buffer.lock().unwrap().write(buf) 95 | } 96 | 97 | fn flush(&mut self) -> Result<()> { 98 | log("flush"); 99 | Ok(()) 100 | } 101 | } 102 | 103 | #[wasm_bindgen] 104 | extern "C" { 105 | // Use `js_namespace` here to bind `console.log(..)` instead of just 106 | // `log(..)` 107 | #[wasm_bindgen(js_namespace = console)] 108 | fn log(s: &str); 109 | 110 | // The `console.log` is quite polymorphic, so we can bind it with multiple 111 | // signatures. Note that we need to use `js_name` to ensure we always call 112 | // `log` in JS. 113 | #[wasm_bindgen(js_namespace = console, js_name = log)] 114 | fn log_u32(a: u32); 115 | 116 | // Multiple arguments too! 117 | #[wasm_bindgen(js_namespace = console, js_name = log)] 118 | fn log_many(a: &str, b: &str); 119 | } 120 | -------------------------------------------------------------------------------- /components/playground.js: -------------------------------------------------------------------------------- 1 | import React, { useRef, useState } from 'react'; 2 | import { getEmbedded } from '../lib/embedded'; 3 | import { runGo } from '../lib/runGo'; 4 | import Editor from "@monaco-editor/react"; 5 | import DropdownButton from 'react-bootstrap/DropdownButton' 6 | import Dropdown from 'react-bootstrap/Dropdown' 7 | import Button from 'react-bootstrap/Button' 8 | import ButtonGroup from 'react-bootstrap/ButtonGroup' 9 | 10 | 11 | export default function Playground({ embedded, code, codeIds }) { 12 | const editorRef = useRef(null); 13 | const [message, setMessage] = useState(''); 14 | 15 | function handleEditorDidMount(editor, monaco) { 16 | editorRef.current = editor; 17 | 18 | if (typeof code === "undefined") { 19 | let codeId = embedded; 20 | if (typeof codeId === "undefined") { 21 | codeId = "fibonacci.go" 22 | } 23 | loadEmbedded(codeId) 24 | } else { 25 | editorRef.current?.setValue(code) 26 | } 27 | } 28 | 29 | async function handleRunClick() { 30 | const code = editorRef.current?.getValue(); 31 | let result = await runGo(code); 32 | let re = result.out + result.err + result.compile_err 33 | setMessage(re); 34 | } 35 | 36 | function loadEmbedded(id) { 37 | const codeData = getEmbedded()[id]; 38 | editorRef.current?.setValue(codeData) 39 | } 40 | 41 | async function handleLoadClick(e) { 42 | loadEmbedded(e.target.value) 43 | } 44 | 45 | async function handleLoadSelect(key, e) { 46 | loadEmbedded(key); 47 | } 48 | 49 | return ( 50 | <> 51 |

52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | {codeIds.map(({ params }) => ( 62 | 63 | {params.id} 64 | 65 | ))} 66 | 67 | 68 |

69 |

Tip: Click RUN to run the code in your browser. The code is editable!

74 | 84 |

85 | 87 | 88 | ); 89 | } -------------------------------------------------------------------------------- /styles/utils.prism.css: -------------------------------------------------------------------------------- 1 | /** 2 | * VS theme by Andrew Lock (https://andrewlock.net) 3 | * Inspired by Visual Studio syntax coloring 4 | */ 5 | 6 | code[class*="language-"], 7 | pre[class*="language-"] { 8 | color: #393A34; 9 | direction: ltr; 10 | text-align: left; 11 | white-space: pre-wrap; 12 | word-spacing: normal; 13 | word-break: normal; 14 | font-size: .9em; 15 | line-height: 1.2em; 16 | 17 | -moz-tab-size: 4; 18 | -o-tab-size: 4; 19 | tab-size: 4; 20 | 21 | -webkit-hyphens: none; 22 | -moz-hyphens: none; 23 | -ms-hyphens: none; 24 | hyphens: none; 25 | } 26 | 27 | pre>code[class*="language-"] { 28 | font-size: 1em; 29 | } 30 | 31 | pre[class*="language-"]::-moz-selection, 32 | pre[class*="language-"] ::-moz-selection, 33 | code[class*="language-"]::-moz-selection, 34 | code[class*="language-"] ::-moz-selection { 35 | background: #C1DEF1; 36 | } 37 | 38 | pre[class*="language-"]::selection, 39 | pre[class*="language-"] ::selection, 40 | code[class*="language-"]::selection, 41 | code[class*="language-"] ::selection { 42 | background: #C1DEF1; 43 | } 44 | 45 | /* Code blocks */ 46 | pre[class*="language-"] { 47 | padding: 1em; 48 | margin: .5em 0; 49 | overflow: auto; 50 | border: 1px solid #dddddd; 51 | background-color: white; 52 | } 53 | 54 | /* Inline code */ 55 | :not(pre)>code[class*="language-"] { 56 | padding: .2em; 57 | padding-top: 1px; 58 | padding-bottom: 1px; 59 | background: #f8f8f8; 60 | border: 1px solid #dddddd; 61 | } 62 | 63 | .token.comment, 64 | .token.prolog, 65 | .token.doctype, 66 | .token.cdata { 67 | color: #008000; 68 | font-style: italic; 69 | } 70 | 71 | .token.namespace { 72 | opacity: .7; 73 | } 74 | 75 | .token.string { 76 | color: #A31515; 77 | } 78 | 79 | .token.punctuation, 80 | .token.operator { 81 | color: #393A34; 82 | /* no highlight */ 83 | } 84 | 85 | .token.url, 86 | .token.symbol, 87 | .token.number, 88 | .token.boolean, 89 | .token.variable, 90 | .token.constant, 91 | .token.inserted { 92 | color: #36acaa; 93 | } 94 | 95 | .token.atrule, 96 | .token.keyword, 97 | .token.attr-value, 98 | .language-autohotkey .token.selector, 99 | .language-json .token.boolean, 100 | .language-json .token.number, 101 | code[class*="language-css"] { 102 | color: #0000ff; 103 | } 104 | 105 | .token.function { 106 | color: #393A34; 107 | } 108 | 109 | .token.deleted, 110 | .language-autohotkey .token.tag { 111 | color: #9a050f; 112 | } 113 | 114 | .token.selector, 115 | .language-autohotkey .token.keyword { 116 | color: #00009f; 117 | } 118 | 119 | .token.important { 120 | color: #e90; 121 | } 122 | 123 | .token.important, 124 | .token.bold { 125 | font-weight: bold; 126 | } 127 | 128 | .token.italic { 129 | font-style: italic; 130 | } 131 | 132 | .token.class-name, 133 | .language-json .token.property { 134 | color: #2B91AF; 135 | } 136 | 137 | .token.tag, 138 | .token.selector { 139 | color: #800000; 140 | } 141 | 142 | .token.attr-name, 143 | .token.property, 144 | .token.regex, 145 | .token.entity { 146 | color: #ff0000; 147 | } 148 | 149 | .token.directive.tag .tag { 150 | background: #ffff00; 151 | color: #393A34; 152 | } 153 | 154 | /* overrides color-values for the Line Numbers plugin 155 | * http://prismjs.com/plugins/line-numbers/ 156 | */ 157 | .line-numbers.line-numbers .line-numbers-rows { 158 | border-right-color: #a5a5a5; 159 | } 160 | 161 | .line-numbers .line-numbers-rows>span:before { 162 | color: #2B91AF; 163 | } 164 | 165 | /* overrides color-values for the Line Highlight plugin 166 | * http://prismjs.com/plugins/line-highlight/ 167 | */ 168 | .line-highlight.line-highlight { 169 | background: rgba(193, 222, 241, 0.2); 170 | background: -webkit-linear-gradient(left, rgba(193, 222, 241, 0.2) 70%, rgba(221, 222, 241, 0)); 171 | background: linear-gradient(to right, rgba(193, 222, 241, 0.2) 70%, rgba(221, 222, 241, 0)); 172 | } -------------------------------------------------------------------------------- /go-code/operations.gos: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | 6 | func shortCircuit() bool { 7 | i := 1 8 | assert(i == 0) 9 | return true 10 | } 11 | 12 | func shortCircuit2() BOOL_ { 13 | i := 1 14 | assert(i == 0) 15 | return BOOL_(true) 16 | } 17 | 18 | func test1() { 19 | var i, j = 80, 8 20 | assert( i + j == 88) 21 | assert( i - j == 72) 22 | assert( i * j == 640) 23 | assert( i / j == 10) 24 | assert( i % j == 0) 25 | var k = i 26 | k += j 27 | assert(k == i + j) 28 | k = i 29 | k -= j 30 | assert(k == i - j) 31 | k = i 32 | k *= j 33 | assert(k == i * j) 34 | k = i 35 | k /= j 36 | assert(k == i / j) 37 | k = i 38 | k %= j 39 | assert(k == i % j) 40 | 41 | { 42 | var i, j = 80.0, 0.1 43 | assert( i + j == 80.1) 44 | assert( i - j == 79.9) 45 | assert( i * j == 8.0) 46 | assert( i / j == 800.0) 47 | var k = i 48 | k += j 49 | assert(k == i + j) 50 | k = i 51 | k -= j 52 | assert(k == i - j) 53 | k = i 54 | k *= j 55 | assert(k == i * j) 56 | k = i 57 | k /= j 58 | assert(k == i / j) 59 | } 60 | 61 | { 62 | type S1 struct {i, j int} 63 | var s S1 64 | s.i = 1 65 | s.j = 2000000000 66 | s.i-- 67 | s.j++ 68 | assert(s.i == 0) 69 | assert(s.j == 2000000001) 70 | } 71 | 72 | { 73 | var a = [3]int{1, 8} 74 | 75 | f := uint(2) 76 | i := 1 <>f 78 | a[0] <<= 2 79 | a[1] >>= 2 80 | assert(i == 4) 81 | assert(j == 2) 82 | assert(a[0] == 4) 83 | assert(a[1] == 2) 84 | 85 | } 86 | 87 | { 88 | i := 1 89 | i ++ 90 | assert(i == 2) 91 | 92 | type S1 struct {i, j int} 93 | var s S1 94 | s.i++ 95 | assert(s.i == 1) 96 | 97 | p := &i; 98 | *p++ 99 | assert(i == 3) 100 | } 101 | 102 | { 103 | t := true 104 | f := false 105 | 106 | assert(!(t&&f)) 107 | assert(t&&t) 108 | assert(t&&t||f) 109 | assert(!(t&&t&&f)) 110 | 111 | assert(t||shortCircuit()) 112 | assert(!(f&&shortCircuit())) 113 | } 114 | } 115 | 116 | type INT_ int 117 | type BOOL_ bool 118 | type FLOAT_ float32 119 | 120 | func test2() { 121 | var i, j INT_ = 80, 8 122 | fmt.Println(i + j) 123 | assert (i != j) 124 | assert (i == j + 72) 125 | assert( i + j == 88) 126 | assert( i - j == 72) 127 | assert( i * j == 640) 128 | assert( i / j == 10) 129 | assert( i % j == 0) 130 | var k = i 131 | k += j 132 | assert(k == i + j) 133 | k = i 134 | k -= j 135 | assert(k == i - j) 136 | k = i 137 | k *= j 138 | assert(k == i * j) 139 | k = i 140 | k /= j 141 | assert(k == i / j) 142 | k = i 143 | k %= j 144 | assert(k == i % j) 145 | 146 | { 147 | var i, j FLOAT_ = 80.0, 0.1 148 | assert( i + j == 80.1) 149 | assert( i - j == 79.9) 150 | assert( i * j == 8.0) 151 | assert( i / j == 800.0) 152 | var k = i 153 | k += j 154 | assert(k == i + j) 155 | k = i 156 | k -= j 157 | assert(k == i - j) 158 | k = i 159 | k *= j 160 | assert(k == i * j) 161 | k = i 162 | k /= j 163 | assert(k == i / j) 164 | } 165 | 166 | { 167 | type S1 struct {i, j INT_} 168 | var s S1 169 | s.i = 1 170 | s.j = 2000000000 171 | s.i-- 172 | s.j++ 173 | assert(s.i == 0) 174 | assert(s.j == 2000000001) 175 | } 176 | 177 | { 178 | var a = [3]INT_{1, 8} 179 | 180 | f := uint(2) 181 | i := INT_(1) <>f 183 | a[0] <<= 2 184 | a[1] >>= 2 185 | assert(i == 4) 186 | assert(j == 2) 187 | assert(a[0] == 4) 188 | assert(a[1] == 2) 189 | 190 | } 191 | 192 | { 193 | i := INT_(1) 194 | i ++ 195 | assert(i == 2) 196 | 197 | type S1 struct {i, j INT_} 198 | var s S1 199 | s.i++ 200 | assert(s.i == 1) 201 | 202 | p := &i; 203 | *p++ 204 | assert(i == 3) 205 | } 206 | 207 | { 208 | var t BOOL_ = true 209 | var f BOOL_ = false 210 | 211 | assert(!(t&&f)) 212 | assert(t&&t) 213 | assert(t&&t||f) 214 | assert(!(t&&t&&f)) 215 | 216 | assert(t||shortCircuit2()) 217 | assert(!(f&&shortCircuit2())) 218 | } 219 | } 220 | 221 | func main() { 222 | 223 | 224 | test1() 225 | test2() 226 | } -------------------------------------------------------------------------------- /go-code/reflect.gos: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | ) 7 | 8 | 9 | type AAA int 10 | 11 | var i = 222 12 | 13 | type Bbb struct { 14 | A string 15 | B int 16 | C int 17 | D bool 18 | } 19 | 20 | type Ccc struct { 21 | I int 22 | I8 int8 23 | I16 int16 24 | I32 int32 25 | I64 int64 26 | U uint 27 | U8 uint8 28 | U16 uint16 29 | U32 uint32 30 | U64 uint64 31 | F32 float32 32 | F64 float64 33 | } 34 | 35 | func get() AAA { 36 | return AAA(i) 37 | } 38 | 39 | 40 | func get2() AAA { 41 | return AAA(6*3) 42 | } 43 | 44 | func testBytes() { 45 | a := []byte{111,112,113} 46 | v := reflect.ValueOf(a); 47 | b := v.Bytes() 48 | assert(a[0] == b[0]) 49 | assert(a[1] == b[1]) 50 | assert(a[2] == b[2]) 51 | } 52 | 53 | func testValueElem() { 54 | t := 8 55 | i := &t 56 | v := reflect.ValueOf(i) 57 | re := v.Elem() 58 | fmt.Println(v, re) 59 | } 60 | 61 | func testField() { 62 | b := Bbb{"xxx", 123, 0, false} 63 | v := reflect.ValueOf(b) 64 | assert(v.NumField() == 4) 65 | // assert(v.Field(1).Int() == 123) 66 | fmt.Println(v, v.Field(1).Int()) 67 | } 68 | 69 | func testVals() { 70 | var i int16 = 555 71 | assert(reflect.ValueOf(i).Int() == 555) 72 | 73 | var j uint32 = 666 74 | assert(reflect.ValueOf(j).Uint() == 666) 75 | 76 | var k float32 = 3.5 77 | assert(reflect.ValueOf(k).Float() == 3.5) 78 | } 79 | 80 | func testIndex() { 81 | a := []int{23,34,56} 82 | v := reflect.ValueOf(a) 83 | assert(v.Index(1).Int() == 34) 84 | 85 | b := "abcd" 86 | v = reflect.ValueOf(b) 87 | assert(v.Index(1).Uint() == 'b') 88 | 89 | c := [3]int{23,34,56} 90 | v = reflect.ValueOf(c) 91 | assert(v.Index(2).Int() == 56) 92 | 93 | } 94 | 95 | func testIsNil() { 96 | var m map[int]string 97 | var s []string 98 | assert(reflect.ValueOf(m).IsNil()) 99 | assert(reflect.ValueOf(s).IsNil()) 100 | } 101 | 102 | 103 | func testIsValid() { 104 | var m map[int]string 105 | v := reflect.ValueOf(m) 106 | var v1 reflect.Value 107 | assert(v.IsValid()) 108 | assert(!v1.IsValid()) 109 | } 110 | 111 | func testLen() { 112 | m := map[int]string{1:"a"} 113 | var s []int 114 | v := reflect.ValueOf(m) 115 | v1 := reflect.ValueOf(s) 116 | assert(v.Len() == 1) 117 | assert(v1.Len() == 0) 118 | } 119 | 120 | func testMapIter() { 121 | m := map[int]string{66:"a"} 122 | v := reflect.ValueOf(m) 123 | iter := v.MapRange() 124 | assert(iter.Next()) 125 | assert(iter.Key().Int() == 66) 126 | assert(!iter.Next()) 127 | } 128 | 129 | func testPointer() { 130 | var i uintptr 131 | i <<= 1 132 | i += 1 133 | i <<= 3 134 | var j int 135 | j = 9 136 | i = (uintptr)(j) 137 | f := float32(i) 138 | assert(f == 9) 139 | 140 | fmt.Println(reflect.ValueOf(i).Pointer()) 141 | } 142 | 143 | func testSet() { 144 | assert(!reflect.ValueOf("").CanSet()) 145 | 146 | var bbb Bbb 147 | bv := reflect.ValueOf(bbb); 148 | bv0 := bv.Field(0) 149 | bv2 := bv.Field(2) 150 | bv3 := bv.Field(3) 151 | assert(bv0.CanSet()) 152 | assert(bv2.CanSet()) 153 | assert(bv3.CanSet()) 154 | assert(!bv3.Bool()) 155 | bv3.SetBool(true) 156 | assert(bv3.Bool()) 157 | 158 | a := []int{23,34,56} 159 | v := reflect.ValueOf(a) 160 | v1 := v.Index(1) 161 | assert(v1.CanSet()) 162 | 163 | v1.Set(reflect.ValueOf(888)) 164 | assert(v.Index(1).Int() == 888) 165 | 166 | bv0.SetString("kkk") 167 | assert(bbb.A == "") 168 | 169 | { 170 | b := new([]byte) 171 | v := reflect.ValueOf(b).Elem() 172 | v.SetBytes([]byte{1,239,3}) 173 | assert((*b)[1] == 239) 174 | } 175 | 176 | {/* 177 | native := ffi(ffiReflect, "reflect") 178 | i := 1 179 | j := 2 180 | pi := native.value_of(i) 181 | ppi := native.value_of(pi) 182 | pj := native.value_of(j) 183 | native.set_pointer(ppi, pj) 184 | */ 185 | } 186 | 187 | { 188 | var ccc Ccc 189 | v := reflect.ValueOf(ccc); 190 | for i := 0; i < 5; i++ { 191 | cf := v.Field(i) 192 | cf.SetInt(8) 193 | assert(cf.Int() == 8) 194 | } 195 | for i := 5; i < 10; i++ { 196 | cf := v.Field(i) 197 | cf.SetUint(9) 198 | assert(cf.Uint() == 9) 199 | } 200 | for i := 10; i < 12; i++ { 201 | cf := v.Field(i) 202 | cf.SetFloat(10) 203 | assert(cf.Float() == 10) 204 | } 205 | 206 | var i interface{} = ccc.I16 207 | _, ok := i.(int16) 208 | assert(ok) 209 | i = ccc.U32 210 | _, ok = i.(uint32) 211 | assert(ok) 212 | i = ccc.F32 213 | _, ok = i.(float32) 214 | assert(ok) 215 | } 216 | 217 | } 218 | 219 | 220 | func main() { 221 | 222 | testBytes() 223 | 224 | testValueElem() 225 | 226 | testField() 227 | 228 | testVals() 229 | 230 | testIndex() 231 | 232 | testIsNil() 233 | 234 | testIsValid() 235 | 236 | testLen() 237 | 238 | testMapIter() 239 | 240 | testPointer() 241 | 242 | testSet() 243 | 244 | 245 | //i := reflect.TypeOf(get) 246 | //j := reflect.TypeOf(get2) 247 | //assert(i == j) 248 | //fmt.Println(get2(), get(), i, j, i == j, reflect.ValueOf(get).Pointer()) 249 | 250 | } -------------------------------------------------------------------------------- /go-code/for.gos: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | 4 | import "fmt" 5 | 6 | 7 | func f1() { 8 | var j int 9 | var s = map[int]int{123:10, 258:20} 10 | var s2 = map[int]int{1:100, 2:200} 11 | for _, v := range s { 12 | j += v 13 | for _, v2 := range s2{ 14 | j += v2 15 | for _, v := range s { 16 | j += v 17 | } 18 | } 19 | } 20 | assert(j == 750) 21 | } 22 | 23 | 24 | func f2() { 25 | j := 0 26 | for i := 0; i < 1000; i++ { 27 | j += 1 28 | } 29 | assert(j == 1000) 30 | } 31 | 32 | 33 | func f3() { 34 | i := 0 35 | i += 'a' 36 | i += 'b' 37 | i += 'c' 38 | i += 'd' 39 | var j, k, j2, k2 int 40 | for _, c := range "abcd" { 41 | j += int(c) 42 | } 43 | for i := range "abcd" { 44 | k += i 45 | } 46 | for i, c := range "abcd" { 47 | j2 += int(c) 48 | k2 += i 49 | } 50 | 51 | assert(i == j) 52 | assert(6 == k) 53 | assert(j2 == j) 54 | assert(k2 == k) 55 | } 56 | 57 | 58 | func range_set() { 59 | s := []int{1, 2} 60 | for i, _ := range s { 61 | s[i] = 5 62 | } 63 | assert(s[0] == 5) 64 | 65 | var s1 = map[int]int{1:2, 3: 888} 66 | for i, _ := range s1 { 67 | s1[i] = 100 68 | } 69 | assert(s1[0] == 0) 70 | assert(s1[1] == 100) 71 | } 72 | 73 | func break_cont() { 74 | j := 0 75 | for i := 0; i < 1000; i++ { 76 | if i >= 10 { 77 | break 78 | } 79 | j += 1 80 | } 81 | assert(j == 10) 82 | 83 | j = 0 84 | for i := 0; i < 10; i++ { 85 | j += 1 86 | if i >= 6 { 87 | continue 88 | } 89 | j -= 1 90 | } 91 | assert(j == 4) 92 | } 93 | 94 | func break_cont_2() { 95 | j := 0 96 | s := []int{3,3,3,3,3} 97 | for i, r := range s { 98 | if i >= 4 { 99 | break 100 | } 101 | j += r 102 | } 103 | assert(j == 3 * 4) 104 | 105 | j = 0 106 | k := 0 107 | for i, r := range s { 108 | k = i 109 | if i >= 2 { 110 | continue 111 | } 112 | j += r 113 | } 114 | assert(j == 3 * 2) 115 | assert(k == 4) 116 | 117 | } 118 | 119 | func break_cont_3() { 120 | j := 0 121 | for i := 0; i < 100; { 122 | i += 1 123 | if i >= 10 { 124 | continue 125 | } 126 | j += 1 127 | } 128 | assert(j == 10 - 1) 129 | 130 | 131 | j = 0 132 | a, b := 60, 80 133 | for i := 0; i < 100000; i++ { 134 | j += 1 135 | if i >= a { 136 | if i >= b { 137 | break 138 | } 139 | continue 140 | } 141 | j -= 1 142 | } 143 | assert(j == b - a + 1) 144 | } 145 | 146 | 147 | func break_label() { 148 | total := 0 149 | OuterLoop: 150 | for i := 0; i < 10; i++ { 151 | for j := 0; j < 10; j++ { 152 | total += 1 153 | if j == 5 { 154 | break OuterLoop 155 | } 156 | } 157 | } 158 | fmt.Println(total) 159 | assert(total == 6) 160 | 161 | 162 | total = 0 163 | OuterLoop2: 164 | for i := 0; i < 10; i++ { 165 | for j := 0; j < 10; j++ { 166 | total += 1 167 | if j == 5 { 168 | continue OuterLoop2 169 | } 170 | } 171 | } 172 | fmt.Println(total) 173 | assert(total == 60) 174 | 175 | total = 0 176 | for i := 0; i < 10; i++ { 177 | for j := 0; j < 10; j++ { 178 | total += 1 179 | if j == 5 { 180 | break 181 | } 182 | } 183 | } 184 | fmt.Println(total) 185 | assert(total == 60) 186 | 187 | total = 0 188 | for i := 0; i < 10; i++ { 189 | for j := 0; j < 10; j++ { 190 | total += 1 191 | if j == 5 { 192 | continue 193 | } 194 | } 195 | } 196 | fmt.Println(total) 197 | assert(total == 100) 198 | } 199 | 200 | func break_select() { 201 | quit := make(chan int, 1) 202 | total := 0 203 | 204 | quit <- 888 205 | 206 | FOR1: 207 | for i := 0; i < 10; i++ { 208 | total += 3 209 | select { 210 | case v, ok := <-quit: 211 | assert(v == 888) 212 | assert(ok) 213 | fmt.Println("quit recv:", v, ok) 214 | break 215 | default: 216 | fmt.Println("default") 217 | break FOR1 218 | } 219 | } 220 | assert(total == 2 * 3) 221 | } 222 | 223 | func break_switch() { 224 | total := 0 225 | 226 | FOR1: 227 | for i := 0; i < 10; i++ { 228 | total += 3 229 | switch total { 230 | case 3: 231 | assert(total == 3) 232 | fmt.Println("case 3") 233 | break 234 | case 106: 235 | assert(total == 106) 236 | fmt.Println("case 106") 237 | continue 238 | default: 239 | fmt.Println("default") 240 | break FOR1 241 | } 242 | total += 100 243 | } 244 | assert(total == 109) 245 | } 246 | 247 | func range_array() { 248 | s2 := [3]string{"a", "b", "c"} 249 | t := "" 250 | for _, s := range s2 { 251 | t += s 252 | } 253 | assert(t == "abc") 254 | } 255 | 256 | 257 | func main() { 258 | f1() 259 | f2() 260 | f3() 261 | 262 | range_set() 263 | 264 | range_array() 265 | 266 | break_cont() 267 | break_cont_2() 268 | 269 | break_label() 270 | 271 | break_select() 272 | 273 | break_switch() 274 | } -------------------------------------------------------------------------------- /lib/embedded.js: -------------------------------------------------------------------------------- 1 | export function getEmbedded() { 2 | return { 3 | "template.go": template, 4 | "fibonacci.go": fibonacci, 5 | "leetcode5.go": leetcode5, 6 | "list.go": list, 7 | "closure.go": closure, 8 | "unicode.go": unicode, 9 | }; 10 | } 11 | 12 | let template = `package main 13 | 14 | //import "fmt" 15 | 16 | func main() { 17 | // click here and start typing! 18 | } 19 | ` 20 | 21 | 22 | const fibonacci = `package main 23 | 24 | import "fmt" 25 | 26 | func fibonacci(c, quit chan int) { 27 | x, y := 0, 1 28 | for { 29 | select { 30 | case c <- x: 31 | x, y = y, x+y 32 | case <-quit: 33 | fmt.Println("quit") 34 | return 35 | } 36 | } 37 | } 38 | 39 | func main() { 40 | c := make(chan int) 41 | quit := make(chan int) 42 | go func() { 43 | for i := 0; i < 12; i++ { 44 | fmt.Println(<-c) 45 | } 46 | quit <- 0 47 | }() 48 | 49 | fibonacci(c, quit) 50 | } 51 | ` 52 | 53 | const leetcode5 = `package main 54 | 55 | import "fmt" 56 | 57 | func longestPalindrome(s string) string { 58 | if len(s) <= 1 { 59 | return s 60 | } 61 | table := make([][]bool, len(s)) 62 | for i := 0; i < len(table); i++ { 63 | table[i] = make([]bool, len(s)) 64 | } 65 | 66 | b := 0 67 | e := 1 68 | for l := 0; l < len(s); l++ { 69 | for i := 0; i < len(s)-l; i++ { 70 | j := i + l 71 | if l == 0 { 72 | table[i][j] = true 73 | } else if l == 1 { 74 | table[i][j] = s[i] == s[j] 75 | } else { 76 | table[i][j] = table[i+1][j-1] && (s[i] == s[j]) 77 | } 78 | if table[i][j] { 79 | b, e = i, j 80 | } 81 | } 82 | } 83 | return s[b : e+1] 84 | } 85 | 86 | func longestPalindrome2(s string) string { 87 | if len(s) <= 1 { 88 | return s 89 | } 90 | table := make([][]int, len(s)) 91 | for i := 0; i < len(table); i++ { 92 | table[i] = make([]int, len(s)) 93 | } 94 | var res string 95 | max := 0 96 | for i, _ := range s { 97 | for j := i; j > -1; j-- { 98 | if s[i] == s[j] && (i-j < 2 || table[i-1][j+1] != 0) { 99 | table[i][j] = 1 100 | } 101 | 102 | if table[i][j] != 0 && (i-j+1) > max { 103 | max = i - j + 1 104 | res = s[j : i+1] 105 | } 106 | } 107 | } 108 | return res 109 | } 110 | 111 | func main() { 112 | s := "ZnVuYyBsb25nZXN0UGFsaW5kcm9tZShzIHN0cmaabbaabbaabbluZykgc3RyaW5nIHsKICAgIGlmIGxlbihzKSA8PSAxIHsKICAgICAgICByZXR1cm4gcwogICAgfQogICAgCiAgICB0YWJsZSA6PaaabbbaaabbbaaaSBtYWtlKFtdW11ib29" 113 | for i := 0; i < 0; i++ { 114 | s = s + s 115 | } 116 | 117 | fmt.Println(len(s)) 118 | result := longestPalindrome2(s) 119 | assert(result == "aaabbbaaabbbaaa") 120 | } 121 | ` 122 | 123 | const list = `package main 124 | 125 | import "fmt" 126 | 127 | type Node struct { 128 | prev *Node 129 | next *Node 130 | key interface{} 131 | } 132 | 133 | type List struct { 134 | head *Node 135 | tail *Node 136 | t1 *Node 137 | t2 *Node 138 | } 139 | 140 | var glist = List{} 141 | 142 | func (L *List) Insert(key interface{}) { 143 | list := &Node{ 144 | next: L.head, 145 | key: key, 146 | } 147 | if L.head != nil { 148 | L.head.prev = list 149 | } 150 | L.head = list 151 | 152 | l := L.head 153 | for l.next != nil { 154 | l = l.next 155 | } 156 | L.tail = l 157 | } 158 | 159 | func (l *List) Display() { 160 | list := l.head 161 | for list != nil { 162 | fmt.Println("%+v ->", list.key) 163 | list = list.next 164 | } 165 | //fmt.Println() 166 | } 167 | 168 | func Display(list *Node) { 169 | for list != nil { 170 | fmt.Println("%v ->", list.key) 171 | list = list.next 172 | } 173 | //fmt.Println() 174 | } 175 | 176 | func ShowBackwards(list *Node) { 177 | for list != nil { 178 | fmt.Println("%v <-", list.key) 179 | list = list.prev 180 | } 181 | //fmt.Println() 182 | } 183 | 184 | func (l *List) Reverse() { 185 | curr := l.head 186 | var prev *Node 187 | l.tail = l.head 188 | 189 | for curr != nil { 190 | next := curr.next 191 | curr.next = prev 192 | prev = curr 193 | curr = next 194 | } 195 | l.head = prev 196 | Display(l.head) 197 | } 198 | 199 | func main() { 200 | 201 | link := List{} 202 | link.Insert(1) 203 | link.Insert(3) 204 | link.Insert(5) 205 | link.Insert(7) 206 | link.Insert(9) 207 | 208 | fmt.Println("\\n==============================\\n") 209 | fmt.Println("Head: %v\\n", link.head.key) 210 | fmt.Println("Tail: %v\\n", link.tail.key) 211 | //fmt.Println("ttt: %v\\n", link.ttt.key) 212 | link.Display() 213 | fmt.Println("\\n==============================\\n") 214 | fmt.Println("head: %v\\n", link.head.key) 215 | fmt.Println("tail: %v\\n", link.tail.key) 216 | link.Reverse() 217 | fmt.Println("\\n==============================\\n") 218 | 219 | // for testing GC 220 | link2 := List{} 221 | link2.Insert(10) 222 | link2.Insert(30) 223 | link2.Insert(50) 224 | link2.Insert(70) 225 | link2.Insert(90) 226 | link2.head.prev = link2.tail 227 | link2.tail.next = link2.head 228 | glist.Insert(2) 229 | glist.Insert(4) 230 | glist.Insert(6) 231 | glist.Insert(8) 232 | glist.Insert(10) 233 | glist.t1 = glist.head 234 | glist.t2 = glist.head 235 | 236 | } 237 | ` 238 | 239 | const closure = `package main 240 | 241 | import "fmt" 242 | 243 | func main() { 244 | a := 44 245 | b := func() func() int { 246 | c := 3 247 | return func()int { 248 | d := 2 249 | return a + 1 + c + d 250 | } 251 | } 252 | e := func() int { 253 | c := b()() + 10 254 | return c + a 255 | } 256 | f := e() 257 | assert(f == 104) 258 | fmt.Println(f) 259 | } 260 | ` 261 | 262 | const unicode = `package main 263 | 264 | import ( 265 | "fmt" 266 | "unicode/utf8" 267 | ) 268 | 269 | func ExampleDecodeLastRune() { 270 | b := []byte("Hello, 世界") 271 | utf8.DecodeLastRune(b) 272 | 273 | 274 | for len(b) > 0 { 275 | r, size := utf8.DecodeLastRune(b) 276 | fmt.Printf("%c %v\\n", r, size) 277 | 278 | b = b[:len(b)-size] 279 | } 280 | 281 | // Output: 282 | // 界 3 283 | // 世 3 284 | // 1 285 | // , 1 286 | // o 1 287 | // l 1 288 | // l 1 289 | // e 1 290 | // H 1 291 | } 292 | 293 | 294 | func main() { 295 | ExampleDecodeLastRune() 296 | fmt.Println("a \\n b👌") 297 | }` 298 | 299 | -------------------------------------------------------------------------------- /posts/goscript_internals_I_overview_zh.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'Goscript 设计原理一: 总览' 3 | date: '2022-07-04' 4 | translation: 'goscript_internals_I_overview_en' 5 | translation_lang: 'English' 6 | --- 7 | 8 | ## 简介 9 | 10 | Goscript是一个用Rust写的,基于虚拟机的Go语言标准实现。Goscript设计原理系列文章的目标是说明其工作机制。目标读者是任何有兴趣了解Goscipt如何工作的人,或者更宽泛地说,任何对编译器,脚本语言,或者Go语言可以如何实现有兴趣的人。具备编译器/Rust/Go相关的背景知识肯定会有助于理解,但不是必须的。同时,对于专家来说,很可能就不值一看了。 11 | 12 | 开始之前,我们给各个子项目列个表: 13 | | 项目 | 简介 | 编程语言 | 创作方 | 14 | | ----------- | ----------- | ------ | ------ | 15 | | Parser | 解析器,从源代码到AST | Rust | 移植自官方Go版本 | 16 | | Type Checker | 类型推导及其他 | Rust | 移植自官方Go版本 | 17 | | Codegen | 从AST到字节码 | Rust | 原创 18 | | VM | 虚拟机,运行字节码 | Rust | 原创 19 | | Engine | 封装和标准库的native部分 | Rust | 原创 20 | | Std | 标准库 | Go | 移植自官方 | 21 | 22 | 我们先用一个简单的例子来展示,Goscript运行代码时发生了什么: 23 | 24 | ```go 25 | package main 26 | 27 | var a = 1 28 | 29 | func main() { 30 | b := 2 31 | c := a + b 32 | assert(c == 3) // 内置函数 33 | } 34 | ``` 35 | 36 | ### 解析器 37 | 38 | 解析器读取源代码然后把它变成一颗抽象语法树即AST ([Abstract Syntax Tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree)) 39 | 40 | - 手写的分词器 ([scanner.rs](https://github.com/oxfeeefeee/goscript/blob/master/parser/src/scanner.rs)) 把源代码变成一个token流,像这样: 41 | 42 | ```go 43 | ` 44 | package, main, var, a, EQL, 1, ... 45 | ` 46 | ``` 47 | 48 | - 手写的 [递归下降](https://en.wikipedia.org/wiki/Recursive_descent_parser) 解析器 ([parser.rs](https://github.com/oxfeeefeee/goscript/blob/master/parser/src/parser.rs)) 将token流变成语法树. 这一步可能看起来有点黑魔法,但是实际上非常直白:它就是这样一个递归程序:通过匹配当前遇到的token和预期的token,来递归的生成各种代表语句或者表达式的节点。AST的定义在这里 ([ast.rs](https://github.com/oxfeeefeee/goscript/blob/master/parser/src/ast.rs)). 上面小程序的AST大概长这样: 49 | ![ast](/images/ast.jpeg) 50 | 51 | ### 类型检查器 52 | 53 | 类型检查器的首要作用就是类型推导,就是说推算出每个变量,结构体,函数等的具体类型。这些类型信息会用作代码生成和语法错误的检查。具体原理是:遍历AST并根据语言标准定义的规则确定类型信息,同时检查类型不合法的情况。主要代码见 [expr.rs](https://github.com/oxfeeefeee/goscript/blob/master/types/src/check/expr.rs) 和 [stmt.rs](https://github.com/oxfeeefeee/goscript/blob/master/types/src/check/stmt.rs)。 54 | 55 | 具体到我们的例子,它会推导出来 `a`, `b`, `a + b` 和 `c` 的类型都是 `int`; `c == 3` 的类型是 `bool`。同时它也会检查 `assert` 的确是接受并且仅接受一个 `bool` 类型的参数。 56 | 57 | 类型检查是整个项目最复杂的一部分,有一个Go [官方文档](https://go.googlesource.com/example/+/HEAD/gotypes/go-types.md) 可供参考。 除了类型推导,它还会做 [ID 解析](https://github.com/oxfeeefeee/goscript/blob/master/types/src/check/resolver.rs), [常量计算](https://github.com/oxfeeefeee/goscript/blob/master/types/src/constant.rs), 和 [初始化顺序计算](https://github.com/oxfeeefeee/goscript/blob/master/types/src/check/initorder.rs). 58 | 59 | 类型检查的输出结果是一个没有任何语法错误的AST,和AST的类型信息库,这些会被用于代码生成。 60 | 61 | ### 代码生成器 62 | 63 | 代码生成器通过再次遍历AST生成运行时对象,其中包含字节码。上面例子相应生产的代码大概如下: 64 | 65 | ```go 66 | ` 67 | - Package Object (main) 68 | - Package member variable (a) 69 | - Package member function (constructor) 70 | - bytecode: 71 | // copy constant10 to register0, which is where "a" is 72 | DUPLICATE |0 |-10 73 | RETURN 74 | - Package member variable (main) 75 | - bytecode: 76 | // copy constant7 to register0, which is where "b" is 77 | DUPLICATE |0 |-7 78 | // load from package8's 0th member, which is "a", to register2 79 | LOAD_PKG |2 |-8 |0 80 | // register1 = register2 + register0 81 | ADD |1 |2 |0 82 | // register2 = (register1 == constant9) 83 | EQL |2 |1 |-9 84 | // crash if register2 != true 85 | ASSERT |...|2 86 | RETURN 87 | ` 88 | ``` 89 | 90 | Goscript最初使用的是基于栈的虚拟机,现在已经改为基于寄存器的。栈虚拟机相对直观,更容易实现,但是寄存器虚拟机性能会更好。比如说上面那个`ADD`,在用栈虚拟机的时候还需要两个"PUSH"和一个"POP"一起才能完成。 91 | 92 | 本质上来说,代码生成器是一个翻译器,它把一颗树翻译为一个一维数组。这样虚拟机自己就不用去遍历树了,虽然那样也可以做到。虚拟机现在只需要在一个虚拟的纸带上一个接一个处理指令,或者按照指令的要求跳转即可。代码生成的主要代码:[codegen.rs](https://github.com/oxfeeefeee/goscript/blob/master/codegen/src/codegen.rs). 93 | 94 | ### 虚拟机 95 | 96 | 如上所述,虚拟机就是一个巨大的循环([vm.rs](https://github.com/oxfeeefeee/goscript/blob/master/vm/src/vm.rs)),它一个接一个处理指令知道结束,所有的指令大概可以分为三类。 97 | 98 | ```go 99 | ` 100 | - 普通指令: 101 | ADD, SUB, EQL, LOAD_ARRAY ... 102 | - 跳转指令: 103 | JUMP, JUMP_IF ... 104 | - 函数间跳转指令: 105 | CALL, RETURN 106 | ` 107 | ``` 108 | 109 | 我们再来看一个稍微复杂的例子: 110 | 111 | ```go 112 | package main 113 | 114 | func main() { 115 | assert(addN(42,69) == mul(42, 69)) 116 | } 117 | 118 | func addN(m, n int) int { 119 | total := 0 120 | for i :=0; i < n; i++ { 121 | total += m 122 | } 123 | return total 124 | } 125 | 126 | func mul(m, n int) int { 127 | return m * n 128 | } 129 | ``` 130 | 131 | 下面是生成的代码,增加了标号以方便下文引用: 132 | 133 | ```go 134 | ` 135 | main: 136 | 1 DUPLICATE |1 |-9 |... |... |..., 137 | 2 DUPLICATE |2 |-10 |... |... |..., 138 | 3 LOAD_PKG |3 |-11 |1 |... |..., 139 | 4 CALL |3 |0 |... |FlagA |..., 140 | 5 DUPLICATE |5 |-12 |... |... |..., 141 | 6 DUPLICATE |6 |-13 |... |... |..., 142 | 7 LOAD_PKG |7 |-14 |2 |... |..., 143 | 8 CALL |7 |4 |... |FlagA |..., 144 | 9 EQL |8 |0 |4 |Int |Int, 145 | 10 ASSERT |... |8 |... |... |..., 146 | 11 RETURN |... |... |... |FlagA |..., 147 | 148 | addN: 149 | 12 DUPLICATE |3 |-7 |... |... |..., 150 | 13 DUPLICATE |4 |-8 |... |... |..., 151 | 14 LSS |5 |4 |2 |Int |..., 152 | 15 JUMP_IF_NOT |3 |5 |... |... |..., 153 | 16 ADD_ASSIGN |3 |1 |... |Int |..., 154 | 17 INC |4 |... |... |Int |..., 155 | 18 JUMP |-5 |... |... |... |..., 156 | 19 DUPLICATE |0 |3 |... |... |..., 157 | 20 RETURN |... |... |... |FlagA |..., 158 | 159 | mul: 160 | 21 MUL |0 |1 |2 |Int |..., 161 | 22 RETURN |... |... |... |FlagA |..., 162 | ` 163 | ``` 164 | 165 | 这是在虚拟机中的执行过程: 166 | 167 | ```go 168 | ` 169 | 1: 拷贝 42 到 register1 作为 addN 的 参数 170 | 2: 拷贝 69 到 register2 作为 addN 的 参数 171 | 3: 读取函数 "addN" 到 register3 172 | 4: 调用 "addN", 跳转到 "addN" 的第一个指令 173 | 12: 初始化 "total" 为 0 174 | 13: 初始化 "i" 为 0 175 | 14: 比较 register4("i") 和 register2("n") 看是否 i < n, 把结果放到 register5 176 | 15: 如果 register5 不是 TRUE, 跳转到 19 177 | 16: total += m 178 | 17: i++ 179 | 18: 跳转回 14 180 | 14: ... 181 | ... ... 182 | 14: ... 183 | 15: 跳转到 19 184 | 19: 拷贝 register3("total") 到 register0(返回值) 185 | 20: 返回, 跳转回 "main" 186 | 5: 拷贝 42 到 register1 作为 mul 的 参数 187 | 6: 拷贝 69 到 register2 作为 mul 的 参数 188 | 7: 读取函数 "mul" 到 register7 189 | 8: 调用 "mul", 跳转到 "mul" 的第一个指令 190 | 22: register0(return value) = register1(argument1) * register2(argument2) 191 | 23: 返回, 跳转回 "main" 192 | 9: 比较 register0 和 register4 是否相等, 把结果放到 register8 193 | 10: 如果 register8 != true 就 crash 194 | 11: 返回, 跳转出main函数,结束执行 195 | ` 196 | ``` 197 | 198 | ### 后续内容 199 | 200 | 目前为止,几乎没有讨论Goscript独有的机制,在Python, Lua 甚至 Java 里,大致的过程也是差不多的。当然有一些小的区别:Python 和 Lua 没有类型检查,而Java的虚拟机则要复杂很多。 201 | 202 | 下一篇文章讲深入Goscript虚拟机内部,解释Go的各种特性是如何在一个相对简单的虚拟机里实现的,而这可能是一个相对有些新意的轮子。 203 | -------------------------------------------------------------------------------- /posts/goscript_internals_II_the_runtime_a_en.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'Goscript Internals II: The Runtime, Part A' 3 | date: '2022-07-14' 4 | --- 5 | 6 | ## Ingredients 7 | 8 | The [previous overview](https://goscript.dev/posts/goscript_internals_I_overview_en) promised a "relatively original wheel", however the runtime is in fact more like a cocktail. Most of the ingredients are from Lua, Python Go and Rust. 9 | 10 | * The VM and instruction design is inspired by Lua. Like Lua, Goscript has a register-based VM, and it also uses "upvalue" to support closure. 11 | 12 | * The memory management and GC is similar to Python's. Goscript uses reference counting and circular reference detection to reclaim memory. 13 | 14 | * To behave exactly like the official version of Go, the data structures have to simulate how they work originally, things like Interface and Slice. 15 | 16 | * To implement async features like goroutine and channel, async-await and channel from Rust ecosystem are used. 17 | 18 | ## Instruction set 19 | 20 | Instructions [(instruction.rs)](https://github.com/oxfeeefeee/goscript/blob/master/vm/src/instruction.rs) can be divided into five categories: 21 | 22 | * Accessing. Copy between registers; access array/slice/map/upvalue/pointers. 23 | 24 | * Operation. Things like plus and minus, things denoted by operators. 25 | 26 | * Function call and return. 27 | 28 | * Jump. 29 | 30 | * Built-in functions. Both the implicitly used ones like BIND_METHOD, and the explicit ones like NEW and COPY. 31 | 32 | ## Architecture of the VM 33 | 34 | Let's take a look again at the previous example: 35 | 36 | ```go 37 | package main 38 | 39 | func main() { 40 | assert(addN(42,69) == mul(42, 69)) 41 | } 42 | 43 | func addN(m, n int) int { 44 | total := 0 45 | for i :=0; i < n; i++ { 46 | total += m 47 | } 48 | return total 49 | } 50 | 51 | func mul(m, n int) int { 52 | return m * n 53 | } 54 | ``` 55 | 56 | Below is a snapshot of the example running on the VM: 57 | ![vm_addn](/images/vm_addn.jpeg) 58 | This diagram shows what's in the memory when `total += m` of `addN` is being processed. On the left side is the loaded bytecode including the instructions and consts, which are read only. 59 | 60 | The right side is more interesting. You should be no stranger to [call stack](https://en.wikipedia.org/wiki/Call_stack), when `main` and `addN` get called, `call frames` generated for them and pushed to call stack, they'll be popped out when the functions return. Call frame is used to record information about the running function including the most obvious one: [PC](https://en.wikipedia.org/wiki/Program_counter). 61 | 62 | Last but not least, the virtual registers. As the diagram shows, when a function is called, a set of virtual registers are allocated for it in the following order: `return value`, `arguments`, `local vars`, `temporary vars`. Registers and consts are the only two places where instructions can read from, and registers are the only place to write to. 63 | 64 | For every step, the processing loop does the following: 65 | Fetch the instruction that the PC of the active call frame on the top of the call stack points to, and process it. Then the PC is either increased by 1, or modified to perform a jump. If the instruction is a CALL / RETURN, the active call frame is changed. 66 | 67 | This is when `m * n` of `mul` is being processed after `addN` returned and `mul` is called: 68 | ![vm_mul](/images/vm_mul.jpeg) 69 | 70 | ## Upvalue 71 | 72 | "The combination of lexical scoping with first-class functions creates a well-known difficulty for accessing outer local variables". -- [《The Implementation of Lua 5.0》](https://www.lua.org/doc/sblp2005.pdf). Goscript steals the solution from Lua as it faces the same problem. 73 | 74 | Here is an example: 75 | 76 | ```go 77 | package main 78 | 79 | import "fmt" 80 | 81 | // Returns a function that add 'up' to the input 'i' 82 | func makeAdd(up int) func(int) int { 83 | return func(i int) int { 84 | return up + i 85 | } 86 | } 87 | 88 | func main() { 89 | add42 := makeAdd(42) 90 | c := add42(1) 91 | fmt.Println(c) 92 | } 93 | ``` 94 | 95 | Let's focus on `add42 := makeAdd(42)`, typically, when makeAdd returns, the argument `42` is released, but in our case, it cannot be released as it's still referenced by `add42`. Lua solve this by using a pointer that points to the `up` value , hence the name. In the example, `add42` holds an upvalue pointing to `42`. 96 | 97 | The design of upvalue is relatively simple. When `makeAdd` is not returned yet, the upvalue is just the address of `up` in the virtual registers, so both the outer and the inner function can access it from the register. When `makeAdd` returns, the value of `up` get copied to the heap, and the upvalue becomes a real pointer pointing to the heap value. 98 | 99 | The upvalue is `open` when `up` is still in the register, the upvalue is `closed` when `up` is in the heap. 100 | 101 | ## Pointer 102 | 103 | It turns out upvalue can also be used elsewhere: pointers. 104 | 105 | There is no VM-based language supports pointers as far as I know, but Goscript has to. Luckily there are limitations for the use of pointers according to the [Go specs](https://go.dev/ref/spec#Address_operators). Simply put, pointers can only be created to point to certain things, for Goscript, they are: 106 | 107 | * local variable 108 | * package member 109 | * slice member 110 | * struct field 111 | 112 | In Goscript, package, slice and struct are all stored in the heap, and we have real pointers that point to them, so a combination of `(pointer, member_index)` is sufficient to represent those three. For local variable, a pointer to it behaves exactly the same as an upvalue, so it is implemented as an upvalue. 113 | 114 | ## Goroutine 115 | 116 | As the most iconic feature of Go, goroutine may seem complicated to implement, surprisingly, it's not. Before look into the details of Goscript's coroutine, let's talk about coroutines in general. 117 | 118 | In terms of where you can yield, there are: 119 | 120 | * Stackful coroutines, meaning they can yield and later resume freely 121 | * Stackless coroutines, meaning they can only yield back to the parent coroutine and later get resumed by the parent coroutine. 122 | 123 | In terms of how coroutines cooperate with each other, there are: 124 | 125 | * Preemptive coroutines, meaning busy coroutines get suspended by the scheduler once in a while to give other coroutines a chance. 126 | 127 | * Cooperative coroutines, meaning coroutines runs indefinitely until they explicitly yield. 128 | 129 | Go has a stackful preemptive coroutine system, which is also the most user friendly. 130 | 131 | In Goscript, for every goroutine a processing loop is launched, which is an async function, and an await can happen at any point inside the loop. Async Rust functions can also be called inside the loop via Goscript FFI. 132 | 133 | The loop yields after a certain amount of instructions are processed, so that the users don't need to call yield explicitly, making the whole system preemptive. 134 | 135 | With the help of Rust's async-await, we have a simple solution behind a powerful feature. It's easier to understand from a different perspective: a goroutine is just a Rust future. 136 | 137 | ### Coming up next 138 | 139 | Thera are a lot more Go features to cover in the runtime, I'm planning to finish them in the next installment. 140 | -------------------------------------------------------------------------------- /posts/goscript_internals_I_overview_en.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'Goscript Internals I: Overview' 3 | date: '2022-07-04' 4 | translation: 'goscript_internals_I_overview_zh' 5 | translation_lang: '中文' 6 | --- 7 | 8 | ## Introduction 9 | 10 | Goscript is a VM-based Golang implementation written in Rust. Goscript Internals will be a series of articles explaining Goscript's design. The intended audience are any experienced programmers who are interested in how Goscript works--or, more generally--how a compiler/ a scripting language/ a Go implementation works. You don't need to have a background in compilers, Go or Rust but it does help if you do. This first article is a brief introduction of how a typed scripting language works, which may be boring to experts. 11 | 12 | Before we dive in, let's make a table of all the sub-projects: 13 | | Project | Description | Language | Credit | 14 | | ------- |--------------------------- | ------ | ------ | 15 | | Parser | turns source into AST | Rust | ported from Official Go | 16 | | Type Checker | type deduction and more | Rust | ported from Official Go | 17 | | Codegen | turns AST into bytecode | Rust | original work 18 | | VM | runs bytecode | Rust | original work 19 | | Engine | wrapper and native library | Rust | original work 20 | | Std | Standard library | Go | adapted from Go| 21 | 22 | Let's get a big picture of how things work by looking into what happens with this simple program, when Goscript runs it: 23 | 24 | ```go 25 | package main 26 | 27 | var a = 1 28 | 29 | func main() { 30 | b := 2 31 | c := a + b 32 | assert(c == 3) // built-in function 33 | } 34 | ``` 35 | 36 | ### The parser 37 | 38 | The parser read the source code and turns it into an AST ([Abstract Syntax Tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree)) 39 | 40 | - A hand-written tokenizer ([scanner.rs](https://github.com/oxfeeefeee/goscript/blob/master/parser/src/scanner.rs)) turns the source code to a list of tokens like this: 41 | 42 | ```go 43 | ` 44 | package, main, var, a, EQL, 1, ... 45 | ` 46 | ``` 47 | 48 | - A hand-written [recursive descent](https://en.wikipedia.org/wiki/Recursive_descent_parser) parser ([parser.rs](https://github.com/oxfeeefeee/goscript/blob/master/parser/src/parser.rs)) turns the list of tokens into a tree. This step might look magical, but in fact quite intuitive, it's just a recursive program, that try to build nodes of different types of statements and expressions, by matching the tokens it sees and the tokens it expects. The tree definition can be found here ([ast.rs](https://github.com/oxfeeefeee/goscript/blob/master/parser/src/ast.rs)). The AST of the above program would be something like this: 49 | ![ast](/images/ast.jpeg) 50 | 51 | ### The type checker 52 | 53 | The main task of the type checker is type deduction, which means figuring out the exact types of variables, structs, functions etc. to provide type information for code generator and to catch more syntax errors. It does so by traversing the AST and enforcing the rules defined in the language specs. You can take a quick look at [expr.rs](https://github.com/oxfeeefeee/goscript/blob/master/types/src/check/expr.rs) and [stmt.rs](https://github.com/oxfeeefeee/goscript/blob/master/types/src/check/stmt.rs) to get a general idea of how it works. 54 | 55 | In the case of the above example, it deduces that the types of `a`, `b`, `a + b` and `c` are all `int`, and the type of `c == 3` is `bool`. it also checks that `assert` does accept one and only one `bool` argument. 56 | 57 | The type checker is the most complex part of the whole project, there is an Go official [document](https://go.googlesource.com/example/+/HEAD/gotypes/go-types.md) talking about it in detail. In addition to type deduction, it does [identifier resolution](https://github.com/oxfeeefeee/goscript/blob/master/types/src/check/resolver.rs), [constant evaluation](https://github.com/oxfeeefeee/goscript/blob/master/types/src/constant.rs), and the insignificant looking [init order computation](https://github.com/oxfeeefeee/goscript/blob/master/types/src/check/initorder.rs). 58 | 59 | The result of a type checking pass is a syntax error free AST, and a very rich database of type info about the AST, which will be used for bytecode generation. 60 | 61 | ### The code generator 62 | 63 | The code generator traverses the AST again to generate runtime objects containing the bytecode. For the example above, it generates objects that logically look like this: 64 | 65 | ```go 66 | ` 67 | - Package Object (main) 68 | - Package member variable (a) 69 | - Package member function (constructor) 70 | - bytecode: 71 | // copy constant10 to register0, which is where "a" is 72 | DUPLICATE |0 |-10 73 | RETURN 74 | - Package member variable (main) 75 | - bytecode: 76 | // copy constant7 to register0, which is where "b" is 77 | DUPLICATE |0 |-7 78 | // load from package8's 0th member, which is "a", to register2 79 | LOAD_PKG |2 |-8 |0 80 | // register1 = register2 + register0 81 | ADD |1 |2 |0 82 | // register2 = (register1 == constant9) 83 | EQL |2 |1 |-9 84 | // crash if register2 != true 85 | ASSERT |...|2 86 | RETURN 87 | ` 88 | ``` 89 | 90 | As you can see, Goscript abandoned the original stack-based VM in favor of a register-based one. Stack-based VM is intuitive to design, but less efficient. With a stack-based VM, the above `ADD` would probably need three extra instructions -- two "PUSH" and one "POP" -- to do the same job. 91 | 92 | In essence, code generator is just a translator, which translates a tree into a one dimension array, so that for the VM, instead of traversing a tree, which is totally doable but would be much less efficient, it only needs to deal with instructions on a virtual tape one by one, plus jumping back and force. The main part of the code is [codegen.rs](https://github.com/oxfeeefeee/goscript/blob/master/codegen/src/codegen.rs). 93 | 94 | ### The virtual machine 95 | 96 | As stated above, the VM is just a big loop ([vm.rs](https://github.com/oxfeeefeee/goscript/blob/master/vm/src/vm.rs)) that processes instructions until they run out. All the instructions can be divided into three categories: 97 | 98 | ```go 99 | ` 100 | - The normal ones: 101 | ADD, SUB, EQL, LOAD_ARRAY ... 102 | - The ones lead to jumping: 103 | JUMP, JUMP_IF ... 104 | - The ones lead to jumping between functions: 105 | CALL, RETURN 106 | ` 107 | ``` 108 | 109 | Let's write a slightly more complex program as an example: 110 | 111 | ```go 112 | package main 113 | 114 | func main() { 115 | assert(addN(42,69) == mul(42, 69)) 116 | } 117 | 118 | func addN(m, n int) int { 119 | total := 0 120 | for i :=0; i < n; i++ { 121 | total += m 122 | } 123 | return total 124 | } 125 | 126 | func mul(m, n int) int { 127 | return m * n 128 | } 129 | ``` 130 | 131 | Below is the generated code, the instructions are numbered to make it easier to refer to: 132 | 133 | ```go 134 | ` 135 | main: 136 | 1 DUPLICATE |1 |-3 |... |... |..., 137 | 2 DUPLICATE |2 |-4 |... |... |..., 138 | 3 LOAD_PKG |3 |-1 |1 |... |..., 139 | 4 CALL |3 |0 |... |FlagA |..., 140 | 5 DUPLICATE |5 |-3 |... |... |..., 141 | 6 DUPLICATE |6 |-4 |... |... |..., 142 | 7 LOAD_PKG |7 |-1 |2 |... |..., 143 | 8 CALL |7 |4 |... |FlagA |..., 144 | 9 EQL |8 |0 |4 |Int |Int, 145 | 10 ASSERT |... |8 |... |... |..., 146 | 11 RETURN |... |... |... |FlagA |..., 147 | 148 | addN: 149 | 12 DUPLICATE |3 |-5 |... |... |..., 150 | 13 DUPLICATE |4 |-5 |... |... |..., 151 | 14 LSS |5 |4 |2 |Int |..., 152 | 15 JUMP_IF_NOT |3 |5 |... |... |..., 153 | 16 ADD_ASSIGN |3 |1 |... |Int |..., 154 | 17 INC |4 |... |... |Int |..., 155 | 18 JUMP |-5 |... |... |... |..., 156 | 19 DUPLICATE |0 |3 |... |... |..., 157 | 20 RETURN |... |... |... |FlagA |..., 158 | 159 | mul: 160 | 21 MUL |0 |1 |2 |Int |..., 161 | 22 RETURN |... |... |... |FlagA |..., 162 | ` 163 | ``` 164 | 165 | This is what happens when the VM execute the code: 166 | 167 | ```go 168 | ` 169 | 1: Copy 42 to register1 as addN's argument 170 | 2: Copy 69 to register2 as addN's argument 171 | 3: Load "addN" to register3 172 | 4: Call "addN", jump to instructions in "addN" 173 | 12: Initialize "total" as 0 174 | 13: Initialize "i" as 0 175 | 14: Compare register4("i") and register2("n") to see if i < n, and put the result in register5 176 | 15: If register5 is not TRUE, jump to 19 177 | 16: total += m 178 | 17: i++ 179 | 18: Jump back to 14 180 | 14: ... 181 | ... ... 182 | 14: ... 183 | 15: Jump to 19 184 | 19: Copy register3("total") to register0(return value) 185 | 20: return, jump back to "main" 186 | 5: Copy 42 to register5 as mul's argument 187 | 6: Copy 69 to register6 as mul's argument 188 | 7: Load "mul" to register7 189 | 8: Call "mul", jump to instructions in "mul" 190 | 22: register0(return value) = register1(argument1) * register2(argument2) 191 | 23: return, jump back to "main" 192 | 9: Compare register0 and register4 to see if they are equal, and put the result in register8 193 | 10: crash if register8 != true 194 | 11: return, jump out of main and exit the program 195 | ` 196 | ``` 197 | 198 | ### Coming up next 199 | 200 | So far, there is almost nothing specifically about Goscript, pretty much the same thing happens in Python, Lua or even Java, though there are a few differences: Python or Lua doesn't have a type checker, and Java has a much more complicated VM. 201 | 202 | The next article will be a deep-dive into Goscript's VM, to explain how various of Go features are implemented in a simple VM, which, unlike to other parts of the project, is a relatively original wheel that got invented. 203 | -------------------------------------------------------------------------------- /goscript/pkg/goscript_playground.js: -------------------------------------------------------------------------------- 1 | let wasm; 2 | 3 | const heap = new Array(128).fill(undefined); 4 | 5 | heap.push(undefined, null, true, false); 6 | 7 | function getObject(idx) { return heap[idx]; } 8 | 9 | let heap_next = heap.length; 10 | 11 | function dropObject(idx) { 12 | if (idx < 132) return; 13 | heap[idx] = heap_next; 14 | heap_next = idx; 15 | } 16 | 17 | function takeObject(idx) { 18 | const ret = getObject(idx); 19 | dropObject(idx); 20 | return ret; 21 | } 22 | 23 | const cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }); 24 | 25 | cachedTextDecoder.decode(); 26 | 27 | let cachedUint8Memory0 = null; 28 | 29 | function getUint8Memory0() { 30 | if (cachedUint8Memory0 === null || cachedUint8Memory0.byteLength === 0) { 31 | cachedUint8Memory0 = new Uint8Array(wasm.memory.buffer); 32 | } 33 | return cachedUint8Memory0; 34 | } 35 | 36 | function getStringFromWasm0(ptr, len) { 37 | return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len)); 38 | } 39 | 40 | function addHeapObject(obj) { 41 | if (heap_next === heap.length) heap.push(heap.length + 1); 42 | const idx = heap_next; 43 | heap_next = heap[idx]; 44 | 45 | heap[idx] = obj; 46 | return idx; 47 | } 48 | 49 | function debugString(val) { 50 | // primitive types 51 | const type = typeof val; 52 | if (type == 'number' || type == 'boolean' || val == null) { 53 | return `${val}`; 54 | } 55 | if (type == 'string') { 56 | return `"${val}"`; 57 | } 58 | if (type == 'symbol') { 59 | const description = val.description; 60 | if (description == null) { 61 | return 'Symbol'; 62 | } else { 63 | return `Symbol(${description})`; 64 | } 65 | } 66 | if (type == 'function') { 67 | const name = val.name; 68 | if (typeof name == 'string' && name.length > 0) { 69 | return `Function(${name})`; 70 | } else { 71 | return 'Function'; 72 | } 73 | } 74 | // objects 75 | if (Array.isArray(val)) { 76 | const length = val.length; 77 | let debug = '['; 78 | if (length > 0) { 79 | debug += debugString(val[0]); 80 | } 81 | for(let i = 1; i < length; i++) { 82 | debug += ', ' + debugString(val[i]); 83 | } 84 | debug += ']'; 85 | return debug; 86 | } 87 | // Test for built-in 88 | const builtInMatches = /\[object ([^\]]+)\]/.exec(toString.call(val)); 89 | let className; 90 | if (builtInMatches.length > 1) { 91 | className = builtInMatches[1]; 92 | } else { 93 | // Failed to match the standard '[object ClassName]' 94 | return toString.call(val); 95 | } 96 | if (className == 'Object') { 97 | // we're a user defined class or Object 98 | // JSON.stringify avoids problems with cycles, and is generally much 99 | // easier than looping through ownProperties of `val`. 100 | try { 101 | return 'Object(' + JSON.stringify(val) + ')'; 102 | } catch (_) { 103 | return 'Object'; 104 | } 105 | } 106 | // errors 107 | if (val instanceof Error) { 108 | return `${val.name}: ${val.message}\n${val.stack}`; 109 | } 110 | // TODO we could test for more things here, like `Set`s and `Map`s. 111 | return className; 112 | } 113 | 114 | let WASM_VECTOR_LEN = 0; 115 | 116 | const cachedTextEncoder = new TextEncoder('utf-8'); 117 | 118 | const encodeString = (typeof cachedTextEncoder.encodeInto === 'function' 119 | ? function (arg, view) { 120 | return cachedTextEncoder.encodeInto(arg, view); 121 | } 122 | : function (arg, view) { 123 | const buf = cachedTextEncoder.encode(arg); 124 | view.set(buf); 125 | return { 126 | read: arg.length, 127 | written: buf.length 128 | }; 129 | }); 130 | 131 | function passStringToWasm0(arg, malloc, realloc) { 132 | 133 | if (realloc === undefined) { 134 | const buf = cachedTextEncoder.encode(arg); 135 | const ptr = malloc(buf.length); 136 | getUint8Memory0().subarray(ptr, ptr + buf.length).set(buf); 137 | WASM_VECTOR_LEN = buf.length; 138 | return ptr; 139 | } 140 | 141 | let len = arg.length; 142 | let ptr = malloc(len); 143 | 144 | const mem = getUint8Memory0(); 145 | 146 | let offset = 0; 147 | 148 | for (; offset < len; offset++) { 149 | const code = arg.charCodeAt(offset); 150 | if (code > 0x7F) break; 151 | mem[ptr + offset] = code; 152 | } 153 | 154 | if (offset !== len) { 155 | if (offset !== 0) { 156 | arg = arg.slice(offset); 157 | } 158 | ptr = realloc(ptr, len, len = offset + arg.length * 3); 159 | const view = getUint8Memory0().subarray(ptr + offset, ptr + len); 160 | const ret = encodeString(arg, view); 161 | 162 | offset += ret.written; 163 | } 164 | 165 | WASM_VECTOR_LEN = offset; 166 | return ptr; 167 | } 168 | 169 | let cachedInt32Memory0 = null; 170 | 171 | function getInt32Memory0() { 172 | if (cachedInt32Memory0 === null || cachedInt32Memory0.byteLength === 0) { 173 | cachedInt32Memory0 = new Int32Array(wasm.memory.buffer); 174 | } 175 | return cachedInt32Memory0; 176 | } 177 | 178 | function passArray8ToWasm0(arg, malloc) { 179 | const ptr = malloc(arg.length * 1); 180 | getUint8Memory0().set(arg, ptr / 1); 181 | WASM_VECTOR_LEN = arg.length; 182 | return ptr; 183 | } 184 | /** 185 | * @param {Uint8Array} zip 186 | * @param {string} source 187 | * @returns {RunResult} 188 | */ 189 | export function run_zip_and_string(zip, source) { 190 | const ptr0 = passArray8ToWasm0(zip, wasm.__wbindgen_malloc); 191 | const len0 = WASM_VECTOR_LEN; 192 | const ptr1 = passStringToWasm0(source, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); 193 | const len1 = WASM_VECTOR_LEN; 194 | const ret = wasm.run_zip_and_string(ptr0, len0, ptr1, len1); 195 | return RunResult.__wrap(ret); 196 | } 197 | 198 | function handleError(f, args) { 199 | try { 200 | return f.apply(this, args); 201 | } catch (e) { 202 | wasm.__wbindgen_exn_store(addHeapObject(e)); 203 | } 204 | } 205 | /** 206 | */ 207 | export class RunResult { 208 | 209 | static __wrap(ptr) { 210 | const obj = Object.create(RunResult.prototype); 211 | obj.ptr = ptr; 212 | 213 | return obj; 214 | } 215 | 216 | __destroy_into_raw() { 217 | const ptr = this.ptr; 218 | this.ptr = 0; 219 | 220 | return ptr; 221 | } 222 | 223 | free() { 224 | const ptr = this.__destroy_into_raw(); 225 | wasm.__wbg_runresult_free(ptr); 226 | } 227 | /** 228 | * @param {string} out 229 | * @param {string} err 230 | * @param {string} compile_err 231 | * @param {string} debug 232 | * @returns {RunResult} 233 | */ 234 | static new(out, err, compile_err, debug) { 235 | const ptr0 = passStringToWasm0(out, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); 236 | const len0 = WASM_VECTOR_LEN; 237 | const ptr1 = passStringToWasm0(err, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); 238 | const len1 = WASM_VECTOR_LEN; 239 | const ptr2 = passStringToWasm0(compile_err, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); 240 | const len2 = WASM_VECTOR_LEN; 241 | const ptr3 = passStringToWasm0(debug, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); 242 | const len3 = WASM_VECTOR_LEN; 243 | const ret = wasm.runresult_new(ptr0, len0, ptr1, len1, ptr2, len2, ptr3, len3); 244 | return RunResult.__wrap(ret); 245 | } 246 | /** 247 | * @returns {string} 248 | */ 249 | get out() { 250 | try { 251 | const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); 252 | wasm.runresult_out(retptr, this.ptr); 253 | var r0 = getInt32Memory0()[retptr / 4 + 0]; 254 | var r1 = getInt32Memory0()[retptr / 4 + 1]; 255 | return getStringFromWasm0(r0, r1); 256 | } finally { 257 | wasm.__wbindgen_add_to_stack_pointer(16); 258 | wasm.__wbindgen_free(r0, r1); 259 | } 260 | } 261 | /** 262 | * @returns {string} 263 | */ 264 | get err() { 265 | try { 266 | const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); 267 | wasm.runresult_err(retptr, this.ptr); 268 | var r0 = getInt32Memory0()[retptr / 4 + 0]; 269 | var r1 = getInt32Memory0()[retptr / 4 + 1]; 270 | return getStringFromWasm0(r0, r1); 271 | } finally { 272 | wasm.__wbindgen_add_to_stack_pointer(16); 273 | wasm.__wbindgen_free(r0, r1); 274 | } 275 | } 276 | /** 277 | * @returns {string} 278 | */ 279 | get compile_err() { 280 | try { 281 | const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); 282 | wasm.runresult_compile_err(retptr, this.ptr); 283 | var r0 = getInt32Memory0()[retptr / 4 + 0]; 284 | var r1 = getInt32Memory0()[retptr / 4 + 1]; 285 | return getStringFromWasm0(r0, r1); 286 | } finally { 287 | wasm.__wbindgen_add_to_stack_pointer(16); 288 | wasm.__wbindgen_free(r0, r1); 289 | } 290 | } 291 | /** 292 | * @returns {string} 293 | */ 294 | get debug() { 295 | try { 296 | const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); 297 | wasm.runresult_debug(retptr, this.ptr); 298 | var r0 = getInt32Memory0()[retptr / 4 + 0]; 299 | var r1 = getInt32Memory0()[retptr / 4 + 1]; 300 | return getStringFromWasm0(r0, r1); 301 | } finally { 302 | wasm.__wbindgen_add_to_stack_pointer(16); 303 | wasm.__wbindgen_free(r0, r1); 304 | } 305 | } 306 | } 307 | 308 | async function load(module, imports) { 309 | if (typeof Response === 'function' && module instanceof Response) { 310 | if (typeof WebAssembly.instantiateStreaming === 'function') { 311 | try { 312 | return await WebAssembly.instantiateStreaming(module, imports); 313 | 314 | } catch (e) { 315 | if (module.headers.get('Content-Type') != 'application/wasm') { 316 | console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e); 317 | 318 | } else { 319 | throw e; 320 | } 321 | } 322 | } 323 | 324 | const bytes = await module.arrayBuffer(); 325 | return await WebAssembly.instantiate(bytes, imports); 326 | 327 | } else { 328 | const instance = await WebAssembly.instantiate(module, imports); 329 | 330 | if (instance instanceof WebAssembly.Instance) { 331 | return { instance, module }; 332 | 333 | } else { 334 | return instance; 335 | } 336 | } 337 | } 338 | 339 | function getImports() { 340 | const imports = {}; 341 | imports.wbg = {}; 342 | imports.wbg.__wbg_log_788e383c5d217a33 = function(arg0, arg1) { 343 | console.log(getStringFromWasm0(arg0, arg1)); 344 | }; 345 | imports.wbg.__wbg_log_a406527fb5feb5f5 = function(arg0, arg1) { 346 | console.log(getStringFromWasm0(arg0, arg1)); 347 | }; 348 | imports.wbg.__wbindgen_object_drop_ref = function(arg0) { 349 | takeObject(arg0); 350 | }; 351 | imports.wbg.__wbindgen_string_new = function(arg0, arg1) { 352 | const ret = getStringFromWasm0(arg0, arg1); 353 | return addHeapObject(ret); 354 | }; 355 | imports.wbg.__wbg_now_c644db5194be8437 = function(arg0) { 356 | const ret = getObject(arg0).now(); 357 | return ret; 358 | }; 359 | imports.wbg.__wbg_newnoargs_2b8b6bd7753c76ba = function(arg0, arg1) { 360 | const ret = new Function(getStringFromWasm0(arg0, arg1)); 361 | return addHeapObject(ret); 362 | }; 363 | imports.wbg.__wbg_get_baf4855f9a986186 = function() { return handleError(function (arg0, arg1) { 364 | const ret = Reflect.get(getObject(arg0), getObject(arg1)); 365 | return addHeapObject(ret); 366 | }, arguments) }; 367 | imports.wbg.__wbg_call_95d1ea488d03e4e8 = function() { return handleError(function (arg0, arg1) { 368 | const ret = getObject(arg0).call(getObject(arg1)); 369 | return addHeapObject(ret); 370 | }, arguments) }; 371 | imports.wbg.__wbindgen_object_clone_ref = function(arg0) { 372 | const ret = getObject(arg0); 373 | return addHeapObject(ret); 374 | }; 375 | imports.wbg.__wbg_self_e7c1f827057f6584 = function() { return handleError(function () { 376 | const ret = self.self; 377 | return addHeapObject(ret); 378 | }, arguments) }; 379 | imports.wbg.__wbg_window_a09ec664e14b1b81 = function() { return handleError(function () { 380 | const ret = window.window; 381 | return addHeapObject(ret); 382 | }, arguments) }; 383 | imports.wbg.__wbg_globalThis_87cbb8506fecf3a9 = function() { return handleError(function () { 384 | const ret = globalThis.globalThis; 385 | return addHeapObject(ret); 386 | }, arguments) }; 387 | imports.wbg.__wbg_global_c85a9259e621f3db = function() { return handleError(function () { 388 | const ret = global.global; 389 | return addHeapObject(ret); 390 | }, arguments) }; 391 | imports.wbg.__wbindgen_is_undefined = function(arg0) { 392 | const ret = getObject(arg0) === undefined; 393 | return ret; 394 | }; 395 | imports.wbg.__wbindgen_debug_string = function(arg0, arg1) { 396 | const ret = debugString(getObject(arg1)); 397 | const ptr0 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); 398 | const len0 = WASM_VECTOR_LEN; 399 | getInt32Memory0()[arg0 / 4 + 1] = len0; 400 | getInt32Memory0()[arg0 / 4 + 0] = ptr0; 401 | }; 402 | imports.wbg.__wbindgen_throw = function(arg0, arg1) { 403 | throw new Error(getStringFromWasm0(arg0, arg1)); 404 | }; 405 | 406 | return imports; 407 | } 408 | 409 | function initMemory(imports, maybe_memory) { 410 | 411 | } 412 | 413 | function finalizeInit(instance, module) { 414 | wasm = instance.exports; 415 | init.__wbindgen_wasm_module = module; 416 | cachedInt32Memory0 = null; 417 | cachedUint8Memory0 = null; 418 | 419 | 420 | return wasm; 421 | } 422 | 423 | function initSync(module) { 424 | const imports = getImports(); 425 | 426 | initMemory(imports); 427 | 428 | if (!(module instanceof WebAssembly.Module)) { 429 | module = new WebAssembly.Module(module); 430 | } 431 | 432 | const instance = new WebAssembly.Instance(module, imports); 433 | 434 | return finalizeInit(instance, module); 435 | } 436 | 437 | async function init(input) { 438 | if (typeof input === 'undefined') { 439 | input = new URL('goscript_playground_bg.wasm', import.meta.url); 440 | } 441 | const imports = getImports(); 442 | 443 | if (typeof input === 'string' || (typeof Request === 'function' && input instanceof Request) || (typeof URL === 'function' && input instanceof URL)) { 444 | input = fetch(input); 445 | } 446 | 447 | initMemory(imports); 448 | 449 | const { instance, module } = await load(await input, imports); 450 | 451 | return finalizeInit(instance, module); 452 | } 453 | 454 | export { initSync } 455 | export default init; 456 | --------------------------------------------------------------------------------