{"id":20017262,"url":"https://github.com/jig/lisp","last_synced_at":"2025-03-02T03:15:41.987Z","repository":{"id":40390901,"uuid":"402513462","full_name":"jig/lisp","owner":"jig","description":"Derived MAL (Lisp/Clojure) implementation","archived":false,"fork":false,"pushed_at":"2025-03-01T17:01:49.000Z","size":309,"stargazers_count":3,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-01T18:19:16.591Z","etag":null,"topics":["clojure","go","golang","lisp"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mpl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jig.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-09-02T17:50:52.000Z","updated_at":"2025-01-19T11:30:14.000Z","dependencies_parsed_at":"2025-03-01T18:19:15.994Z","dependency_job_id":"cf25a54b-b583-44e4-a0ff-59f08970b06a","html_url":"https://github.com/jig/lisp","commit_stats":null,"previous_names":["jig/mal"],"tags_count":17,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jig%2Flisp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jig%2Flisp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jig%2Flisp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jig%2Flisp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jig","download_url":"https://codeload.github.com/jig/lisp/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":241451677,"owners_count":19964901,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["clojure","go","golang","lisp"],"created_at":"2024-11-13T08:15:25.682Z","updated_at":"2025-03-02T03:15:41.969Z","avatar_url":"https://github.com/jig.png","language":"Go","readme":"# lisp\n\nDerived from `kanaka/mal` Go implementation of a Lisp interpreter.\n`kanaka/mal` Lisp is _Clojure inspired_.\n\nKeeping 100% backwards compatibility with `kanaka/mal`.\nThere almost 100 implementations on almost 100 languages available on repository [kanaka/mal](https://github.com/kanaka/mal).\n\nThis derived implementation is focused on _embeddability_ in Go projects.\nSee [lisp main](./cmd/lisp) for an example on how to embed it in Go code.\n\nRequires Go 1.18.\n\nThis implementation uses [chzyer/readline](https://github.com/chzyer/readline) instead of C implented readline or libedit, making this implementation pure Go.\n\n# Changes\n\nChanges respect to [kanaka/mal](https://github.com/kanaka/mal):\n\n- Using `def` insted of `def!`, `try` instead of `try*`, etc. symbols\n- `atom` is multithread\n- Tests executed using Go test library. Original implementation uses a `runtest.py` in Python to keep all implementations compatible. But it makes the Go development less enjoyable. Tests files are the original ones, there is simply a new `runtest_test.go` that substitutes the original Python script\n- Some tests are actually in lisp (mal), using the macros commented in _Additions_ section (now only the test library itself). Well, actually not many at this moment, see \"Test file specs\" below\n- Reader regexp's are removed and substituted by an ad-hoc scanner [jig/scanner](https://github.com/jig/scanner)\n- `core` library moved to `lib/core`\n- Using [chzyer/readline](https://github.com/chzyer/readline) instead of C `readline` for the mal REPL\n- Multiline REPL\n- REPL history stored in `~/.lisp_history` (instead of kanaka/mal's `~/.mal-history`)\n- `(let () A B C)` returns `C` as Clojure `let` instead of `A`, and evaluates `A`, `B` and `C`\n- `(do)` returns nil as Clojure instead of panicking\n- `hash-map` creates maps or converts a Go object to a map if the marshaler is defined in Go for that object\n- `reduce-kv` added\n- `take`, `take-last`, `drop`, `drop-last`, `subvec` added\n\nTo test the implementation use:\n\n```bash\ngo test ./...\n```\n\n`go test` actually validates the `step*.mal` files.\n\nThere are some benchmarks as well:\n\n```bash\ngo test -benchmem -benchtime 5s -bench '^.+$' github.com/jig/lisp\n```\n\n# Additions\n\n- Debugger: prefix program name with `--debug`. File to debug is the sole argument supported\n- Errors return line position and stack trace\n- `(range a b)` returns a vector of integers from `a` to `b-1`\n- `(merge hm1 hm2)` returns the merge of two hash maps, second takes precedence\n- `(unbase64 string)`, `(unbase64 byteString)`, `(str2binary string)`, `(binary2str byteString)` to deal with `[]byte` variables\n- `(sleep ms)` sleeps `ms` milliseconds\n- Support of `¬` as string terminator to simplify JSON strings. Strings that start with `{\"` and end with `\"}` are printed using `¬`, otherwise strings are printed as usual (with `\"`). To escape a `¬` character in a `¬` delimited string you must escape it by doubling it: `¬Hello¬¬World!¬` would be printed as `Hello¬World`. This behaviour allows to not to have to escape `\"` nor `\\` characters\n- `(json-decode {} ¬{\"key\": \"value\"}¬)` to decode JSON to lisp hash map\n- `(json-encode obj)` JSON encodes either a lisp structure or a go. Example: `(json-encode (json-decode {} ¬{\"key\":\"value\",\"key1\": [{\"a\":\"b\",\"c\":\"d\"},2,3]}¬))`. Note that lisp vectors (e.g. `[1 2 3]`) and lisp lists (e.g. `(list 1 2 3)` are both converted to JSON vectors always. Decoding a JSON vector is done on a lisp vector always though\n- `(hash-map-decode (new-go-object) ¬{\"key\": \"value\"}¬)` to decode hash map to a Go struct if that struct has the appropiate Go marshaler\n- `(context (do ...))` provides a Go context. Context contents depend on Go, and might be passed to specific functions context compatible\n- Test minimal library to be used with `maltest` interpreter (see [./cmd/maltest/](./cmd/maltest/) folder). See below test specs\n- Project compatible with GitHub CodeSpaces. Press `.` on your keyboard and you are ready to deploy a CodeSpace with mal in it\n- `(assert expr \u0026 optional-error)` asserts expression is not `nil` nor `false`, otherwise it success returning `nil`\n- Errors are decorated with line numbers\n- `(rename-keys hm hmAlterKeys)` as in Clojure\n- `(get-in m ks)` to access nested values from a `m` map; `ks` must be a vector of hash map keys\n- `(uuid)` returns an 128 bit rfc4122 random UUID\n- `(split string cutset)` returns a lisp Vector of the elements splitted by the cutset (see [./tests/stepH_strings](./tests/stepH_strings.mal) for examples)\n- support of (hashed, unordered) sets. Only sets of strings or keywords supported. Use `#{}` for literal sets. Functions supported for sets: `set`, `set?`, `conj`, `get`, `assoc`, `dissoc`, `contains?`, `empty?`. `meta`, `with-meta` (see [./tests/stepA_mal](./tests/stepF_set.mal) and (see [./tests/stepA_mal](./tests/stepF_set.mal) for examples). `json-encode` will encode a set to a JSON array\n- `update`, `update-in` and `assoc-in` supported for hash maps and vectors\n- Go function `READ_WithPreamble` works like `READ` but supports placeholders to be filled on READ time (see [./placeholder_test.go](./placeholder_test.go) for som samples)\n- Added support for `finally` inside `try`. `finally` expression is evaluated for side effects only. `finally` is optional\n- Added `spew`\n- Added `future`, and `future-*` companion functions from Clojure\n- `type?` returns the type name string\n- `go-error`, `unwrap` and `panic` mapping to Go's `errors.New/fmt.Errorf`, `Unwrap` and `panic` respectively\n- `getenv`, `setenv` and `unsetenv` functions for environment variables\n\n\n# Embed Lisp in Go code\n\nYou execute lisp from Go code and get results from it back to Go. Example from [./example_test/example_test.go](./example_test/example_test.go):\n\n```go\nfunc ExampleEVAL() {\n\tnewEnv := env.NewEnv()\n\n\t// Load required lisp libraries\n\tfor _, library := range []struct {\n\t\tname string\n\t\tload func(newEnv types.EnvType) error\n\t}{\n\t\t{\"core mal\", nscore.Load},\n\t\t{\"core mal with input\", nscore.LoadInput},\n\t\t{\"command line args\", nscore.LoadCmdLineArgs},\n\t\t{\"core mal extended\", nscoreextended.Load},\n\t\t{\"assert\", nsassert.Load},\n\t} {\n\t\tif err := library.load(newEnv); err != nil {\n\t\t\tlog.Fatalf(\"Library Load Error: %v\", err)\n\t\t}\n\t}\n\n\t// parse (READ) lisp code\n\tast, err := lisp.READ(`(+ 2 2)`, nil)\n\tif err != nil {\n\t\tlog.Fatalf(\"READ error: %v\", err)\n\t}\n\n\t// eval AST\n\tresult, err := lisp.EVAL(ast, newEnv, nil)\n\tif err != nil {\n\t\tlog.Fatalf(\"EVAL error: %v\", err)\n\t}\n\n\t// use result\n\tif result.(int) != 4 {\n\t\tlog.Fatalf(\"Result check error: %v\", err)\n\t}\n\n\t// optionally print resulting AST\n\tfmt.Println(lisp.PRINT(result))\n\t// Output: 4\n}\n```\n\n# L notation\n\nYou may generate lisp Go structures without having to parse lisp strings, by using Go `L` notation.\n\n```go\nvar (\n    prn = S(\"prn\")\n    str = S(\"str\")\n)\n\n// (prn (str \"hello\" \" \" \"world!\"))\nsampleCode := L(prn, L(str, \"hello\", \" \", \"world!\"))\n\nEVAL(sampleCode, newTestEnv(), nil)\n```\n\nSee [./helloworldlnotationexample_test.go](./helloworldlnotationexample_test.go) and [./lnotation/lnotation_test.go](./lnotation/lnotation_test.go).\n\n# Test file specs\n\nExecute the testfile with:\n\n```bash\n$ lisp --test .\n```\n\nAnd a minimal test example `sample_test.mal`:\n\n```lisp\n(test.suite \"complete tests\"\n    (assert-true \"2 + 2 = 4 is true\" (= 4 (+ 2 2)))\n    (assert-false \"2 + 2 = 5 is false\" (= 5 (+ 2 2)))\n    (assert-throws \"0 / 0 throws an error\" (/ 0 0)))\n```\n\nSome benchmark of the implementations:\n\n```bash\n$ go test -bench \".+\" -benchtime 2s\n```\n\n# Install\n\n```bash\ncd cmd/lisp\ngo install\n```\n\n# Execute REPL\n\n```bash\nlisp\n```\n\nUse \u003ckbd\u003eCtrl\u003c/kbd\u003e + \u003ckbd\u003eD\u003c/kbd\u003e to exit Lisp REPL.\n\n# Execute lisp program\n\n```bash\nlisp helloworld.lisp\n```\n\n# Licence\n\nThis \"lisp\" implementation is licensed under the MPL 2.0 (Mozilla Public License 2.0). See [LICENCE](./LICENCE) for more details.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjig%2Flisp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjig%2Flisp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjig%2Flisp/lists"}