• Home
  • History
  • Annotate
Name Date Size #Lines LOC

..03-May-2022-

_example/H05-Mar-2019-

ast2/H05-Mar-2019-

atomic/H05-Mar-2019-

base/H05-Mar-2019-

classic/H05-Mar-2019-

cmd/H05-Mar-2019-

doc/H05-Mar-2019-

experiments/H05-Mar-2019-

fast/H05-Mar-2019-

gls/H05-Mar-2019-

imports/H05-Mar-2019-

jit/H05-Mar-2019-

parser/H05-Mar-2019-

printer/H05-Mar-2019-

scanner/H05-Mar-2019-

token/H05-Mar-2019-

typeutil/H05-Mar-2019-

vendor/H03-May-2022-

xreflect/H05-Mar-2019-

.gitignoreH A D05-Mar-2019245

LICENSEH A D05-Mar-201916.3 KiB

README.mdH A D05-Mar-201917.4 KiB

TrickyGo.mdH A D05-Mar-2019862

all_test.goH A D05-Mar-201953.9 KiB

bench_jit_test.goH A D05-Mar-20191.4 KiB

bench_sort_test.goH A D05-Mar-20195.3 KiB

bench_test.goH A D05-Mar-201916.1 KiB

main.goH A D05-Mar-2019663

README.md

1## gomacro - interactive Go interpreter and debugger with generics and macros
2
3gomacro is an almost complete Go interpreter, implemented in pure Go. It offers both
4an interactive REPL and a scripting mode, and does not require a Go toolchain at runtime
5(except in one very specific case: import of a 3<sup>rd</sup> party package at runtime).
6
7It has two dependencies beyond the Go standard library: github.com/peterh/liner and golang.org/x/sys
8
9Gomacro can be used as:
10* a standalone executable with interactive Go REPL, line editing and code completion:
11  just run `gomacro` from your command line, then type Go code. Example:
12    ```
13    $ gomacro
14    [greeting message...]
15
16    gomacro> import "fmt"
17    gomacro> fmt.Println("hello, world!")
18    hello, world!
19    14      // int
20    <nil>   // error
21    gomacro>
22    ```
23  press TAB to autocomplete a word, and press it again to cycle on possible completions.
24
25  Line editing follows mostly Emacs: Ctrl+A or Home jumps to start of line,
26  Ctrl+E or End jumps to end of line, Ald+D deletes word starting at cursor...
27  For the full list of key bindings, see https://github.com/peterh/liner
28
29* a tool to experiment with Go **generics**: see [Generics](#generics)
30
31* a Go source code debugger: see [Debugger](#debugger)
32
33* an interactive tool to make science more productive and more fun.
34  If you use compiled Go with scientific libraries (physics, bioinformatics, statistics...)
35  you can import the same libraries from gomacro REPL (immediate on Go 1.8+ and Linux
36  or Go 1.10.2+ and Mac OS X, requires restarting on other platforms,
37  see [Importing packages](#importing-packages) below), call them interactively,
38  inspect the results, feed them to other functions/libraries, all in a single session.
39  The imported libraries will be **compiled**, not interpreted,
40  so they will be as fast as in compiled Go.
41
42  For a graphical user interface on top of gomacro, see [Gophernotes](https://github.com/gopherdata/gophernotes).
43  It is a Go kernel for Jupyter notebooks and nteract, and uses gomacro for Go code evaluation.
44
45* a library that adds Eval() and scripting capabilities to your Go programs in few lines
46  of code:
47	```go
48	package main
49	import (
50		"fmt"
51		"reflect"
52		"github.com/cosmos72/gomacro/fast"
53	)
54	func RunGomacro(toeval string) reflect.Value {
55		interp := fast.New()
56		// for simplicity, only collect the first returned value
57		val, _ := interp.Eval(toeval)
58		return val
59	}
60	func main() {
61		fmt.Println(RunGomacro("1+1"))
62	}
63	```
64  Also, [github issue #13](https://github.com/cosmos72/gomacro/issues/13) explains
65  how to have your application's functions, variable, constants and types
66  available in the interpreter.
67
68  Note: gomacro license is [MPL 2.0](LICENSE), which imposes some restrictions
69  on programs that use gomacro.
70  See [MPL 2.0 FAQ](https://www.mozilla.org/en-US/MPL/2.0/FAQ/) for common questions
71  regarding the license terms and conditions.
72
73* a way to execute Go source code on-the-fly without a Go compiler:
74  you can either run `gomacro FILENAME.go` (works on every supported platform)
75
76  or you can insert a line `#!/usr/bin/env gomacro` at the beginning of a Go source file,
77  then mark the file as executable with `chmod +x FILENAME.go` and finally execute it
78  with `./FILENAME.go` (works only on Unix-like systems: Linux, *BSD, Mac OS X ...)
79
80* a Go code generation tool:
81  gomacro was started as an experiment to add Lisp-like macros to Go, and they are
82  extremely useful (in the author's opinion) to simplify code generation.
83  Macros are normal Go functions, they are special only in one aspect:
84  they are executed **before** compiling code, and their input and output **is** code
85  (abstract syntax trees, in the form of go/ast.Node)
86
87  Don't confuse them with C preprocessor macros: in Lisp, Scheme and now in Go,
88  macros are regular functions written in the same programming language
89  as the rest of the source code. They can perform arbitrary computations
90  and call any other function or library: they can even read and write files,
91  open network connections, etc... as a normal Go function can do.
92
93  Run `gomacro -m -w FILENAMES` to parse and expand macros in one or more files.
94  For each filename on the command line, gomacro will parse it, expand macros,
95  then create a corresponding FILENAME.go with the parsed and macroexpanded
96  imports, declarations and statements.
97
98  To parse and macroexpand all *.gomacro files in a directory, run `gomacro -m -w DIRECTORY`
99
100## Installation
101
102### Prerequites
103
104- [Go 1.9+](https://golang.org/doc/install)
105
106### Supported platforms
107
108Gomacro is pure Go, and in theory it should work on any platform supported by the Go compiler.
109The following combinations are tested and known to work:
110
111- Linux: amd64, 386, arm64, arm, mips, ppc64le
112- Mac OS X: amd64, 386 (386 binaries running on amd64 system)
113- Windows: amd64, 386
114- FreeBSD: amd64, 386
115- Android: arm64, arm (tested with [Termux](https://termux.com/) and the Go compiler distributed with it)
116
117### How to install
118
119  The command
120  ```
121  go get -u github.com/cosmos72/gomacro
122  ```
123  downloads, compiles and installs gomacro and its dependencies
124
125## Current Status
126
127Almost complete.
128
129The main limitations and missing features are:
130
131* importing 3<sup>rd</sup> party libraries at runtime currently only works on Linux and Mac OS X.
132  On other systems as Windows, Android and *BSD it is cumbersome and requires recompiling - see [Importing packages](#importing-packages).
133* some corner cases using interpreted interfaces, as interface -> interface type assertions and type switches, are not implemented yet.
134* goto can only jump backward, not forward
135* out-of-order code is under testing - some corner cases, as for example out-of-order declarations
136  used in keys of composite literals, are not supported.
137  Clearly, at REPL code is still executed as soon as possible, so it makes a difference mostly
138  if you separate multiple declarations with ; on a single line. Example: `var a = b; var b = 42`
139  Support for "batch mode" is in progress - it reads as much source code as possible before executing it,
140  and it's useful mostly to execute whole files or directories.
141
142The [documentation](doc/) also contains the [full list of features and limitations](doc/features-and-limitations.md)
143
144## Extensions
145
146Compared to compiled Go, gomacro supports several extensions:
147
148* generics (experimental) - see [Generics](#generics)
149
150* an integrated debugger, see [Debugger](#debugger)
151
152* configurable special commands. Type `:help` at REPL to list them,
153  and see [cmd.go:37](https://github.com/cosmos72/gomacro/blob/master/fast/cmd.go#L37)
154  for the documentation and API to define new ones.
155
156* untyped constants can be manipulated directly at REPL. Examples:
157    ```
158	gomacro> 1<<100
159	{int 1267650600228229401496703205376}	// untyped.Lit
160	gomacro> const c = 1<<100; c * c / 100000000000
161	{int 16069380442589902755419620923411626025222029937827}	// untyped.Lit
162	```
163  This provides a handy arbitrary-precision calculator.
164
165  Note: operations on large untyped integer constants are always exact,
166  while operations on large untyped float constants are implemented with `go/constant.Value`,
167  and are exact as long as both numerator and denominator are <= 5e1232.
168
169  Beyond that, `go/constant.Value` switches from `*big.Rat` to `*big.Float`
170  with precision = 512, which can accumulate rounding errors.
171
172  If you need **exact** results, convert the untyped float constant to `*big.Rat`
173  (see next item) before exceeding 5e1232.
174
175* untyped constants can be converted implicitly to `*big.Int`, `*big.Rat` and `*big.Float`. Examples:
176    ```go
177	import "math/big"
178	var i *big.Int = 1<<1000                 // exact - would overflow int
179	var r *big.Rat = 1.000000000000000000001 // exact - different from 1.0
180	var s *big.Rat = 5e1232                  // exact - would overflow float64
181	var t *big.Rat = 1e1234                  // approximate, exceeds 5e1232
182	var f *big.Float = 1e646456992           // largest untyped float constant that is different from +Inf
183    ```
184  Note: every time such a conversion is evaluated, it creates a new value - no risk to modify the constant.
185
186  Be aware that converting a huge value to string, as typing `f` at REPL would do, can be very slow.
187
188* macros, quoting and quasiquoting (to be documented)
189
190and slightly relaxed checks:
191
192* unused variables and unused return values never cause errors
193
194## Examples
195
196Some short, notable examples - to run them on non-Linux platforms, see [Importing packages](#importing-packages) first.
197
198### plot mathematical functions
199
200* install libraries: `go get gonum.org/v1/plot gonum.org/v1/plot/plotter gonum.org/v1/plot/vg`
201* start the interpreter: `gomacro`
202* at interpreter prompt, paste the whole Go code listed at https://github.com/gonum/plot/wiki/Example-plots#functions
203  (the source code starts after the picture under the section "Functions", and ends just before the section "Histograms")
204* still at interpreter prompt, enter `main()`
205  If all goes well, it will create a file named "functions.png" in current directory containing the plotted functions.
206
207### simple mandelbrot web server
208
209* install libraries: `go get github.com/sverrirab/mandelbrot-go`
210* chdir to mandelbrot-go source folder: `cd; cd go/src/github.com/sverrirab/mandelbrot-go`
211* start interpreter with arguments: `gomacro -i mbrot.go`
212* at interpreter prompt, enter `init(); main()`
213* visit http://localhost:8090/
214  Be patient, rendering and zooming mandelbrot set with an interpreter is a little slow.
215
216Further examples are listed by [Gophernotes](https://github.com/gopherdata/gophernotes/#example-notebooks-dowload-and-run-them-locally-follow-the-links-to-view-in-github-or-use-the-jupyter-notebook-viewer)
217
218## Importing packages
219
220Gomacro supports the standard Go syntax `import`, including package renaming. Examples:
221```go
222import "fmt"
223import (
224    "io"
225    "net/http"
226    r "reflect"
227)
228```
229Third party packages - i.e. packages not in Go standard library - can also be imported
230with the same syntax, as long as the package is **already** installed.
231
232To install a package, follow its installation procedure: quite often it is the command `go get PACKAGE-PATH`
233
234The next steps depend on the system you are running gomacro on:
235
236### Linux and Mac OS X
237
238If you are running gomacro on Linux or Mac OS X, `import` will then just work. Example:
239```
240$ go get gonum.org/v1/plot
241$ gomacro
242[greeting message...]
243
244gomacro> import "gonum.org/v1/plot"
245// debug: created file "/home/max/src/gomacro_imports/gonum.org/v1/plot/plot.go"...
246// debug: compiling "/home/max/go/src/gomacro_imports/gonum.org/v1/plot/plot.go" ...
247gomacro> plot.New()
248&{...} // *plot.Plot
249<nil>  // error
250```
251
252Note: internally, gomacro will compile and load a Go plugin containing the package's exported declarations.
253Go plugins require Go 1.8+ on Linux and Go 1.10.2+ on Mac OS X.
254
255**WARNING** On Mac OS X, **never** execute `strip gomacro`: it breaks plugin support,
256            and loading third party packages stops working.
257
258
259### Other systems
260
261On all other systems as Windows, Android and *BSD you can still use `import`, but there are some more steps.
262Example:
263```
264$ go get gonum.org/v1/plot
265$ gomacro
266[greeting message...]
267
268gomacro> import "gonum.org/v1/plot"
269// warning: created file "/home/max/go/src/github.com/cosmos72/gomacro/imports/thirdparty/gonum_org_v1_plot.go", recompile gomacro to use it
270```
271
272Now quit gomacro, recompile and reinstall it:
273```
274gomacro> :quit
275$ go install github.com/cosmos72/gomacro
276```
277
278Finally restart it. Your import is now linked **inside** gomacro and will work:
279```
280$ gomacro
281[greeting message...]
282
283gomacro> import "gonum.org/v1/plot"
284gomacro> plot.New()
285&{...} // *plot.Plot
286<nil>  // error
287```
288
289Note: if you need several packages, you can first `import` all of them,
290then quit and recompile gomacro only once.
291
292## Generics
293
294gomacro contains an experimental version of Go generics.
295
296For the experience report written while implementing them, see [doc/generics.md](doc/generics.md)
297
298They are in beta status, and at the moment only generic types and functions are supported.
299Syntax and examples:
300```go
301template[T,U] type Pair struct { First T; Second U }
302
303var pair Pair#[complex64, struct{}]
304
305// equivalent:
306pair := Pair#[complex64, struct{}] {}
307
308
309template[T] func Sum(args ...T) T {
310	var sum T // exploit zero value of T
311	for _, elem := range args {
312		sum += elem
313	}
314	return sum
315}
316Sum#[int]         // returns func(...int) int
317Sum#[int] (1,2,3) // returns int(6)
318
319Sum#[complex64]                 // returns func(...complex64) complex64
320Sum#[complex64] (1.1+2.2i, 3.3) // returns complex64(4.4+2.2i)
321
322Sum#[string]                         // returns func(...string) string
323Sum#[string]("abc.","def.","xy","z") // returns "abc.def.xyz"
324
325template[T,U] func Transform(slice []T, trans func(T) U) []U {
326	ret := make([]U, len(slice))
327	for i := range slice {
328		ret[i] = trans(slice[i])
329	}
330	return ret
331}
332Transform#[string,int] // returns func([]string, func(string) int) []int
333
334// returns []int{3, 2, 1} i.e. the len() of each string in input slice:
335
336Transform#[string,int]([]string{"abc","xy","z"}, func(s string) int { return len(s) })
337
338// Partial and full specialization of templates are supported.
339// Together with recursive templates, they also (incidentally)
340// provide Turing completeness at compile-time:
341
342// The following example uses recursion and full specialization
343// to compute fibonacci sequence at compile time.
344
345// general case: encode Fib#[N] in the length of array type.
346template[N] type Fib [
347	len((*Fib#[N-1])(nil)) +
348	len((*Fib#[N-2])(nil))   ]int
349
350template[] for[2] type Fib [1]int // specialization for Fib#[2]
351template[] for[1] type Fib [1]int // specialization for Fib#[1]
352
353const Fib30 = len((*Fib#[30])(nil)) // compile-time constant
354
355```
356Current limitations:
357* instantiation is on-demand, but template arguments #[...] must be explicit.
358* template methods not supported yet.
359
360Observation: the compile-time Turing completeness provided by these C++-style templates
361is really poorly readable, for three reasons:
362* iteration must be written as recursion
363* `if` must be written as template specialization, outside the main template
364* integers must be encoded inside types, for example in the length of array types
365
366In the author's opinion, compile-time Turing completeness is a very enticing
367feature for several use cases and for a non-trivial percentage of developers.
368
369If the only way to get such feature is with poorly readable (ab)use of templates,
370the result is a lot of poorly readable template code.
371
372If Turing-complete templates are ever added to Go (or any other language)
373it is thus very important to also provide an alternative, more natural syntax
374to perform Turing-complete computation at compile-time. An example
375could be: `const foo(args)` where the function `foo` must respect certain
376constraints (to be defined) in order to be callable at compile time.
377
378For a more detailed discussion, see [doc/generics.md](doc/generics.md).
379
380## Debugger
381
382Since version 2.6, gomacro also has an integrated debugger.
383There are three ways to enter it:
384* hit CTRL+C while interpreted code is running.
385* type `:debug STATEMENT-OR-FUNCTION-CALL` at the prompt.
386* add a statement (an expression is not enough) `"break"` or `_ = "break"` to your code, then execute it normally.
387
388In all cases, execution will be suspended and you will get a `debug>` prompt, which accepts the following commands:
389`step`, `next`, `finish`, `continue`, `env [NAME]`, `inspect EXPR`, `list`, `print EXPR-OR-STATEMENT`
390
391Also,
392* commands can be abbreviated.
393* `print` fully supports expressions or statements with side effects, including function calls and modifying local variables.
394* `env` without arguments prints all global and local variables.
395* an empty command (i.e. just pressing enter) repeats the last command.
396
397Only interpreted statements can be debugged: expressions and compiled code will be executed, but you cannot step into them.
398
399The debugger is quite new, and may have some minor glitches.
400
401## Why it was created
402
403First of all, to experiment with Go :)
404
405Second, to simplify Go code generation tools (keep reading for the gory details)
406
407---
408
409Problem: "go generate" and many other Go tools automatically create
410Go source code from some kind of description - usually an interface
411specifications as WSDL, XSD, JSON...
412
413Such specification may be written in Go, for example when creating JSON
414marshallers/unmarshallers from Go structs, or in some other language,
415for example when creating Go structs from JSON sample data.
416
417In both cases, a variety of external programs are needed to
418generate Go source code: such programs need to be installed
419separately from the code being generated and compiled.
420
421Also, Go is currently lacking generics (read: C++-like templates)
422because of the rationale "we do not yet know how to do them right,
423and once you do them wrong everybody is stuck with them"
424
425The purpose of Lisp-like macros is to execute arbitrary code
426while compiling, **in particular** to generate source code.
427
428This makes them very well suited (although arguably a bit low level)
429for both purposes: code generation and C++-like templates, which
430are a special case of code generation - for a demonstration of how
431to implement C++-like templates on top of Lisp-like macros,
432see for example the project https://github.com/cosmos72/cl-parametric-types
433from the same author.
434
435Building a Go interpreter that supports Lisp-like macros,
436allows to embed all these code-generation activities
437into regular Go source code, without the need for external programs
438(except for the interpreter itself).
439
440As a free bonus, we get support for Eval()
441
442## LEGAL
443
444Gomacro is distributed under the terms of [Mozilla Public License 2.0](LICENSE)
445or any later version.
446