├── .circleci └── config.yml ├── .gitattributes ├── .gitignore ├── DEVELOPER.md ├── LIBRARIES.md ├── LICENSE ├── README.md ├── all-tests.sh ├── build-all.sh ├── build-arm.sh ├── build.sh ├── core ├── array_map.go ├── array_vector.go ├── buffer.go ├── buffered_reader.go ├── channel.go ├── code.go ├── common.go ├── data.go ├── data │ ├── better_cond.joke │ ├── core.joke │ ├── hiccup.joke │ ├── linter_all.joke │ ├── linter_clj.joke │ ├── linter_cljs.joke │ ├── linter_cljx.joke │ ├── linter_joker.joke │ ├── pprint.joke │ ├── repl.joke │ ├── set.joke │ ├── template.joke │ ├── test.joke │ ├── tools_cli.joke │ └── walk.joke ├── deps.go ├── environment.go ├── environment_fast_init.go ├── environment_slow_init.go ├── eval.go ├── expr.go ├── file.go ├── format.go ├── gen │ └── gen_types.go ├── gen_code │ └── gen_code.go ├── gen_go │ ├── gen_go.go │ ├── sorter.go │ └── unsafe_stuff.go ├── hash_map.go ├── intern.go ├── intern_slow_init.go ├── io_reader.go ├── io_writer.go ├── line_runereader.go ├── list.go ├── map.go ├── ns.go ├── numbers.go ├── object.go ├── object_slow_init.go ├── pack.go ├── parse.go ├── parse_slow_init.go ├── procs.go ├── procs_slow_init.go ├── read.go ├── reader.go ├── rune_window.go ├── seq.go ├── set.go ├── spew_disabled.go ├── spew_enabled.go ├── stringable.go ├── types_assert_gen.go ├── types_info_gen.go └── vector.go ├── docs ├── dash.sh ├── dash │ └── dashing.json ├── generate-docs.joke ├── generate-xml.joke ├── index.html ├── joker.base64.html ├── joker.better-cond.html ├── joker.bolt.html ├── joker.core.html ├── joker.crypto.html ├── joker.csv.html ├── joker.filepath.html ├── joker.git.html ├── joker.hex.html ├── joker.hiccup.html ├── joker.html.html ├── joker.http.html ├── joker.io.html ├── joker.json.html ├── joker.markdown.html ├── joker.math.html ├── joker.os.html ├── joker.pprint.html ├── joker.repl.html ├── joker.runtime.html ├── joker.set.html ├── joker.strconv.html ├── joker.string.html ├── joker.template.html ├── joker.test.html ├── joker.tgz ├── joker.time.html ├── joker.tools.cli.html ├── joker.url.html ├── joker.uuid.html ├── joker.walk.html ├── joker.xml ├── joker.yaml.html ├── joker32.png ├── main.css ├── main.js ├── misc │ ├── bigfloat.md │ └── lib-loader.md └── templates │ ├── index.html │ ├── link-item.html │ ├── main.js │ ├── ns-summary.html │ ├── ns.html │ ├── special-form.html │ ├── type-summary.html │ ├── usage-with-types.html │ ├── usage.html │ ├── var.html │ └── xml.html ├── epl-v10.html ├── eval-tests.sh ├── exiters.go ├── exiters_plan9.go ├── exiters_windows.go ├── flag-tests.sh ├── formatter-tests.sh ├── go.mod ├── go.sum ├── linter-tests.sh ├── main.go ├── release.joke ├── repl.go ├── repl_plan9.go ├── run.sh ├── shadow.sh ├── std ├── addmeta.tmpl ├── arity.tmpl ├── base64.joke ├── base64 │ ├── a_base64.go │ ├── a_base64_slow_init.go │ └── base64_native.go ├── bolt.joke ├── bolt │ ├── a_bolt.go │ ├── a_bolt_slow_init.go │ └── bolt_native.go ├── crypto.joke ├── crypto │ ├── a_crypto.go │ ├── a_crypto_slow_init.go │ └── crypto_native.go ├── csv.joke ├── csv │ ├── a_csv.go │ ├── a_csv_slow_init.go │ └── csv_native.go ├── filepath.joke ├── filepath │ ├── a_filepath.go │ ├── a_filepath_slow_init.go │ └── filepath_native.go ├── fn.tmpl ├── generate-std.joke ├── git.joke ├── git │ ├── a_git.go │ ├── a_git_slow_init.go │ └── git_native.go ├── hex.joke ├── hex │ ├── a_hex.go │ └── a_hex_slow_init.go ├── html.joke ├── html │ ├── a_html.go │ ├── a_html_fast_init.go │ └── a_html_slow_init.go ├── http.joke ├── http │ ├── a_http.go │ ├── a_http_slow_init.go │ └── http_native.go ├── intern.tmpl ├── io.joke ├── io │ ├── a_io.go │ ├── a_io_slow_init.go │ └── io_native.go ├── json.joke ├── json │ ├── a_json.go │ ├── a_json_slow_init.go │ └── json_native.go ├── markdown.joke ├── markdown │ ├── a_markdown.go │ ├── a_markdown_slow_init.go │ └── markdown_native.go ├── math.joke ├── math │ ├── a_math.go │ ├── a_math_slow_init.go │ └── math_native.go ├── os.joke ├── os │ ├── a_os.go │ ├── a_os_slow_init.go │ ├── os_native.go │ ├── sh.go │ └── sh_plan9.go ├── package-fast.tmpl ├── package-slow.tmpl ├── package.tmpl ├── runtime.joke ├── runtime │ ├── a_runtime.go │ └── a_runtime_slow_init.go ├── strconv.joke ├── strconv │ ├── a_strconv.go │ └── a_strconv_slow_init.go ├── string.joke ├── string │ ├── a_string.go │ ├── a_string_fast_init.go │ ├── a_string_slow_init.go │ └── string_native.go ├── time.joke ├── time │ ├── a_time.go │ ├── a_time_slow_init.go │ └── time_native.go ├── url.joke ├── url │ ├── a_url.go │ ├── a_url_slow_init.go │ └── url_native.go ├── uuid.joke ├── uuid │ ├── a_uuid.go │ ├── a_uuid_slow_init.go │ └── uuid_native.go ├── yaml.joke └── yaml │ ├── a_yaml.go │ ├── a_yaml_slow_init.go │ └── yaml_native.go ├── test-format.sh ├── test-linter.sh ├── tests ├── 1.clj ├── eval │ ├── atoms.joke │ ├── better-cond-test.joke │ ├── bolt.joke │ ├── classpath │ │ ├── a │ │ │ └── b │ │ │ │ └── c.joke │ │ ├── b │ │ │ └── c.joke │ │ ├── d │ │ │ └── e │ │ │ │ └── f.joke │ │ ├── g │ │ │ └── h │ │ │ │ └── i.joke │ │ ├── input.joke │ │ ├── stdout.txt │ │ └── x │ │ │ └── y │ │ │ ├── q │ │ │ └── r │ │ │ │ └── s.joke │ │ │ └── z.joke │ ├── clojure_walk.joke │ ├── control.joke │ ├── core.joke │ ├── corelibs │ │ ├── input.joke │ │ └── stdout.txt │ ├── crypto-test.joke │ ├── csv.joke │ ├── deps-test.joke │ ├── deps.joke │ ├── format.joke │ ├── hash.joke │ ├── hiccup.joke │ ├── json.joke │ ├── large-forked-stdout │ │ ├── input.joke │ │ ├── lots-of-stderr.joke │ │ ├── stderr.txt │ │ └── stdout.txt │ ├── load-file │ │ ├── input.joke │ │ ├── parse-error.joke │ │ └── stdout.txt │ ├── macro-test.joke │ ├── map-test.joke │ ├── markdown.joke │ ├── math.joke │ ├── multi-methods │ │ ├── input.joke │ │ └── stdout.txt │ ├── multimethods.joke │ ├── numbers.joke │ ├── os.joke │ ├── printer.joke │ ├── read-line │ │ ├── input.joke │ │ ├── lines.txt │ │ ├── stdin.txt │ │ └── stdout.txt │ ├── reader-conditionals.joke │ ├── reader.joke │ ├── stdin-pipe │ │ ├── input.joke │ │ ├── stdin.txt │ │ └── stdout.txt │ ├── string.joke │ ├── tools_cli.joke │ ├── types.joke │ └── url-test.joke ├── flags │ ├── config │ │ └── .joker │ ├── input-warning.clj │ ├── input.clj │ ├── input.cljs │ ├── input.edn │ ├── input.joke │ ├── macro.clj │ └── script-flags.joke ├── formatter │ └── main │ │ ├── input.clj │ │ └── output.clj ├── lib │ └── test-local │ │ └── lib.joke ├── lib1.joke ├── lib2.joke ├── lib3.joke ├── linter │ ├── anonymous-fn │ │ ├── input.clj │ │ └── output.txt │ ├── case │ │ ├── input.clj │ │ └── output.txt │ ├── classnames │ │ ├── input.clj │ │ └── output.txt │ ├── cljs-interop │ │ ├── input.cljs │ │ └── output.txt │ ├── cond │ │ ├── input.clj │ │ └── output.txt │ ├── conditionals-clj-1 │ │ ├── input.clj │ │ └── output.txt │ ├── conditionals-clj-2 │ │ ├── input.clj │ │ └── output.txt │ ├── conditionals-clj-3 │ │ ├── input.clj │ │ └── output.txt │ ├── conditionals-cljs │ │ ├── input.cljs │ │ └── output.txt │ ├── condp │ │ ├── input.clj │ │ └── output.txt │ ├── constr-call-cljs │ │ ├── input.cljs │ │ └── output.txt │ ├── def │ │ ├── input.clj │ │ └── output.txt │ ├── defmethod │ │ ├── input.clj │ │ └── output.txt │ ├── defn-0 │ │ ├── input.clj │ │ └── output.txt │ ├── defn-1 │ │ ├── input.clj │ │ └── output.txt │ ├── defn-2 │ │ ├── input.clj │ │ └── output.txt │ ├── defn-3 │ │ ├── input.clj │ │ └── output.txt │ ├── defprotocol │ │ ├── .joker │ │ ├── input.clj │ │ └── output.txt │ ├── defrecord │ │ ├── input.clj │ │ └── output.txt │ ├── deftest │ │ ├── input.clj │ │ └── output.txt │ ├── do │ │ ├── input.clj │ │ └── output.txt │ ├── doseq │ │ ├── input.clj │ │ └── output.txt │ ├── duplicate-def │ │ ├── input.clj │ │ └── output.txt │ ├── exclude-list │ │ ├── input.clj │ │ └── output.txt │ ├── extend-protocol │ │ ├── input.clj │ │ └── output.txt │ ├── extend-type-cljs │ │ ├── input.cljs │ │ └── output.txt │ ├── extend-type │ │ ├── input.clj │ │ └── output.txt │ ├── fn-0 │ │ ├── input.clj │ │ └── output.txt │ ├── fn-1 │ │ ├── input.clj │ │ └── output.txt │ ├── fn-2 │ │ ├── input.clj │ │ └── output.txt │ ├── fn-3 │ │ ├── input.clj │ │ └── output.txt │ ├── fn-unused-parameters │ │ ├── .joker │ │ ├── input.clj │ │ └── output.txt │ ├── fn-with-empty-body │ │ ├── input.clj │ │ └── output.txt │ ├── for-1 │ │ ├── input.clj │ │ └── output.txt │ ├── for-2 │ │ ├── input.clj │ │ └── output.txt │ ├── for-3 │ │ ├── input.clj │ │ └── output.txt │ ├── function-call-cljs │ │ ├── input.cljs │ │ └── output.txt │ ├── function-call │ │ ├── input.clj │ │ └── output.txt │ ├── if-let-1 │ │ ├── input.clj │ │ └── output.txt │ ├── if-let-2 │ │ ├── input.clj │ │ └── output.txt │ ├── if-let-3 │ │ ├── input.clj │ │ └── output.txt │ ├── if │ │ ├── .joker │ │ ├── input.clj │ │ └── output.txt │ ├── inline-def │ │ ├── input.clj │ │ └── output.txt │ ├── learned-macros │ │ ├── .jokerd │ │ │ └── linter.clj │ │ ├── input.clj │ │ └── output.txt │ ├── let-1 │ │ ├── input.clj │ │ └── output.txt │ ├── let │ │ ├── input.clj │ │ └── output.txt │ ├── letfn │ │ ├── input.clj │ │ └── output.txt │ ├── linter-files │ │ ├── .joker │ │ ├── .jokerd │ │ │ └── linter.clj │ │ ├── input.clj │ │ └── output.txt │ ├── macro-call-1 │ │ ├── input.clj │ │ └── output.txt │ ├── macro-call │ │ ├── input.clj │ │ └── output.txt │ ├── merge │ │ ├── input.clj │ │ └── output.txt │ ├── namespaced-maps │ │ ├── input.clj │ │ └── output.txt │ ├── ns-1 │ │ ├── input.clj │ │ └── output.txt │ ├── ns-10 │ │ ├── input.clj │ │ └── output.txt │ ├── ns-2 │ │ ├── input.clj │ │ └── output.txt │ ├── ns-3 │ │ ├── input.clj │ │ └── output.txt │ ├── ns-4 │ │ ├── input.clj │ │ └── output.txt │ ├── ns-5 │ │ ├── input.clj │ │ └── output.txt │ ├── ns-6 │ │ ├── input.clj │ │ └── output.txt │ ├── ns-7 │ │ ├── input.clj │ │ └── output.txt │ ├── ns-8 │ │ ├── input.clj │ │ └── output.txt │ ├── ns-9 │ │ ├── input.clj │ │ └── output.txt │ ├── ns-cljs-1 │ │ ├── input.cljs │ │ └── output.txt │ ├── ns-cljs-2 │ │ ├── .joker │ │ ├── input.cljs │ │ └── output.txt │ ├── ns-cljs-3 │ │ ├── input.cljs │ │ └── output.txt │ ├── ns-cljs-4 │ │ ├── input.cljs │ │ └── output.txt │ ├── ns-cljs-5 │ │ ├── input.cljs │ │ └── output.txt │ ├── proxy │ │ ├── input.clj │ │ └── output.txt │ ├── reader │ │ ├── input.clj │ │ └── output.txt │ ├── recur │ │ ├── input.clj │ │ └── output.txt │ ├── redundant-do │ │ ├── input.clj │ │ └── output.txt │ ├── regex │ │ ├── input.clj │ │ └── output.txt │ ├── reify │ │ ├── input.clj │ │ └── output.txt │ ├── symbol-resolution-1 │ │ ├── .joker │ │ ├── input.clj │ │ └── output.txt │ ├── symbol-resolution-2 │ │ ├── input.clj │ │ └── output.txt │ ├── symbol-resolution-cljs │ │ ├── input.cljs │ │ └── output.txt │ ├── symbol-resolution │ │ ├── input.clj │ │ └── output.txt │ ├── tagged-literals │ │ ├── .joker │ │ ├── input.clj │ │ └── output.txt │ ├── threading-macros-1 │ │ ├── .joker │ │ ├── input.clj │ │ └── output.txt │ ├── threading-macros │ │ ├── input.clj │ │ └── output.txt │ ├── try │ │ ├── input.clj │ │ └── output.txt │ ├── types-1 │ │ ├── input.clj │ │ └── output.txt │ ├── types-2 │ │ ├── input.clj │ │ └── output.txt │ ├── types-3 │ │ ├── input.clj │ │ └── output.txt │ ├── types-4 │ │ ├── input.clj │ │ └── output.txt │ ├── types-cljs │ │ ├── input.cljs │ │ └── output.txt │ ├── unused-as │ │ ├── .joker │ │ ├── input.clj │ │ └── output.txt │ ├── unused-bindings │ │ ├── input.clj │ │ └── output.txt │ ├── unused-keys │ │ ├── .joker │ │ ├── input.clj │ │ └── output.txt │ ├── unused-ns │ │ ├── .joker │ │ ├── input.clj │ │ └── output.txt │ ├── unused-vars-1 │ │ ├── input.clj │ │ └── output.txt │ ├── unused-vars │ │ ├── input.clj │ │ └── output.txt │ ├── valid-indent │ │ ├── input.clj │ │ └── output.txt │ └── when │ │ ├── input.clj │ │ └── output.txt ├── main.clj ├── run-eval-tests.joke ├── run-flag-tests.joke ├── run-tests.joke └── test-helper.joke └── tools ├── cljs-macros.joke ├── gen-defns.clj ├── gen-defns.cljs └── sum256dir ├── .gitignore └── main.go /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | jobs: 3 | build: 4 | docker: 5 | - image: cimg/go:1.23 6 | working_directory: /home/circleci/go/src/github.com/candid82/joker 7 | steps: 8 | - checkout 9 | - run: 10 | name: go get liner 11 | command: go get -d -v github.com/candid82/liner 12 | - run: 13 | name: go get profile 14 | command: go get -d -v github.com/pkg/profile 15 | - run: 16 | name: go get yaml 17 | command: go get -d -v gopkg.in/yaml.v2 18 | - run: 19 | name: go generate 20 | command: go generate -v ./... 21 | - run: 22 | name: go build 23 | command: go build -v github.com/candid82/joker 24 | - run: 25 | name: linter tests 26 | command: ./linter-tests.sh 27 | - run: 28 | name: flag tests 29 | command: ./flag-tests.sh 30 | - run: 31 | name: eval tests 32 | command: ./eval-tests.sh 33 | - run: 34 | name: formatter tests 35 | command: ./formatter-tests.sh 36 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Set the default behavior. 2 | * text eol=lf 3 | 4 | # DOS line endings on .bat files. 5 | *.bat text=auto 6 | 7 | *.tgz binary 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | joker 2 | .vscode 3 | .idea 4 | core/a_*.go 5 | *.exe 6 | *~ 7 | *.zip 8 | *.docset 9 | .DS_Store 10 | docs/dash/*.html 11 | docs/dash/*.css 12 | docs/dash/*.js 13 | -------------------------------------------------------------------------------- /all-tests.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | fail=0 4 | 5 | for test_script in *-tests.sh 6 | do 7 | if [ $test_script != "all-tests.sh" ]; then 8 | echo >&2 "RUNNING $test_script" 9 | ./$test_script 10 | if [[ $? != 0 ]]; then 11 | echo ${test_script//.sh/} failed. 12 | fail=1 13 | fi 14 | fi 15 | done 16 | 17 | if [[ $fail == 0 ]]; then 18 | echo >&2 "All tests passed." 19 | fi 20 | 21 | exit $fail 22 | -------------------------------------------------------------------------------- /build-all.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -eu 3 | 4 | export GOARCH=amd64 5 | export GOOS= 6 | 7 | for GOOS in darwin linux windows freebsd netbsd openbsd; do 8 | if ! go build; then 9 | echo \ 10 | error: \ 11 | "building for GOARCH=$GOARCH GOOS=$GOOS failed" \ 12 | >/dev/stderr \ 13 | ; 14 | continue 15 | fi 16 | 17 | case "$GOOS" in 18 | darwin) os=mac ;; 19 | windows) os=win ;; 20 | *) os=$GOOS ;; 21 | esac 22 | [[ $GOOS = windows ]] && executable=joker.exe || executable=joker 23 | 24 | zip -9 joker-"$os"-"$GOARCH".zip "$executable" 25 | done 26 | -------------------------------------------------------------------------------- /build-arm.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | GOOS=linux GOARCH=386 go generate ./... 4 | GOOS=linux GOARCH=arm go build 5 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | go generate ./... && go vet ./... && go build 4 | -------------------------------------------------------------------------------- /core/buffer.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "bytes" 5 | "unsafe" 6 | ) 7 | 8 | type ( 9 | Buffer struct { 10 | *bytes.Buffer 11 | hash uint32 12 | } 13 | ) 14 | 15 | func MakeBuffer(b *bytes.Buffer) *Buffer { 16 | res := &Buffer{b, 0} 17 | res.hash = HashPtr(uintptr(unsafe.Pointer(res))) 18 | return res 19 | } 20 | 21 | func (b *Buffer) ToString(escape bool) string { 22 | return b.String() 23 | } 24 | 25 | func (b *Buffer) Equals(other interface{}) bool { 26 | return b == other 27 | } 28 | 29 | func (b *Buffer) GetInfo() *ObjectInfo { 30 | return nil 31 | } 32 | 33 | func (b *Buffer) GetType() *Type { 34 | return TYPE.Buffer 35 | } 36 | 37 | func (b *Buffer) Hash() uint32 { 38 | return b.hash 39 | } 40 | 41 | func (b *Buffer) WithInfo(info *ObjectInfo) Object { 42 | return b 43 | } 44 | -------------------------------------------------------------------------------- /core/buffered_reader.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "bufio" 5 | "io" 6 | "unsafe" 7 | ) 8 | 9 | type ( 10 | BufferedReader struct { 11 | *bufio.Reader 12 | hash uint32 13 | } 14 | ) 15 | 16 | func MakeBufferedReader(rd io.Reader) *BufferedReader { 17 | res := &BufferedReader{bufio.NewReader(rd), 0} 18 | res.hash = HashPtr(uintptr(unsafe.Pointer(res))) 19 | return res 20 | } 21 | 22 | func (br *BufferedReader) ToString(escape bool) string { 23 | return "#object[BufferedReader]" 24 | } 25 | 26 | func (br *BufferedReader) Equals(other interface{}) bool { 27 | return br == other 28 | } 29 | 30 | func (br *BufferedReader) GetInfo() *ObjectInfo { 31 | return nil 32 | } 33 | 34 | func (br *BufferedReader) GetType() *Type { 35 | return TYPE.BufferedReader 36 | } 37 | 38 | func (br *BufferedReader) Hash() uint32 { 39 | return br.hash 40 | } 41 | 42 | func (br *BufferedReader) WithInfo(info *ObjectInfo) Object { 43 | return br 44 | } 45 | -------------------------------------------------------------------------------- /core/channel.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "unsafe" 5 | ) 6 | 7 | type ( 8 | FutureResult struct { 9 | value Object 10 | err Error 11 | } 12 | Channel struct { 13 | ch chan FutureResult 14 | isClosed bool 15 | hash uint32 16 | } 17 | ) 18 | 19 | func MakeFutureResult(value Object, err Error) FutureResult { 20 | return FutureResult{value: value, err: err} 21 | } 22 | 23 | func (ch *Channel) ToString(escape bool) string { 24 | return "#object[Channel]" 25 | } 26 | 27 | func (ch *Channel) Equals(other interface{}) bool { 28 | return ch == other 29 | } 30 | 31 | func (ch *Channel) GetInfo() *ObjectInfo { 32 | return nil 33 | } 34 | 35 | func (ch *Channel) GetType() *Type { 36 | return TYPE.Channel 37 | } 38 | 39 | func (ch *Channel) Hash() uint32 { 40 | return ch.hash 41 | } 42 | 43 | func (ch *Channel) WithInfo(info *ObjectInfo) Object { 44 | return ch 45 | } 46 | 47 | func MakeChannel(ch chan FutureResult) *Channel { 48 | res := &Channel{ch: ch, hash: 0} 49 | res.hash = HashPtr(uintptr(unsafe.Pointer(res))) 50 | return res 51 | } 52 | 53 | func ExtractChannel(args []Object, index int) *Channel { 54 | return EnsureArgIsChannel(args, index) 55 | } 56 | 57 | func (ch *Channel) Close() { 58 | if !ch.isClosed { 59 | close(ch.ch) 60 | ch.isClosed = true 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /core/data.go: -------------------------------------------------------------------------------- 1 | //go:build !gen_code 2 | // +build !gen_code 3 | 4 | package core 5 | 6 | var haveSetCoreNamespaces bool 7 | 8 | func ProcessCoreData() { 9 | // Let MaybeLazy() handle initialization. 10 | if !haveSetCoreNamespaces { 11 | setCoreNamespaces() 12 | haveSetCoreNamespaces = true 13 | } 14 | } 15 | 16 | func ProcessReplData() { 17 | // Let MaybeLazy() handle initialization. 18 | } 19 | 20 | func ProcessLinterData(dialect Dialect) { 21 | if dialect == EDN { 22 | markJokerNamespacesAsUsed() 23 | return 24 | } 25 | processData(linter_allData) 26 | if dialect == JOKER { 27 | markJokerNamespacesAsUsed() 28 | processData(linter_jokerData) 29 | return 30 | } 31 | processData(linter_cljxData) 32 | switch dialect { 33 | case CLJ: 34 | processData(linter_cljData) 35 | case CLJS: 36 | processData(linter_cljsData) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /core/data/linter_all.joke: -------------------------------------------------------------------------------- 1 | ;; Additional restriction on arities where it makes sense 2 | 3 | (def __=__ =) 4 | (defn = 5 | ^Boolean [x y & more] 6 | (apply __=__ x y more)) 7 | 8 | (def __not=__ not=) 9 | (defn not= 10 | ^Boolean [x y & more] 11 | (apply __not=__ x y more)) 12 | 13 | (def __<__ <) 14 | (defn < 15 | ^Boolean [^Number x ^Number y & more] 16 | (apply __<__ x y more)) 17 | 18 | (def __>__ >) 19 | (defn > 20 | ^Boolean [^Number x ^Number y & more] 21 | (apply __>__ x y more)) 22 | 23 | (def __<=__ <=) 24 | (defn <= 25 | ^Boolean [^Number x ^Number y & more] 26 | (apply __<=__ x y more)) 27 | 28 | (def __>=__ >=) 29 | (defn >= 30 | ^Boolean [^Number x ^Number y & more] 31 | (apply __>=__ x y more)) 32 | 33 | (def __==__ ==) 34 | (defn == 35 | ^Boolean [^Number x ^Number y & more] 36 | (apply __==__ x y more)) 37 | 38 | (def __merge__ merge) 39 | (defn merge 40 | ^Map [^Map m1 & more] 41 | (apply __merge__ m1 more)) 42 | 43 | (def __merge-with__ merge-with) 44 | (defn merge-with 45 | ^Map [^Callable f ^Map m1 & more] 46 | (apply __merge-with__ f m1 more)) 47 | 48 | ;; Redefine when and when-not with additional checks 49 | 50 | (defmacro when 51 | "Evaluates test. If logical true, evaluates body in an implicit do." 52 | {:added "1.0"} 53 | [test & body] 54 | (let [c (count body) 55 | b (if (> c 1) 56 | (cons 'do body) 57 | (first body))] 58 | (when *linter-mode* 59 | (when (zero? c) 60 | (println-linter__ (ex-info "when form with empty body" {:form &form :_prefix "Parse warning"})))) 61 | (list 'if test b nil))) 62 | 63 | (defmacro when-not 64 | "Evaluates test. If logical false, evaluates body in an implicit do." 65 | {:added "1.0"} 66 | [test & body] 67 | (let [c (count body) 68 | b (if (> c 1) 69 | (cons 'do body) 70 | (first body))] 71 | (when *linter-mode* 72 | (when (zero? c) 73 | (println-linter__ (ex-info "when-not form with empty body" {:form &form :_prefix "Parse warning"})))) 74 | (list 'if test nil b))) 75 | -------------------------------------------------------------------------------- /core/data/linter_joker.joke: -------------------------------------------------------------------------------- 1 | (def *known-macros* 2 | (merge {'joker.test/deftest nil 'joker.test/is nil 'joker.test/are nil} 3 | (:known-macros joker.core/*linter-config*))) 4 | -------------------------------------------------------------------------------- /core/data/pprint.joke: -------------------------------------------------------------------------------- 1 | (ns joker.pprint 2 | "Pretty printing utilities. Based on Clojure implementation." 3 | {:added "1.0"}) 4 | 5 | (defn print-table 6 | "Prints a collection of maps in a textual table. Prints table headings 7 | ks, and then a line of output for each row, corresponding to the keys 8 | in ks. If ks are not specified, use the keys of the first item in rows." 9 | {:added "1.0"} 10 | ([ks rows] 11 | (when (seq rows) 12 | (let [widths (map 13 | (fn [k] 14 | (apply max (count (str k)) (map #(count (str (get % k))) rows))) 15 | ks) 16 | spacers (map #(apply str (repeat % "-")) widths) 17 | fmts (map #(str "%" % "s") widths) 18 | fmt-row (fn [leader divider trailer row] 19 | (str leader 20 | (apply str (interpose divider 21 | (for [[col fmt] (map vector (map #(get row %) ks) fmts)] 22 | (format fmt (str col))))) 23 | trailer))] 24 | (println) 25 | (println (fmt-row "| " " | " " |" (zipmap ks ks))) 26 | (println (fmt-row "|-" "-+-" "-|" (zipmap ks spacers))) 27 | (doseq [row rows] 28 | (println (fmt-row "| " " | " " |" row)))))) 29 | ([rows] (print-table (keys (first rows)) rows))) 30 | -------------------------------------------------------------------------------- /core/data/template.joke: -------------------------------------------------------------------------------- 1 | ; Copyright (c) Rich Hickey. All rights reserved. 2 | ; The use and distribution terms for this software are covered by the 3 | ; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ; which can be found in the file epl-v10.html at the root of this distribution. 5 | ; By using this software in any fashion, you are agreeing to be bound by 6 | ; the terms of this license. 7 | ; You must not remove this notice, or any other, from this software. 8 | 9 | ;;; template.clj - anonymous functions that pre-evaluate sub-expressions 10 | 11 | ;; By Stuart Sierra 12 | ;; June 23, 2009 13 | 14 | ;; CHANGE LOG 15 | ;; 16 | ;; June 23, 2009: complete rewrite, eliminated _1,_2,... argument 17 | ;; syntax 18 | ;; 19 | ;; January 20, 2009: added "template?" and checks for valid template 20 | ;; expressions. 21 | ;; 22 | ;; December 15, 2008: first version 23 | 24 | 25 | (ns ^{:doc "Macros that expand to repeated copies of a template expression." 26 | :author "Stuart Sierra" 27 | :added "1.0"} 28 | joker.template 29 | (:require [joker.walk :as walk])) 30 | 31 | (defn apply-template 32 | "For use in macros. argv is an argument list, as in defn. expr is 33 | a quoted expression using the symbols in argv. values is a sequence 34 | of values to be used for the arguments. 35 | 36 | apply-template will recursively replace argument symbols in expr 37 | with their corresponding values, returning a modified expr. 38 | 39 | Example: (apply-template '[x] '(+ x x) '[2]) 40 | ;=> (+ 2 2)" 41 | {:added "1.0"} 42 | [argv expr values] 43 | (assert (vector? argv)) 44 | (assert (every? symbol? argv)) 45 | (walk/postwalk-replace (zipmap argv values) expr)) 46 | 47 | (defmacro do-template 48 | "Repeatedly copies expr (in a do block) for each group of arguments 49 | in values. values are automatically partitioned by the number of 50 | arguments in argv, an argument vector as in defn. 51 | 52 | Example: (macroexpand '(do-template [x y] (+ y x) 2 4 3 5)) 53 | ;=> (do (+ 4 2) (+ 5 3))" 54 | {:added "1.0"} 55 | [argv expr & values] 56 | (let [c (count argv)] 57 | `(do ~@(map (fn [a] (apply-template argv expr a)) 58 | (partition c values))))) 59 | -------------------------------------------------------------------------------- /core/deps.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "net/http" 7 | "os" 8 | "path/filepath" 9 | "regexp" 10 | "strings" 11 | ) 12 | 13 | func externalHttpSourceToPath(lib string, url string) (path string) { 14 | home := HomeDir() 15 | localBase := filepath.Join(home, ".jokerd", "deps", strings.SplitN(url, "//", 2)[1]) 16 | libBase := filepath.Join(strings.Split(lib, ".")...) + ".joke" 17 | libPath := filepath.Join(localBase, libBase) 18 | libPathDir := filepath.Dir(libPath) 19 | 20 | if _, err := os.Stat(libPathDir); os.IsNotExist(err) { 21 | os.MkdirAll(libPathDir, 0777) 22 | } 23 | 24 | if _, err := os.Stat(libPath); os.IsNotExist(err) { 25 | if !strings.HasSuffix(url, ".joke") { 26 | url = url + libBase 27 | } 28 | resp, err := http.Get(url) 29 | PanicOnErr(err) 30 | defer resp.Body.Close() 31 | 32 | if resp.StatusCode != http.StatusOK { 33 | panic(RT.NewError(fmt.Sprintf("Unable to retrieve: %s\nServer response: %d", url, resp.StatusCode))) 34 | } 35 | 36 | out, err := os.Create(libPath) 37 | defer out.Close() 38 | PanicOnErr(err) 39 | 40 | _, err = io.Copy(out, resp.Body) 41 | PanicOnErr(err) 42 | } 43 | 44 | return libPath 45 | } 46 | 47 | func externalSourceToPath(lib string, url string) (path string) { 48 | httpPath, _ := regexp.MatchString("http://|https://", url) 49 | if httpPath { 50 | return externalHttpSourceToPath(lib, url) 51 | } else { 52 | return filepath.Join(append([]string{url}, strings.Split(lib, ".")...)...) + ".joke" 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /core/environment_fast_init.go: -------------------------------------------------------------------------------- 1 | //go:build !gen_code 2 | // +build !gen_code 3 | 4 | package core 5 | 6 | func (env *Env) ReferCoreToUser() { 7 | // Nothing need be done; it's already "baked in" in the fast-startup version. 8 | } 9 | -------------------------------------------------------------------------------- /core/environment_slow_init.go: -------------------------------------------------------------------------------- 1 | //go:build gen_code 2 | // +build gen_code 3 | 4 | package core 5 | 6 | /* 7 | Called by parse_init.go in an outer var block, this runs before any 8 | 9 | func init() as well as before func main(). InitEnv() and others are 10 | called at runtime to set some of these Values based on the current 11 | invocation. 12 | */ 13 | func NewEnv() *Env { 14 | features := EmptySet() 15 | features.Add(MakeKeyword("default")) 16 | features.Add(MakeKeyword("joker")) 17 | res := &Env{ 18 | Namespaces: make(map[*string]*Namespace), 19 | Features: features, 20 | } 21 | res.CoreNamespace = res.EnsureSymbolIsNamespace(SYMBOLS.joker_core) 22 | res.CoreNamespace.meta = MakeMeta(nil, "Core library of Joker.", "1.0") 23 | res.NS_VAR = res.CoreNamespace.Intern(MakeSymbol("ns")) 24 | res.IN_NS_VAR = res.CoreNamespace.Intern(MakeSymbol("in-ns")) 25 | res.ns = res.CoreNamespace.Intern(MakeSymbol("*ns*")) 26 | res.stdin = res.CoreNamespace.Intern(MakeSymbol("*in*")) 27 | res.stdout = res.CoreNamespace.Intern(MakeSymbol("*out*")) 28 | res.stderr = res.CoreNamespace.Intern(MakeSymbol("*err*")) 29 | res.file = res.CoreNamespace.Intern(MakeSymbol("*file*")) 30 | res.MainFile = res.CoreNamespace.Intern(MakeSymbol("*main-file*")) 31 | res.version = res.CoreNamespace.InternVar("*joker-version*", versionMap(), 32 | MakeMeta(nil, `The version info for Clojure core, as a map containing :major :minor 33 | :incremental and :qualifier keys. Feature releases may increment 34 | :minor and/or :major, bugfix releases will increment :incremental.`, "1.0")) 35 | res.args = res.CoreNamespace.Intern(MakeSymbol("*command-line-args*")) 36 | res.classPath = res.CoreNamespace.Intern(MakeSymbol("*classpath*")) 37 | res.classPath.Value = NIL 38 | res.classPath.isPrivate = true 39 | res.printReadably = res.CoreNamespace.Intern(MakeSymbol("*print-readably*")) 40 | res.printReadably.Value = Boolean{B: true} 41 | res.CoreNamespace.InternVar("*repl*", Boolean{B: false}, 42 | MakeMeta(nil, "true if Joker is running in repl mode", "1.5")) 43 | res.CoreNamespace.InternVar("*linter-mode*", Boolean{B: LINTER_MODE}, 44 | MakeMeta(nil, "true if Joker is running in linter mode", "1.0")) 45 | res.CoreNamespace.InternVar("*linter-config*", EmptyArrayMap(), 46 | MakeMeta(nil, "Map of configuration key/value pairs for linter mode", "1.0")) 47 | res.libs = res.CoreNamespace.Intern(MakeSymbol("*loaded-libs*")) 48 | res.libs.Value = EmptySet() 49 | res.libs.isPrivate = true 50 | return res 51 | } 52 | 53 | func (env *Env) ReferCoreToUser() { 54 | env.FindNamespace(MakeSymbol("user")).ReferAll(env.CoreNamespace) 55 | } 56 | -------------------------------------------------------------------------------- /core/file.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "os" 5 | "unsafe" 6 | ) 7 | 8 | type ( 9 | File struct { 10 | *os.File 11 | } 12 | ) 13 | 14 | func (f *File) ToString(escape bool) string { 15 | return "#object[File]" 16 | } 17 | 18 | func (f *File) Equals(other interface{}) bool { 19 | return f == other 20 | } 21 | 22 | func (f *File) GetInfo() *ObjectInfo { 23 | return nil 24 | } 25 | 26 | func (f *File) GetType() *Type { 27 | return TYPE.File 28 | } 29 | 30 | func (f *File) Hash() uint32 { 31 | return HashPtr(uintptr(unsafe.Pointer(f))) 32 | } 33 | 34 | func (f *File) WithInfo(info *ObjectInfo) Object { 35 | return f 36 | } 37 | 38 | // To satisfy Named interface 39 | func (f *File) Namespace() string { 40 | return "" 41 | } 42 | 43 | func MakeFile(f *os.File) *File { 44 | return &File{f} 45 | } 46 | 47 | func ExtractFile(args []Object, index int) *File { 48 | return EnsureArgIsFile(args, index) 49 | } 50 | -------------------------------------------------------------------------------- /core/gen_go/unsafe_stuff.go: -------------------------------------------------------------------------------- 1 | package gen_go 2 | 3 | // This module currently is needed only to give gen_code access to 4 | // private fields in Joker's core-package types. 5 | // 6 | // Since Joker's core package isn't really designed to be imported by 7 | // any component outside of Joker itself, it's possible that making 8 | // all (pertinent) fields public could be a straightforward way to 9 | // eliminate the need for this module. 10 | 11 | // NOTE: This code comes from github.com/jcburley/go-spew (originally 12 | // davecgh/go-spew): 13 | 14 | import ( 15 | "reflect" 16 | "unsafe" 17 | ) 18 | 19 | const flagPrivate = 0x20 20 | 21 | var flagValOffset = func() uintptr { 22 | field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag") 23 | if !ok { 24 | panic("reflect.Value has no flag field") 25 | } 26 | return field.Offset 27 | }() 28 | 29 | type flag uintptr 30 | 31 | func flagField(v *reflect.Value) *flag { 32 | return (*flag)(unsafe.Pointer(uintptr(unsafe.Pointer(v)) + flagValOffset)) 33 | } 34 | 35 | func UnsafeReflectValue(v reflect.Value) reflect.Value { 36 | if !v.IsValid() || (v.CanInterface() && v.CanAddr()) { 37 | return v 38 | } 39 | flagFieldPtr := flagField(&v) 40 | *flagFieldPtr &^= flagPrivate 41 | return v 42 | } 43 | -------------------------------------------------------------------------------- /core/intern.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | type ( 4 | StringPool map[string]*string 5 | ) 6 | 7 | func (p StringPool) Intern(s string) *string { 8 | ss, exists := p[s] 9 | if exists { 10 | return ss 11 | } 12 | p[s] = &s 13 | return &s 14 | } 15 | -------------------------------------------------------------------------------- /core/intern_slow_init.go: -------------------------------------------------------------------------------- 1 | //go:build gen_code 2 | // +build gen_code 3 | 4 | package core 5 | 6 | var STRINGS StringPool = StringPool{} 7 | -------------------------------------------------------------------------------- /core/io_reader.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "io" 5 | "unsafe" 6 | ) 7 | 8 | type ( 9 | IOReader struct { 10 | io.Reader 11 | hash uint32 12 | } 13 | ) 14 | 15 | func MakeIOReader(r io.Reader) *IOReader { 16 | res := &IOReader{r, 0} 17 | res.hash = HashPtr(uintptr(unsafe.Pointer(res))) 18 | return res 19 | } 20 | 21 | func (ior *IOReader) ToString(escape bool) string { 22 | return "#object[IOReader]" 23 | } 24 | 25 | func (ior *IOReader) Equals(other interface{}) bool { 26 | return ior == other 27 | } 28 | 29 | func (ior *IOReader) GetInfo() *ObjectInfo { 30 | return nil 31 | } 32 | 33 | func (ior *IOReader) GetType() *Type { 34 | return TYPE.IOReader 35 | } 36 | 37 | func (ior *IOReader) Hash() uint32 { 38 | return ior.hash 39 | } 40 | 41 | func (ior *IOReader) WithInfo(info *ObjectInfo) Object { 42 | return ior 43 | } 44 | 45 | func (ior *IOReader) Close() error { 46 | if c, ok := ior.Reader.(io.Closer); ok { 47 | return c.Close() 48 | } else { 49 | return RT.NewError("Object is not closable: " + ior.ToString(false)) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /core/io_writer.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "io" 5 | "unsafe" 6 | ) 7 | 8 | type ( 9 | IOWriter struct { 10 | io.Writer 11 | hash uint32 12 | } 13 | ) 14 | 15 | func MakeIOWriter(w io.Writer) *IOWriter { 16 | res := &IOWriter{w, 0} 17 | res.hash = HashPtr(uintptr(unsafe.Pointer(res))) 18 | return res 19 | } 20 | 21 | func (iow *IOWriter) ToString(escape bool) string { 22 | return "#object[IOWriter]" 23 | } 24 | 25 | func (iow *IOWriter) Equals(other interface{}) bool { 26 | return iow == other 27 | } 28 | 29 | func (iow *IOWriter) GetInfo() *ObjectInfo { 30 | return nil 31 | } 32 | 33 | func (iow *IOWriter) GetType() *Type { 34 | return TYPE.IOWriter 35 | } 36 | 37 | func (iow *IOWriter) Hash() uint32 { 38 | return iow.hash 39 | } 40 | 41 | func (iow *IOWriter) WithInfo(info *ObjectInfo) Object { 42 | return iow 43 | } 44 | 45 | func (iow *IOWriter) Close() error { 46 | if c, ok := iow.Writer.(io.Closer); ok { 47 | return c.Close() 48 | } else { 49 | return RT.NewError("Object is not closable: " + iow.ToString(false)) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /core/line_runereader.go: -------------------------------------------------------------------------------- 1 | //go:build !plan9 2 | // +build !plan9 3 | 4 | package core 5 | 6 | import ( 7 | "io" 8 | "strings" 9 | "unicode/utf8" 10 | 11 | "github.com/candid82/liner" 12 | ) 13 | 14 | type ( 15 | LineRuneReader struct { 16 | rl *liner.State 17 | buffer []rune 18 | i int 19 | Prompt string 20 | } 21 | ) 22 | 23 | func NewLineRuneReader(rl *liner.State) *LineRuneReader { 24 | return &LineRuneReader{rl: rl} 25 | } 26 | 27 | func (lrr *LineRuneReader) ReadRune() (rune, int, error) { 28 | if lrr.buffer != nil && lrr.i < len(lrr.buffer) { 29 | r := lrr.buffer[lrr.i] 30 | lrr.i++ 31 | return r, utf8.RuneLen(r), nil 32 | } 33 | line, err := lrr.rl.Prompt(lrr.Prompt) 34 | if err != nil { 35 | return EOF, 0, io.EOF 36 | } 37 | if strings.TrimSpace(line) != "" { 38 | lrr.rl.AppendHistory(line) 39 | } 40 | lrr.buffer = make([]rune, 0, len(line)+1) 41 | for _, r := range line { 42 | lrr.buffer = append(lrr.buffer, r) 43 | } 44 | lrr.buffer = append(lrr.buffer, '\n') 45 | lrr.i = 1 46 | return lrr.buffer[0], utf8.RuneLen(lrr.buffer[0]), nil 47 | } 48 | -------------------------------------------------------------------------------- /core/reader.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "io" 5 | ) 6 | 7 | type ( 8 | Reader struct { 9 | runeReader io.RuneReader 10 | rw *RuneWindow 11 | line int 12 | prevLineLength int 13 | column int 14 | isEof bool 15 | rewind int 16 | filename *string 17 | } 18 | ) 19 | 20 | func NewReader(runeReader io.RuneReader, filename string) *Reader { 21 | return &Reader{ 22 | line: 1, 23 | runeReader: runeReader, 24 | rw: &RuneWindow{}, 25 | rewind: -1, 26 | filename: STRINGS.Intern(filename), 27 | } 28 | } 29 | 30 | func (reader *Reader) Get() rune { 31 | if reader.isEof { 32 | return EOF 33 | } 34 | if reader.rewind > -1 { 35 | r := top(reader.rw, reader.rewind) 36 | reader.rewind-- 37 | if r == '\n' { 38 | reader.line++ 39 | reader.prevLineLength = reader.column 40 | reader.column = 0 41 | } else { 42 | reader.column++ 43 | } 44 | return r 45 | } 46 | r, _, err := reader.runeReader.ReadRune() 47 | switch { 48 | case err == io.EOF: 49 | reader.isEof = true 50 | return EOF 51 | case err != nil: 52 | panic(err) 53 | case r == '\n': 54 | reader.line++ 55 | reader.prevLineLength = reader.column 56 | reader.column = 0 57 | add(reader.rw, r) 58 | return r 59 | default: 60 | reader.column++ 61 | add(reader.rw, r) 62 | return r 63 | } 64 | } 65 | 66 | func (reader *Reader) Unget() { 67 | if reader.isEof { 68 | return 69 | } 70 | reader.rewind++ 71 | if reader.column == 0 { 72 | reader.line-- 73 | reader.column = reader.prevLineLength 74 | } else { 75 | reader.column-- 76 | } 77 | } 78 | 79 | func (reader *Reader) Peek() rune { 80 | if reader.isEof { 81 | return EOF 82 | } 83 | r := reader.Get() 84 | reader.Unget() 85 | return r 86 | } 87 | -------------------------------------------------------------------------------- /core/rune_window.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | type ( 4 | RuneWindow struct { 5 | arr [5]rune 6 | start int // points to an element before the first one 7 | end int // point to the last element 8 | } 9 | ) 10 | 11 | func add(rw *RuneWindow, r rune) { 12 | rw.end++ 13 | if rw.end == len(rw.arr) { 14 | rw.end = 0 15 | } 16 | rw.arr[rw.end] = r 17 | if rw.end == rw.start { 18 | rw.start++ 19 | if rw.start == len(rw.arr) { 20 | rw.start = 0 21 | } 22 | } 23 | } 24 | 25 | func size(rw *RuneWindow) int { 26 | if rw.end >= rw.start { 27 | return rw.end - rw.start 28 | } 29 | return len(rw.arr) - (rw.start - rw.end) 30 | } 31 | 32 | func top(rw *RuneWindow, i int) rune { 33 | if i >= size(rw) { 34 | panic("RuneWindow: index out of range") 35 | } 36 | index := rw.end - i 37 | if index < 0 { 38 | index += len(rw.arr) 39 | } 40 | return rw.arr[index] 41 | } 42 | -------------------------------------------------------------------------------- /core/spew_disabled.go: -------------------------------------------------------------------------------- 1 | //go:build !go_spew 2 | // +build !go_spew 3 | 4 | package core 5 | 6 | var procGoSpew = func(args []Object) (res Object) { 7 | return MakeBoolean(false) 8 | } 9 | -------------------------------------------------------------------------------- /core/spew_enabled.go: -------------------------------------------------------------------------------- 1 | //go:build go_spew 2 | // +build go_spew 3 | 4 | package core 5 | 6 | import ( 7 | "fmt" 8 | "github.com/jcburley/go-spew/spew" 9 | ) 10 | 11 | var procGoSpew = func(args []Object) (res Object) { 12 | res = MakeBoolean(false) 13 | CheckArity(args, 1, 2) 14 | defer func() { 15 | if r := recover(); r != nil { 16 | fmt.Fprintf(Stderr, "Error: %v\n", r) 17 | } 18 | }() 19 | scs := spew.NewDefaultConfig() 20 | if len(args) > 1 { 21 | m := ExtractMap(args, 1) 22 | if yes, k := m.Get(MakeKeyword("Indent")); yes { 23 | scs.Indent = k.(Native).Native().(string) 24 | } 25 | if yes, k := m.Get(MakeKeyword("MaxDepth")); yes { 26 | scs.MaxDepth = k.(Native).Native().(int) 27 | } 28 | if yes, k := m.Get(MakeKeyword("DisableMethods")); yes { 29 | scs.DisableMethods = k.(Native).Native().(bool) 30 | } 31 | if yes, k := m.Get(MakeKeyword("DisablePointerMethods")); yes { 32 | scs.DisablePointerMethods = k.(Native).Native().(bool) 33 | } 34 | if yes, k := m.Get(MakeKeyword("DisablePointerAddresses")); yes { 35 | scs.DisablePointerAddresses = k.(Native).Native().(bool) 36 | } 37 | if yes, k := m.Get(MakeKeyword("DisableCapacities")); yes { 38 | scs.DisableCapacities = k.(Native).Native().(bool) 39 | } 40 | if yes, k := m.Get(MakeKeyword("ContinueOnMethod")); yes { 41 | scs.ContinueOnMethod = k.(Native).Native().(bool) 42 | } 43 | if yes, k := m.Get(MakeKeyword("SortKeys")); yes { 44 | scs.SortKeys = k.(Native).Native().(bool) 45 | } 46 | if yes, k := m.Get(MakeKeyword("SpewKeys")); yes { 47 | scs.SpewKeys = k.(Native).Native().(bool) 48 | } 49 | if yes, k := m.Get(MakeKeyword("NoDuplicates")); yes { 50 | scs.NoDuplicates = k.(Native).Native().(bool) 51 | } 52 | if yes, k := m.Get(MakeKeyword("UseOrdinals")); yes { 53 | scs.UseOrdinals = k.(Native).Native().(bool) 54 | } 55 | } 56 | scs.Fdump(Stderr, args[0]) 57 | return MakeBoolean(true) 58 | } 59 | -------------------------------------------------------------------------------- /core/stringable.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | func EnsureObjectIsStringable(obj Object, pattern string) String { 4 | switch c := obj.(type) { 5 | case String: 6 | return c 7 | case Char: 8 | return String{S: string(c.Ch)} 9 | default: 10 | panic(FailObject(c, "Stringable", pattern)) 11 | } 12 | } 13 | 14 | func EnsureArgIsStringable(args []Object, index int) String { 15 | switch c := args[index].(type) { 16 | case String: 17 | return c 18 | case Char: 19 | return String{S: string(c.Ch)} 20 | default: 21 | panic(FailArg(c, "Stringable", index)) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /docs/dash.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | rm -f ./joker.xml 4 | ../joker generate-xml.joke || exit 5 | 6 | pushd dash 7 | rm -f ./*.html 8 | rm -f ./*.css 9 | rm -f ./*.js 10 | rm -rf joker.docset 11 | cp ../*.html ./ 12 | cp ../*.css ./ 13 | cp ../*.js ./ 14 | dashing build joker 15 | tar --exclude='.DS_Store' -cvzf ../joker.tgz joker.docset 16 | popd 17 | -------------------------------------------------------------------------------- /docs/dash/dashing.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Joker", 3 | "package": "joker", 4 | "index": "index.html", 5 | "selectors": { 6 | "h3.ns": "Namespace", 7 | "h3.type": "Type", 8 | "h3.Function": "Function", 9 | "h3.Macro": "Macro", 10 | "h3.Variable": "Variable", 11 | "h3.Constant": "Constant" 12 | }, 13 | "icon32x32": "../joker32.png", 14 | "allowJS": true, 15 | "ExternalURL": "https://candid82.github.io/joker", 16 | "ignore": ["{name}"] 17 | } 18 | -------------------------------------------------------------------------------- /docs/generate-xml.joke: -------------------------------------------------------------------------------- 1 | (require '[joker.string :as s]) 2 | 3 | (def xml-template 4 | (slurp "templates/xml.html")) 5 | 6 | (spit "joker.xml" (s/replace xml-template "{version}" (joker-version))) 7 | -------------------------------------------------------------------------------- /docs/joker.base64.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
7 |

Namespace: joker.base64

8 | v1.0 9 |

Contents

10 | 27 |

Summary

28 |

Implements base64 encoding as specified by RFC 4648.

29 |

Index

30 | 39 |

Constants

40 | Constants are variables with :const true in their metadata. Joker currently does not recognize them as special; as such, it allows redefining them or their values. 41 | 44 |

Variables

45 | 48 |

Functions, Macros, and Special Forms

49 | 72 |
73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /docs/joker.hex.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
7 |

Namespace: joker.hex

8 | v1.0 9 |

Contents

10 | 27 |

Summary

28 |

Implements hexadecimal encoding and decoding.

29 |

Index

30 | 39 |

Constants

40 | Constants are variables with :const true in their metadata. Joker currently does not recognize them as special; as such, it allows redefining them or their values. 41 | 44 |

Variables

45 | 48 |

Functions, Macros, and Special Forms

49 | 72 |
73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /docs/joker.html.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
7 |

Namespace: joker.html

8 | v1.0 9 |

Contents

10 | 27 |

Summary

28 |

Provides functions for escaping and unescaping HTML text.

29 |

Index

30 | 39 |

Constants

40 | Constants are variables with :const true in their metadata. Joker currently does not recognize them as special; as such, it allows redefining them or their values. 41 | 44 |

Variables

45 | 48 |

Functions, Macros, and Special Forms

49 | 72 |
73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /docs/joker.markdown.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
7 |

Namespace: joker.markdown

8 | v1.0 9 |

Contents

10 | 27 |

Summary

28 |

Implements GitHub Flavored Markdown rendering.

29 |

Index

30 | 36 |

Constants

37 | Constants are variables with :const true in their metadata. Joker currently does not recognize them as special; as such, it allows redefining them or their values. 38 | 41 |

Variables

42 | 45 |

Functions, Macros, and Special Forms

46 | 66 |
67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /docs/joker.pprint.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
7 |

Namespace: joker.pprint

8 | v1.0 9 |

Contents

10 | 27 |

Summary

28 |

Pretty printing utilities. Based on Clojure implementation.

29 |

Index

30 | 36 |

Constants

37 | Constants are variables with :const true in their metadata. Joker currently does not recognize them as special; as such, it allows redefining them or their values. 38 | 41 |

Variables

42 | 45 |

Functions, Macros, and Special Forms

46 | 62 |
63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /docs/joker.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/candid82/joker/1eed1c02c38ef1373640c42af1423808826820cc/docs/joker.tgz -------------------------------------------------------------------------------- /docs/joker.uuid.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
7 |

Namespace: joker.uuid

8 | v1.0 9 |

Contents

10 | 27 |

Summary

28 |

Generates UUIDs.

29 |

Index

30 | 36 |

Constants

37 | Constants are variables with :const true in their metadata. Joker currently does not recognize them as special; as such, it allows redefining them or their values. 38 | 41 |

Variables

42 | 45 |

Functions, Macros, and Special Forms

46 | 59 |
60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /docs/joker.xml: -------------------------------------------------------------------------------- 1 | 2 | 0.17.0 3 | https://github.com/candid82/joker/raw/master/docs/joker.tgz 4 | 5 | -------------------------------------------------------------------------------- /docs/joker.yaml.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
7 |

Namespace: joker.yaml

8 | v1.0 9 |

Contents

10 | 27 |

Summary

28 |

Implements encoding and decoding of YAML.

29 |

Index

30 | 39 |

Constants

40 | Constants are variables with :const true in their metadata. Joker currently does not recognize them as special; as such, it allows redefining them or their values. 41 | 44 |

Variables

45 | 48 |

Functions, Macros, and Special Forms

49 | 72 |
73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /docs/joker32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/candid82/joker/1eed1c02c38ef1373640c42af1423808826820cc/docs/joker32.png -------------------------------------------------------------------------------- /docs/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
7 | Joker Standard Namespaces and Types 8 | 12 |

Indexes of Joker Standard Namespaces and Types

13 |

Index of Special Forms

14 | 17 |

Index of Namespaces

18 | 21 |

Index of Types

22 | 25 |

Joker Special Forms

26 | 29 |

Joker Standard Namespaces

30 | 33 |

Joker Standard Types

34 |

Note: These types are "omnipresent", in that they're not members of any particular namespace, but are available for resolution regardless of the current value of *ns* (the current namespace).

35 | 38 |
39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /docs/templates/link-item.html: -------------------------------------------------------------------------------- 1 |
  • 2 | {escaped-name} 3 |
  • 4 | -------------------------------------------------------------------------------- /docs/templates/ns-summary.html: -------------------------------------------------------------------------------- 1 |
  • 2 |

    {name}

    3 | v{added} 4 |

    {docstring}

    5 | details 6 |
  • 7 | -------------------------------------------------------------------------------- /docs/templates/ns.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
    7 |

    Namespace: {name}

    8 | v{added} 9 |

    Contents

    10 | 27 |

    Summary

    28 |

    {docstring}

    29 |

    Index

    30 | 33 |

    Constants

    34 | Constants are variables with :const true in their metadata. Joker currently does not recognize them as special; as such, it allows redefining them or their values. 35 | 38 |

    Variables

    39 | 42 |

    Functions, Macros, and Special Forms

    43 | 46 |
    47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /docs/templates/special-form.html: -------------------------------------------------------------------------------- 1 |
  • 2 |

    {name}

    3 |
    {usage}
    4 |

    {docstring}

    5 |
  • 6 | -------------------------------------------------------------------------------- /docs/templates/type-summary.html: -------------------------------------------------------------------------------- 1 |
  • 2 |

    {name}

    3 | v{added} 4 |

    {docstring}

    5 |
  • 6 | -------------------------------------------------------------------------------- /docs/templates/usage-with-types.html: -------------------------------------------------------------------------------- 1 |
    {usage}{usage-with-types}
    2 | -------------------------------------------------------------------------------- /docs/templates/usage.html: -------------------------------------------------------------------------------- 1 |
    {usage}
    2 | -------------------------------------------------------------------------------- /docs/templates/var.html: -------------------------------------------------------------------------------- 1 |
  • 2 |

    {name}

    3 | {type} 4 | v{added} 5 |
    {usage}
    6 |

    {docstring}

    7 | {source} 8 | {show-types} 9 |
  • 10 | -------------------------------------------------------------------------------- /docs/templates/xml.html: -------------------------------------------------------------------------------- 1 | 2 | {version} 3 | https://github.com/candid82/joker/raw/master/docs/joker.tgz 4 | 5 | -------------------------------------------------------------------------------- /eval-tests.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ./joker tests/run-eval-tests.joke "$@" 4 | -------------------------------------------------------------------------------- /exiters.go: -------------------------------------------------------------------------------- 1 | //go:build !windows && !plan9 2 | // +build !windows,!plan9 3 | 4 | package main 5 | 6 | const EXITERS = "EOF (Ctrl-D), or SIGINT (Ctrl-C)" 7 | -------------------------------------------------------------------------------- /exiters_plan9.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | const EXITERS = "EOF (Ctrl-D), or 'Interrupt' note (Ctrl-C)" 4 | -------------------------------------------------------------------------------- /exiters_windows.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | const EXITERS = "EOF (Ctrl-Z), or Ctrl-C" 4 | -------------------------------------------------------------------------------- /flag-tests.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ./joker tests/run-flag-tests.joke 4 | -------------------------------------------------------------------------------- /formatter-tests.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ./joker tests/run-tests.joke --format tests/formatter out output.clj 4 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/candid82/joker 2 | 3 | go 1.23.0 4 | 5 | toolchain go1.24.1 6 | 7 | require ( 8 | github.com/candid82/liner v1.4.0 9 | github.com/go-git/go-git/v5 v5.13.0 10 | github.com/jcburley/go-spew v1.3.0 11 | github.com/pkg/profile v1.2.1 12 | github.com/yuin/goldmark v1.4.13 13 | go.etcd.io/bbolt v1.3.7 14 | gopkg.in/yaml.v2 v2.4.0 15 | ) 16 | 17 | require ( 18 | dario.cat/mergo v1.0.0 // indirect 19 | github.com/Microsoft/go-winio v0.6.1 // indirect 20 | github.com/ProtonMail/go-crypto v1.1.3 // indirect 21 | github.com/cloudflare/circl v1.3.7 // indirect 22 | github.com/cyphar/filepath-securejoin v0.2.5 // indirect 23 | github.com/emirpasic/gods v1.18.1 // indirect 24 | github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect 25 | github.com/go-git/go-billy/v5 v5.6.0 // indirect 26 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect 27 | github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect 28 | github.com/kevinburke/ssh_config v1.2.0 // indirect 29 | github.com/mattn/go-runewidth v0.0.3 // indirect 30 | github.com/pjbgf/sha1cd v0.3.0 // indirect 31 | github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect 32 | github.com/skeema/knownhosts v1.3.0 // indirect 33 | github.com/xanzy/ssh-agent v0.3.3 // indirect 34 | golang.org/x/crypto v0.36.0 // indirect 35 | golang.org/x/mod v0.17.0 // indirect 36 | golang.org/x/net v0.38.0 // indirect 37 | golang.org/x/sync v0.10.0 // indirect 38 | golang.org/x/sys v0.31.0 // indirect 39 | golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect 40 | gopkg.in/warnings.v0 v0.1.2 // indirect 41 | ) 42 | -------------------------------------------------------------------------------- /linter-tests.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ./joker tests/run-tests.joke --lint tests/linter err output.txt 4 | -------------------------------------------------------------------------------- /repl_plan9.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "io" 7 | 8 | . "github.com/candid82/joker/core" 9 | ) 10 | 11 | func repl(phase Phase) { 12 | ProcessReplData() 13 | GLOBAL_ENV.FindNamespace(MakeSymbol("user")).ReferAll(GLOBAL_ENV.FindNamespace(MakeSymbol("joker.repl"))) 14 | fmt.Printf("Welcome to joker %s. Use '(exit)', %s to exit.\n", VERSION, EXITERS) 15 | parseContext := &ParseContext{GlobalEnv: GLOBAL_ENV} 16 | replContext := NewReplContext(parseContext.GlobalEnv) 17 | 18 | var runeReader io.RuneReader 19 | runeReader = bufio.NewReader(Stdin) 20 | reader := NewReader(runeReader, "") 21 | 22 | for { 23 | print(GLOBAL_ENV.CurrentNamespace().Name.ToString(false) + "=> ") 24 | if processReplCommand(reader, phase, parseContext, replContext) { 25 | return 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | build() { 4 | go clean 5 | rm -f core/a_*.go # In case switching from a gen-code branch or similar (any existing files might break the build here) 6 | go generate ./... 7 | (cd core; go fmt a_*.go > /dev/null) 8 | go vet ./... 9 | go build 10 | } 11 | 12 | set -e # Exit on error. 13 | 14 | build 15 | 16 | if [ "$1" == "-v" ]; then 17 | ./joker -e '(print "\nLibraries available in this build:\n ") (loaded-libs) (println)' 18 | fi 19 | 20 | SUM256="$(go run tools/sum256dir/main.go std)" 21 | OUT="$(cd std; ../joker generate-std.joke 2>&1 | grep -v 'WARNING:.*already refers' | grep '.')" || : # grep returns non-zero if no lines match 22 | if [ -n "$OUT" ]; then 23 | echo "$OUT" 24 | echo >&2 "Unable to generate fresh library files; exiting." 25 | exit 2 26 | fi 27 | (cd std; go fmt ./... > /dev/null) 28 | NEW_SUM256="$(go run tools/sum256dir/main.go std)" 29 | 30 | if [ "$SUM256" != "$NEW_SUM256" ]; then 31 | echo 'std has changed, rebuilding...' 32 | build 33 | fi 34 | 35 | if [ "$1" != "--build-only" ]; then 36 | ./joker "$@" 37 | fi 38 | -------------------------------------------------------------------------------- /shadow.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Check for shadowed variables. 4 | 5 | if which shadow >/dev/null 2>/dev/null; then 6 | # Install via: go install golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow 7 | SHADOW="-vettool=$(which shadow)" 8 | elif $(go tool vet nonexistent.go 2>&1 | grep -q -v unsupported); then 9 | SHADOW="-shadow=true" 10 | fi 11 | 12 | if [ -n "$SHADOW" ]; then 13 | go vet "$SHADOW" ./... 14 | else 15 | echo >&2 "Not performing shadowed-variables check; consider installing shadow tool via:" 16 | echo >&2 " go install golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow" 17 | echo >&2 "and rerunning this script." 18 | exit 99 19 | fi 20 | -------------------------------------------------------------------------------- /std/addmeta.tmpl: -------------------------------------------------------------------------------- 1 | .Plus(MakeKeyword("{key}"), {value}) 2 | -------------------------------------------------------------------------------- /std/arity.tmpl: -------------------------------------------------------------------------------- 1 | case {arity}: 2 | {arityCheck} 3 | {args} 4 | {goExpr} 5 | {return} 6 | -------------------------------------------------------------------------------- /std/base64.joke: -------------------------------------------------------------------------------- 1 | (ns ^{:go-imports [] 2 | :doc "Implements base64 encoding as specified by RFC 4648."} 3 | base64) 4 | 5 | (defn ^String decode-string 6 | "Returns the bytes represented by the base64 string s." 7 | {:added "1.0" 8 | :go "decodeString(s)"} 9 | [^String s]) 10 | 11 | (defn ^String encode-string 12 | "Returns the base64 encoding of s." 13 | {:added "1.0" 14 | :go "encodeString(s)"} 15 | [^String s]) 16 | -------------------------------------------------------------------------------- /std/base64/a_base64.go: -------------------------------------------------------------------------------- 1 | // This file is generated by generate-std.joke script. Do not edit manually! 2 | 3 | package base64 4 | 5 | import ( 6 | . "github.com/candid82/joker/core" 7 | ) 8 | 9 | var __decode_string__P ProcFn = __decode_string_ 10 | var decode_string_ Proc = Proc{Fn: __decode_string__P, Name: "decode_string_", Package: "std/base64"} 11 | 12 | func __decode_string_(_args []Object) Object { 13 | _c := len(_args) 14 | switch { 15 | case _c == 1: 16 | s := ExtractString(_args, 0) 17 | _res := decodeString(s) 18 | return MakeString(_res) 19 | 20 | default: 21 | PanicArity(_c) 22 | } 23 | return NIL 24 | } 25 | 26 | var __encode_string__P ProcFn = __encode_string_ 27 | var encode_string_ Proc = Proc{Fn: __encode_string__P, Name: "encode_string_", Package: "std/base64"} 28 | 29 | func __encode_string_(_args []Object) Object { 30 | _c := len(_args) 31 | switch { 32 | case _c == 1: 33 | s := ExtractString(_args, 0) 34 | _res := encodeString(s) 35 | return MakeString(_res) 36 | 37 | default: 38 | PanicArity(_c) 39 | } 40 | return NIL 41 | } 42 | 43 | func Init() { 44 | 45 | InternsOrThunks() 46 | } 47 | 48 | var base64Namespace = GLOBAL_ENV.EnsureSymbolIsLib(MakeSymbol("joker.base64")) 49 | 50 | func init() { 51 | base64Namespace.Lazy = Init 52 | } 53 | -------------------------------------------------------------------------------- /std/base64/a_base64_slow_init.go: -------------------------------------------------------------------------------- 1 | // This file is generated by generate-std.joke script. Do not edit manually! 2 | 3 | package base64 4 | 5 | import ( 6 | "fmt" 7 | . "github.com/candid82/joker/core" 8 | "os" 9 | ) 10 | 11 | func InternsOrThunks() { 12 | if VerbosityLevel > 0 { 13 | fmt.Fprintln(os.Stderr, "Lazily running slow version of base64.InternsOrThunks().") 14 | } 15 | base64Namespace.ResetMeta(MakeMeta(nil, `Implements base64 encoding as specified by RFC 4648.`, "1.0")) 16 | 17 | base64Namespace.InternVar("decode-string", decode_string_, 18 | MakeMeta( 19 | NewListFrom(NewVectorFrom(MakeSymbol("s"))), 20 | `Returns the bytes represented by the base64 string s.`, "1.0").Plus(MakeKeyword("tag"), String{S: "String"})) 21 | 22 | base64Namespace.InternVar("encode-string", encode_string_, 23 | MakeMeta( 24 | NewListFrom(NewVectorFrom(MakeSymbol("s"))), 25 | `Returns the base64 encoding of s.`, "1.0").Plus(MakeKeyword("tag"), String{S: "String"})) 26 | 27 | } 28 | -------------------------------------------------------------------------------- /std/base64/base64_native.go: -------------------------------------------------------------------------------- 1 | package base64 2 | 3 | import ( 4 | "encoding/base64" 5 | 6 | . "github.com/candid82/joker/core" 7 | ) 8 | 9 | func decodeString(s string) string { 10 | decoded, err := base64.StdEncoding.DecodeString(s) 11 | if err != nil { 12 | panic(RT.NewError("Invalid base64 string: " + err.Error())) 13 | } 14 | return string(decoded) 15 | } 16 | 17 | func encodeString(s string) string { 18 | return base64.StdEncoding.EncodeToString([]byte(s)) 19 | } 20 | -------------------------------------------------------------------------------- /std/crypto.joke: -------------------------------------------------------------------------------- 1 | (ns ^{:go-imports ["crypto/sha256" "crypto/sha512" "crypto/md5" "crypto/sha1"] 2 | :doc "Implements common cryptographic and hash functions."} 3 | crypto) 4 | 5 | (defn ^String hmac 6 | "Returns HMAC signature for message and key using specified algorithm. 7 | Algorithm is one of the following: :sha1, :sha224, :sha256, :sha384, :sha512." 8 | {:added "1.0" 9 | :go "hmacSum(algorithm, message, key)"} 10 | [^Keyword algorithm ^String message ^String key]) 11 | 12 | (defn ^String sha256 13 | "Returns the SHA256 checksum of the data." 14 | {:added "1.0" 15 | :go "! t := sha256.Sum256([]byte(data)); _res := string(t[:])"} 16 | [^String data]) 17 | 18 | (defn ^String sha224 19 | "Returns the SHA224 checksum of the data." 20 | {:added "1.0" 21 | :go "! t := sha256.Sum224([]byte(data)); _res := string(t[:])"} 22 | [^String data]) 23 | 24 | (defn ^String sha384 25 | "Returns the SHA384 checksum of the data." 26 | {:added "1.0" 27 | :go "! t := sha512.Sum384([]byte(data)); _res := string(t[:])"} 28 | [^String data]) 29 | 30 | (defn ^String sha512 31 | "Returns the SHA512 checksum of the data." 32 | {:added "1.0" 33 | :go "! t := sha512.Sum512([]byte(data)); _res := string(t[:])"} 34 | [^String data]) 35 | 36 | (defn ^String sha512-224 37 | "Returns the SHA512/224 checksum of the data." 38 | {:added "1.0" 39 | :go "! t := sha512.Sum512_224([]byte(data)); _res := string(t[:])"} 40 | [^String data]) 41 | 42 | (defn ^String sha512-256 43 | "Returns the SHA512/256 checksum of the data." 44 | {:added "1.0" 45 | :go "! t := sha512.Sum512_256([]byte(data)); _res := string(t[:])"} 46 | [^String data]) 47 | 48 | (defn ^String md5 49 | "Returns the MD5 checksum of the data." 50 | {:added "1.0" 51 | :go "! t := md5.Sum([]byte(data)); _res := string(t[:])"} 52 | [^String data]) 53 | 54 | (defn ^String sha1 55 | "Returns the SHA1 checksum of the data." 56 | {:added "1.0" 57 | :go "! t := sha1.Sum([]byte(data)); _res := string(t[:])"} 58 | [^String data]) 59 | -------------------------------------------------------------------------------- /std/crypto/crypto_native.go: -------------------------------------------------------------------------------- 1 | package crypto 2 | 3 | import ( 4 | "crypto/hmac" 5 | "crypto/sha1" 6 | "crypto/sha256" 7 | "crypto/sha512" 8 | "hash" 9 | 10 | . "github.com/candid82/joker/core" 11 | ) 12 | 13 | func hmacSum(algorithm, message, key string) string { 14 | var h func() hash.Hash 15 | switch algorithm { 16 | case ":sha1": 17 | h = sha1.New 18 | case ":sha224": 19 | h = sha256.New224 20 | case ":sha256": 21 | h = sha256.New 22 | case ":sha384": 23 | h = sha512.New384 24 | case ":sha512": 25 | h = sha512.New 26 | default: 27 | panic(RT.NewError("Unsupported algorithm " + algorithm + 28 | ". Supported algorithms are: :sha1, :sha224, :sha256, :sha384, :sha512")) 29 | } 30 | mac := hmac.New(h, []byte(key)) 31 | mac.Write([]byte(message)) 32 | return string(mac.Sum(nil)) 33 | } 34 | -------------------------------------------------------------------------------- /std/csv/a_csv.go: -------------------------------------------------------------------------------- 1 | // This file is generated by generate-std.joke script. Do not edit manually! 2 | 3 | package csv 4 | 5 | import ( 6 | . "github.com/candid82/joker/core" 7 | ) 8 | 9 | var __csv_seq__P ProcFn = __csv_seq_ 10 | var csv_seq_ Proc = Proc{Fn: __csv_seq__P, Name: "csv_seq_", Package: "std/csv"} 11 | 12 | func __csv_seq_(_args []Object) Object { 13 | _c := len(_args) 14 | switch { 15 | case _c == 1: 16 | rdr := ExtractObject(_args, 0) 17 | _res := csvSeqOpts(rdr, EmptyArrayMap()) 18 | return _res 19 | 20 | case _c == 2: 21 | rdr := ExtractObject(_args, 0) 22 | opts := ExtractMap(_args, 1) 23 | _res := csvSeqOpts(rdr, opts) 24 | return _res 25 | 26 | default: 27 | PanicArity(_c) 28 | } 29 | return NIL 30 | } 31 | 32 | var __write__P ProcFn = __write_ 33 | var write_ Proc = Proc{Fn: __write__P, Name: "write_", Package: "std/csv"} 34 | 35 | func __write_(_args []Object) Object { 36 | _c := len(_args) 37 | switch { 38 | case _c == 2: 39 | f := ExtractIOWriter(_args, 0) 40 | data := ExtractSeqable(_args, 1) 41 | _res := write(f, data, EmptyArrayMap()) 42 | return _res 43 | 44 | case _c == 3: 45 | f := ExtractIOWriter(_args, 0) 46 | data := ExtractSeqable(_args, 1) 47 | opts := ExtractMap(_args, 2) 48 | _res := write(f, data, opts) 49 | return _res 50 | 51 | default: 52 | PanicArity(_c) 53 | } 54 | return NIL 55 | } 56 | 57 | var __write_string__P ProcFn = __write_string_ 58 | var write_string_ Proc = Proc{Fn: __write_string__P, Name: "write_string_", Package: "std/csv"} 59 | 60 | func __write_string_(_args []Object) Object { 61 | _c := len(_args) 62 | switch { 63 | case _c == 1: 64 | data := ExtractSeqable(_args, 0) 65 | _res := writeString(data, EmptyArrayMap()) 66 | return MakeString(_res) 67 | 68 | case _c == 2: 69 | data := ExtractSeqable(_args, 0) 70 | opts := ExtractMap(_args, 1) 71 | _res := writeString(data, opts) 72 | return MakeString(_res) 73 | 74 | default: 75 | PanicArity(_c) 76 | } 77 | return NIL 78 | } 79 | 80 | func Init() { 81 | 82 | InternsOrThunks() 83 | } 84 | 85 | var csvNamespace = GLOBAL_ENV.EnsureSymbolIsLib(MakeSymbol("joker.csv")) 86 | 87 | func init() { 88 | csvNamespace.Lazy = Init 89 | } 90 | -------------------------------------------------------------------------------- /std/filepath/filepath_native.go: -------------------------------------------------------------------------------- 1 | package filepath 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | 7 | . "github.com/candid82/joker/core" 8 | ) 9 | 10 | func fileSeq(root string) *ArrayVector { 11 | res := EmptyArrayVector() 12 | filepath.Walk(root, func(path string, info os.FileInfo, err error) error { 13 | PanicOnErr(err) 14 | m := FileInfoMap(path, info) 15 | res.Append(m) 16 | return nil 17 | }) 18 | return res 19 | } 20 | -------------------------------------------------------------------------------- /std/fn.tmpl: -------------------------------------------------------------------------------- 1 | var __{goName}_P ProcFn = __{goName} 2 | var {goName} Proc = Proc{Fn: __{goName}_P, Name: "{goName}", Package: "std/{pkg}"} 3 | 4 | func __{goName}(_args []Object) Object { 5 | _c := len(_args) 6 | switch { 7 | {arities} 8 | default: 9 | PanicArity(_c) 10 | } 11 | return NIL 12 | } 13 | -------------------------------------------------------------------------------- /std/hex.joke: -------------------------------------------------------------------------------- 1 | (ns ^{:go-imports ["encoding/hex"] 2 | :doc "Implements hexadecimal encoding and decoding."} 3 | hex) 4 | 5 | (defn ^String decode-string 6 | "Returns the bytes represented by the hexadecimal string s." 7 | {:added "1.0" 8 | :go "! t, err := hex.DecodeString(s); PanicOnErr(err); _res := string(t)"} 9 | [^String s]) 10 | 11 | (defn ^String encode-string 12 | "Returns the hexadecimal encoding of s." 13 | {:added "1.0" 14 | :go "hex.EncodeToString([]byte(s))"} 15 | [^String s]) 16 | -------------------------------------------------------------------------------- /std/hex/a_hex.go: -------------------------------------------------------------------------------- 1 | // This file is generated by generate-std.joke script. Do not edit manually! 2 | 3 | package hex 4 | 5 | import ( 6 | "encoding/hex" 7 | . "github.com/candid82/joker/core" 8 | ) 9 | 10 | var __decode_string__P ProcFn = __decode_string_ 11 | var decode_string_ Proc = Proc{Fn: __decode_string__P, Name: "decode_string_", Package: "std/hex"} 12 | 13 | func __decode_string_(_args []Object) Object { 14 | _c := len(_args) 15 | switch { 16 | case _c == 1: 17 | s := ExtractString(_args, 0) 18 | t, err := hex.DecodeString(s) 19 | PanicOnErr(err) 20 | _res := string(t) 21 | return MakeString(_res) 22 | 23 | default: 24 | PanicArity(_c) 25 | } 26 | return NIL 27 | } 28 | 29 | var __encode_string__P ProcFn = __encode_string_ 30 | var encode_string_ Proc = Proc{Fn: __encode_string__P, Name: "encode_string_", Package: "std/hex"} 31 | 32 | func __encode_string_(_args []Object) Object { 33 | _c := len(_args) 34 | switch { 35 | case _c == 1: 36 | s := ExtractString(_args, 0) 37 | _res := hex.EncodeToString([]byte(s)) 38 | return MakeString(_res) 39 | 40 | default: 41 | PanicArity(_c) 42 | } 43 | return NIL 44 | } 45 | 46 | func Init() { 47 | 48 | InternsOrThunks() 49 | } 50 | 51 | var hexNamespace = GLOBAL_ENV.EnsureSymbolIsLib(MakeSymbol("joker.hex")) 52 | 53 | func init() { 54 | hexNamespace.Lazy = Init 55 | } 56 | -------------------------------------------------------------------------------- /std/hex/a_hex_slow_init.go: -------------------------------------------------------------------------------- 1 | // This file is generated by generate-std.joke script. Do not edit manually! 2 | 3 | package hex 4 | 5 | import ( 6 | "fmt" 7 | . "github.com/candid82/joker/core" 8 | "os" 9 | ) 10 | 11 | func InternsOrThunks() { 12 | if VerbosityLevel > 0 { 13 | fmt.Fprintln(os.Stderr, "Lazily running slow version of hex.InternsOrThunks().") 14 | } 15 | hexNamespace.ResetMeta(MakeMeta(nil, `Implements hexadecimal encoding and decoding.`, "1.0")) 16 | 17 | hexNamespace.InternVar("decode-string", decode_string_, 18 | MakeMeta( 19 | NewListFrom(NewVectorFrom(MakeSymbol("s"))), 20 | `Returns the bytes represented by the hexadecimal string s.`, "1.0").Plus(MakeKeyword("tag"), String{S: "String"})) 21 | 22 | hexNamespace.InternVar("encode-string", encode_string_, 23 | MakeMeta( 24 | NewListFrom(NewVectorFrom(MakeSymbol("s"))), 25 | `Returns the hexadecimal encoding of s.`, "1.0").Plus(MakeKeyword("tag"), String{S: "String"})) 26 | 27 | } 28 | -------------------------------------------------------------------------------- /std/html.joke: -------------------------------------------------------------------------------- 1 | (ns ^{:go-imports ["html"] 2 | :doc "Provides functions for escaping and unescaping HTML text."} 3 | html) 4 | 5 | (defn ^String escape 6 | "Escapes special characters like < to become <. It escapes only five such characters: <, >, &, ' and \"." 7 | {:added "1.0" 8 | :go "html.EscapeString(s)"} 9 | [^String s]) 10 | 11 | (defn ^String unescape 12 | "Unescapes entities like < to become <." 13 | {:added "1.0" 14 | :go "html.UnescapeString(s)"} 15 | [^String s]) 16 | -------------------------------------------------------------------------------- /std/html/a_html.go: -------------------------------------------------------------------------------- 1 | // This file is generated by generate-std.joke script. Do not edit manually! 2 | 3 | package html 4 | 5 | import ( 6 | . "github.com/candid82/joker/core" 7 | "html" 8 | ) 9 | 10 | var __escape__P ProcFn = __escape_ 11 | var escape_ Proc = Proc{Fn: __escape__P, Name: "escape_", Package: "std/html"} 12 | 13 | func __escape_(_args []Object) Object { 14 | _c := len(_args) 15 | switch { 16 | case _c == 1: 17 | s := ExtractString(_args, 0) 18 | _res := html.EscapeString(s) 19 | return MakeString(_res) 20 | 21 | default: 22 | PanicArity(_c) 23 | } 24 | return NIL 25 | } 26 | 27 | var __unescape__P ProcFn = __unescape_ 28 | var unescape_ Proc = Proc{Fn: __unescape__P, Name: "unescape_", Package: "std/html"} 29 | 30 | func __unescape_(_args []Object) Object { 31 | _c := len(_args) 32 | switch { 33 | case _c == 1: 34 | s := ExtractString(_args, 0) 35 | _res := html.UnescapeString(s) 36 | return MakeString(_res) 37 | 38 | default: 39 | PanicArity(_c) 40 | } 41 | return NIL 42 | } 43 | 44 | func Init() { 45 | 46 | InternsOrThunks() 47 | } 48 | 49 | var htmlNamespace = GLOBAL_ENV.EnsureSymbolIsLib(MakeSymbol("joker.html")) 50 | 51 | func init() { 52 | htmlNamespace.Lazy = Init 53 | } 54 | -------------------------------------------------------------------------------- /std/html/a_html_fast_init.go: -------------------------------------------------------------------------------- 1 | // This file is generated by generate-std.joke script. Do not edit manually! 2 | 3 | //go:build !gen_code 4 | // +build !gen_code 5 | 6 | package html 7 | 8 | import ( 9 | "fmt" 10 | . "github.com/candid82/joker/core" 11 | "os" 12 | ) 13 | 14 | func InternsOrThunks() { 15 | if VerbosityLevel > 0 { 16 | fmt.Fprintln(os.Stderr, "Lazily running fast version of html.InternsOrThunks().") 17 | } 18 | STD_thunk_html_escape__var = __escape_ 19 | STD_thunk_html_unescape__var = __unescape_ 20 | } 21 | -------------------------------------------------------------------------------- /std/html/a_html_slow_init.go: -------------------------------------------------------------------------------- 1 | // This file is generated by generate-std.joke script. Do not edit manually! 2 | 3 | //go:build gen_code 4 | // +build gen_code 5 | 6 | package html 7 | 8 | import ( 9 | "fmt" 10 | . "github.com/candid82/joker/core" 11 | "os" 12 | ) 13 | 14 | func InternsOrThunks() { 15 | if VerbosityLevel > 0 { 16 | fmt.Fprintln(os.Stderr, "Lazily running slow version of html.InternsOrThunks().") 17 | } 18 | htmlNamespace.ResetMeta(MakeMeta(nil, `Provides functions for escaping and unescaping HTML text.`, "1.0")) 19 | 20 | htmlNamespace.InternVar("escape", escape_, 21 | MakeMeta( 22 | NewListFrom(NewVectorFrom(MakeSymbol("s"))), 23 | `Escapes special characters like < to become <. It escapes only five such characters: <, >, &, ' and ".`, "1.0").Plus(MakeKeyword("tag"), String{S: "String"})) 24 | 25 | htmlNamespace.InternVar("unescape", unescape_, 26 | MakeMeta( 27 | NewListFrom(NewVectorFrom(MakeSymbol("s"))), 28 | `Unescapes entities like < to become <.`, "1.0").Plus(MakeKeyword("tag"), String{S: "String"})) 29 | 30 | } 31 | -------------------------------------------------------------------------------- /std/http.joke: -------------------------------------------------------------------------------- 1 | (ns ^{:go-imports [] 2 | :doc "Provides HTTP client and server implementations."} 3 | http) 4 | 5 | (defn send 6 | "Sends an HTTP request and returns an HTTP response. 7 | request is a map with the following keys: 8 | - url (string) 9 | - method (string, keyword or symbol, defaults to :get) 10 | - body (string) 11 | - host (string, overrides Host header if provided) 12 | - headers (map). 13 | All keys except for url are optional. 14 | response is a map with the following keys: 15 | - status (int) 16 | - body (string) 17 | - headers (map) 18 | - content-length (int)" 19 | {:added "1.0" 20 | :go "sendRequest(request)"} 21 | [^Map request]) 22 | 23 | (defn start-server 24 | "Starts HTTP server on the TCP network address addr." 25 | {:added "1.0" 26 | :go "startServer(addr, handler)"} 27 | [^String addr ^Callable handler]) 28 | 29 | (defn start-file-server 30 | "Starts HTTP server on the TCP network address addr that 31 | serves HTTP requests with the contents of the file system rooted at root." 32 | {:added "1.0" 33 | :go "startFileServer(addr, root)"} 34 | [^String addr ^String root]) 35 | -------------------------------------------------------------------------------- /std/http/a_http.go: -------------------------------------------------------------------------------- 1 | // This file is generated by generate-std.joke script. Do not edit manually! 2 | 3 | package http 4 | 5 | import ( 6 | . "github.com/candid82/joker/core" 7 | ) 8 | 9 | var __send__P ProcFn = __send_ 10 | var send_ Proc = Proc{Fn: __send__P, Name: "send_", Package: "std/http"} 11 | 12 | func __send_(_args []Object) Object { 13 | _c := len(_args) 14 | switch { 15 | case _c == 1: 16 | request := ExtractMap(_args, 0) 17 | _res := sendRequest(request) 18 | return _res 19 | 20 | default: 21 | PanicArity(_c) 22 | } 23 | return NIL 24 | } 25 | 26 | var __start_file_server__P ProcFn = __start_file_server_ 27 | var start_file_server_ Proc = Proc{Fn: __start_file_server__P, Name: "start_file_server_", Package: "std/http"} 28 | 29 | func __start_file_server_(_args []Object) Object { 30 | _c := len(_args) 31 | switch { 32 | case _c == 2: 33 | addr := ExtractString(_args, 0) 34 | root := ExtractString(_args, 1) 35 | _res := startFileServer(addr, root) 36 | return _res 37 | 38 | default: 39 | PanicArity(_c) 40 | } 41 | return NIL 42 | } 43 | 44 | var __start_server__P ProcFn = __start_server_ 45 | var start_server_ Proc = Proc{Fn: __start_server__P, Name: "start_server_", Package: "std/http"} 46 | 47 | func __start_server_(_args []Object) Object { 48 | _c := len(_args) 49 | switch { 50 | case _c == 2: 51 | addr := ExtractString(_args, 0) 52 | handler := ExtractCallable(_args, 1) 53 | _res := startServer(addr, handler) 54 | return _res 55 | 56 | default: 57 | PanicArity(_c) 58 | } 59 | return NIL 60 | } 61 | 62 | func Init() { 63 | 64 | InternsOrThunks() 65 | } 66 | 67 | var httpNamespace = GLOBAL_ENV.EnsureSymbolIsLib(MakeSymbol("joker.http")) 68 | 69 | func init() { 70 | httpNamespace.Lazy = Init 71 | } 72 | -------------------------------------------------------------------------------- /std/http/a_http_slow_init.go: -------------------------------------------------------------------------------- 1 | // This file is generated by generate-std.joke script. Do not edit manually! 2 | 3 | package http 4 | 5 | import ( 6 | "fmt" 7 | . "github.com/candid82/joker/core" 8 | "os" 9 | ) 10 | 11 | func InternsOrThunks() { 12 | if VerbosityLevel > 0 { 13 | fmt.Fprintln(os.Stderr, "Lazily running slow version of http.InternsOrThunks().") 14 | } 15 | httpNamespace.ResetMeta(MakeMeta(nil, `Provides HTTP client and server implementations.`, "1.0")) 16 | 17 | httpNamespace.InternVar("send", send_, 18 | MakeMeta( 19 | NewListFrom(NewVectorFrom(MakeSymbol("request"))), 20 | `Sends an HTTP request and returns an HTTP response. 21 | request is a map with the following keys: 22 | - url (string) 23 | - method (string, keyword or symbol, defaults to :get) 24 | - body (string) 25 | - host (string, overrides Host header if provided) 26 | - headers (map). 27 | All keys except for url are optional. 28 | response is a map with the following keys: 29 | - status (int) 30 | - body (string) 31 | - headers (map) 32 | - content-length (int)`, "1.0")) 33 | 34 | httpNamespace.InternVar("start-file-server", start_file_server_, 35 | MakeMeta( 36 | NewListFrom(NewVectorFrom(MakeSymbol("addr"), MakeSymbol("root"))), 37 | `Starts HTTP server on the TCP network address addr that 38 | serves HTTP requests with the contents of the file system rooted at root.`, "1.0")) 39 | 40 | httpNamespace.InternVar("start-server", start_server_, 41 | MakeMeta( 42 | NewListFrom(NewVectorFrom(MakeSymbol("addr"), MakeSymbol("handler"))), 43 | `Starts HTTP server on the TCP network address addr.`, "1.0")) 44 | 45 | } 46 | -------------------------------------------------------------------------------- /std/intern.tmpl: -------------------------------------------------------------------------------- 1 | {nsName}Namespace.InternVar("{fnName}", {goName}, 2 | MakeMeta( 3 | {args}, 4 | {fnDocstring}, "{added}"){moreMeta}) 5 | -------------------------------------------------------------------------------- /std/io.joke: -------------------------------------------------------------------------------- 1 | (ns ^{:go-imports ["io"] 2 | :doc "Provides basic interfaces to I/O primitives."} 3 | io) 4 | 5 | (defn ^Int copy 6 | "Copies from src to dst until either EOF is reached on src or an error occurs. 7 | Returns the number of bytes copied or throws an error. 8 | src must be IOReader, e.g. as returned by joker.os/open. 9 | dst must be IOWriter, e.g. as returned by joker.os/create." 10 | {:added "1.0" 11 | :go "! n, err := io.Copy(dst, src); PanicOnErr(err); _res := int(n)"} ;; TODO: 32-bit issue 12 | [^IOWriter dst ^IOReader src]) 13 | 14 | (defn pipe 15 | "Pipe creates a synchronous in-memory pipe. It can be used to connect code expecting an IOReader 16 | with code expecting an IOWriter. 17 | Returns a vector [reader, writer]." 18 | {:added "1.0" 19 | :go "pipe()"} 20 | []) 21 | 22 | (defn close 23 | "Closes f (IOWriter, IOReader, or File) if possible. Otherwise throws an error." 24 | {:added "1.0" 25 | :go "close(f)"} 26 | [^Object f]) 27 | 28 | (defn ^String read 29 | "Reads up to n bytes from IOReader r and returns a string of the read bytes. 30 | May return a shorter (or blank) string if EOF is encountered." 31 | {:added "1.3.6" 32 | :go "read(r, n)"} 33 | ^String [^IOReader r ^Int n]) 34 | -------------------------------------------------------------------------------- /std/io/a_io.go: -------------------------------------------------------------------------------- 1 | // This file is generated by generate-std.joke script. Do not edit manually! 2 | 3 | package io 4 | 5 | import ( 6 | . "github.com/candid82/joker/core" 7 | "io" 8 | ) 9 | 10 | var __close__P ProcFn = __close_ 11 | var close_ Proc = Proc{Fn: __close__P, Name: "close_", Package: "std/io"} 12 | 13 | func __close_(_args []Object) Object { 14 | _c := len(_args) 15 | switch { 16 | case _c == 1: 17 | f := ExtractObject(_args, 0) 18 | _res := close(f) 19 | return _res 20 | 21 | default: 22 | PanicArity(_c) 23 | } 24 | return NIL 25 | } 26 | 27 | var __copy__P ProcFn = __copy_ 28 | var copy_ Proc = Proc{Fn: __copy__P, Name: "copy_", Package: "std/io"} 29 | 30 | func __copy_(_args []Object) Object { 31 | _c := len(_args) 32 | switch { 33 | case _c == 2: 34 | dst := ExtractIOWriter(_args, 0) 35 | src := ExtractIOReader(_args, 1) 36 | n, err := io.Copy(dst, src) 37 | PanicOnErr(err) 38 | _res := int(n) 39 | return MakeInt(_res) 40 | 41 | default: 42 | PanicArity(_c) 43 | } 44 | return NIL 45 | } 46 | 47 | var __pipe__P ProcFn = __pipe_ 48 | var pipe_ Proc = Proc{Fn: __pipe__P, Name: "pipe_", Package: "std/io"} 49 | 50 | func __pipe_(_args []Object) Object { 51 | _c := len(_args) 52 | switch { 53 | case _c == 0: 54 | _res := pipe() 55 | return _res 56 | 57 | default: 58 | PanicArity(_c) 59 | } 60 | return NIL 61 | } 62 | 63 | var __read__P ProcFn = __read_ 64 | var read_ Proc = Proc{Fn: __read__P, Name: "read_", Package: "std/io"} 65 | 66 | func __read_(_args []Object) Object { 67 | _c := len(_args) 68 | switch { 69 | case _c == 2: 70 | r := ExtractIOReader(_args, 0) 71 | n := ExtractInt(_args, 1) 72 | _res := read(r, n) 73 | return MakeString(_res) 74 | 75 | default: 76 | PanicArity(_c) 77 | } 78 | return NIL 79 | } 80 | 81 | func Init() { 82 | 83 | InternsOrThunks() 84 | } 85 | 86 | var ioNamespace = GLOBAL_ENV.EnsureSymbolIsLib(MakeSymbol("joker.io")) 87 | 88 | func init() { 89 | ioNamespace.Lazy = Init 90 | } 91 | -------------------------------------------------------------------------------- /std/io/a_io_slow_init.go: -------------------------------------------------------------------------------- 1 | // This file is generated by generate-std.joke script. Do not edit manually! 2 | 3 | package io 4 | 5 | import ( 6 | "fmt" 7 | . "github.com/candid82/joker/core" 8 | "os" 9 | ) 10 | 11 | func InternsOrThunks() { 12 | if VerbosityLevel > 0 { 13 | fmt.Fprintln(os.Stderr, "Lazily running slow version of io.InternsOrThunks().") 14 | } 15 | ioNamespace.ResetMeta(MakeMeta(nil, `Provides basic interfaces to I/O primitives.`, "1.0")) 16 | 17 | ioNamespace.InternVar("close", close_, 18 | MakeMeta( 19 | NewListFrom(NewVectorFrom(MakeSymbol("f"))), 20 | `Closes f (IOWriter, IOReader, or File) if possible. Otherwise throws an error.`, "1.0")) 21 | 22 | ioNamespace.InternVar("copy", copy_, 23 | MakeMeta( 24 | NewListFrom(NewVectorFrom(MakeSymbol("dst"), MakeSymbol("src"))), 25 | `Copies from src to dst until either EOF is reached on src or an error occurs. 26 | Returns the number of bytes copied or throws an error. 27 | src must be IOReader, e.g. as returned by joker.os/open. 28 | dst must be IOWriter, e.g. as returned by joker.os/create.`, "1.0").Plus(MakeKeyword("tag"), String{S: "Int"})) 29 | 30 | ioNamespace.InternVar("pipe", pipe_, 31 | MakeMeta( 32 | NewListFrom(NewVectorFrom()), 33 | `Pipe creates a synchronous in-memory pipe. It can be used to connect code expecting an IOReader 34 | with code expecting an IOWriter. 35 | Returns a vector [reader, writer].`, "1.0")) 36 | 37 | ioNamespace.InternVar("read", read_, 38 | MakeMeta( 39 | NewListFrom(NewVectorFrom(MakeSymbol("r"), MakeSymbol("n"))), 40 | `Reads up to n bytes from IOReader r and returns a string of the read bytes. 41 | May return a shorter (or blank) string if EOF is encountered.`, "1.3.6").Plus(MakeKeyword("tag"), String{S: "String"})) 42 | 43 | } 44 | -------------------------------------------------------------------------------- /std/io/io_native.go: -------------------------------------------------------------------------------- 1 | package io 2 | 3 | import ( 4 | . "github.com/candid82/joker/core" 5 | "io" 6 | ) 7 | 8 | func pipe() Object { 9 | r, w := io.Pipe() 10 | res := EmptyVector() 11 | res = res.Conjoin(MakeIOReader(r)) 12 | res = res.Conjoin(MakeIOWriter(w)) 13 | return res 14 | } 15 | 16 | func close(f Object) Nil { 17 | if c, ok := f.(io.Closer); ok { 18 | if err := c.Close(); err != nil { 19 | panic(RT.NewError(err.Error())) 20 | } 21 | return NIL 22 | } 23 | panic(RT.NewError("Object is not closable: " + f.ToString(false))) 24 | } 25 | 26 | func read(r io.Reader, n int) string { 27 | buf := make([]byte, n) 28 | cnt, err := r.Read(buf) 29 | if err != io.EOF { 30 | PanicOnErr(err) 31 | } 32 | return string(buf[:cnt]) 33 | } 34 | -------------------------------------------------------------------------------- /std/json.joke: -------------------------------------------------------------------------------- 1 | (ns ^{:go-imports [] 2 | :doc "Implements encoding and decoding of JSON as defined in RFC 4627."} 3 | json) 4 | 5 | (defn read-string 6 | "Parses the JSON-encoded data and return the result as a Joker value. 7 | Optional opts map may have the following keys: 8 | :keywords? - if true, JSON keys will be converted from strings to keywords." 9 | {:added "1.0" 10 | :go {1 "readString(s, nil)" 11 | 2 "readString(s, opts)"}} 12 | ([^String s]) 13 | ([^String s ^Map opts])) 14 | 15 | (defn write-string 16 | "Returns the JSON encoding of v." 17 | {:added "1.0" 18 | :go "writeString(v)"} 19 | [^Object v]) 20 | 21 | (defn json-seq 22 | "Returns the json records from rdr as a lazy sequence. 23 | rdr must be a string or implement io.Reader. 24 | Optional opts map may have the following keys: 25 | :keywords? - if true, JSON keys will be converted from strings to keywords." 26 | {:added "1.0" 27 | :go {1 "jsonSeqOpts(rdr, EmptyArrayMap())" 28 | 2 "jsonSeqOpts(rdr, opts)"}} 29 | ([^Object rdr]) 30 | ([^Object rdr ^Map opts])) 31 | -------------------------------------------------------------------------------- /std/json/a_json.go: -------------------------------------------------------------------------------- 1 | // This file is generated by generate-std.joke script. Do not edit manually! 2 | 3 | package json 4 | 5 | import ( 6 | . "github.com/candid82/joker/core" 7 | ) 8 | 9 | var __json_seq__P ProcFn = __json_seq_ 10 | var json_seq_ Proc = Proc{Fn: __json_seq__P, Name: "json_seq_", Package: "std/json"} 11 | 12 | func __json_seq_(_args []Object) Object { 13 | _c := len(_args) 14 | switch { 15 | case _c == 1: 16 | rdr := ExtractObject(_args, 0) 17 | _res := jsonSeqOpts(rdr, EmptyArrayMap()) 18 | return _res 19 | 20 | case _c == 2: 21 | rdr := ExtractObject(_args, 0) 22 | opts := ExtractMap(_args, 1) 23 | _res := jsonSeqOpts(rdr, opts) 24 | return _res 25 | 26 | default: 27 | PanicArity(_c) 28 | } 29 | return NIL 30 | } 31 | 32 | var __read_string__P ProcFn = __read_string_ 33 | var read_string_ Proc = Proc{Fn: __read_string__P, Name: "read_string_", Package: "std/json"} 34 | 35 | func __read_string_(_args []Object) Object { 36 | _c := len(_args) 37 | switch { 38 | case _c == 1: 39 | s := ExtractString(_args, 0) 40 | _res := readString(s, nil) 41 | return _res 42 | 43 | case _c == 2: 44 | s := ExtractString(_args, 0) 45 | opts := ExtractMap(_args, 1) 46 | _res := readString(s, opts) 47 | return _res 48 | 49 | default: 50 | PanicArity(_c) 51 | } 52 | return NIL 53 | } 54 | 55 | var __write_string__P ProcFn = __write_string_ 56 | var write_string_ Proc = Proc{Fn: __write_string__P, Name: "write_string_", Package: "std/json"} 57 | 58 | func __write_string_(_args []Object) Object { 59 | _c := len(_args) 60 | switch { 61 | case _c == 1: 62 | v := ExtractObject(_args, 0) 63 | _res := writeString(v) 64 | return _res 65 | 66 | default: 67 | PanicArity(_c) 68 | } 69 | return NIL 70 | } 71 | 72 | func Init() { 73 | 74 | InternsOrThunks() 75 | } 76 | 77 | var jsonNamespace = GLOBAL_ENV.EnsureSymbolIsLib(MakeSymbol("joker.json")) 78 | 79 | func init() { 80 | jsonNamespace.Lazy = Init 81 | } 82 | -------------------------------------------------------------------------------- /std/json/a_json_slow_init.go: -------------------------------------------------------------------------------- 1 | // This file is generated by generate-std.joke script. Do not edit manually! 2 | 3 | package json 4 | 5 | import ( 6 | "fmt" 7 | . "github.com/candid82/joker/core" 8 | "os" 9 | ) 10 | 11 | func InternsOrThunks() { 12 | if VerbosityLevel > 0 { 13 | fmt.Fprintln(os.Stderr, "Lazily running slow version of json.InternsOrThunks().") 14 | } 15 | jsonNamespace.ResetMeta(MakeMeta(nil, `Implements encoding and decoding of JSON as defined in RFC 4627.`, "1.0")) 16 | 17 | jsonNamespace.InternVar("json-seq", json_seq_, 18 | MakeMeta( 19 | NewListFrom(NewVectorFrom(MakeSymbol("rdr")), NewVectorFrom(MakeSymbol("rdr"), MakeSymbol("opts"))), 20 | `Returns the json records from rdr as a lazy sequence. 21 | rdr must be a string or implement io.Reader. 22 | Optional opts map may have the following keys: 23 | :keywords? - if true, JSON keys will be converted from strings to keywords.`, "1.0")) 24 | 25 | jsonNamespace.InternVar("read-string", read_string_, 26 | MakeMeta( 27 | NewListFrom(NewVectorFrom(MakeSymbol("s")), NewVectorFrom(MakeSymbol("s"), MakeSymbol("opts"))), 28 | `Parses the JSON-encoded data and return the result as a Joker value. 29 | Optional opts map may have the following keys: 30 | :keywords? - if true, JSON keys will be converted from strings to keywords.`, "1.0")) 31 | 32 | jsonNamespace.InternVar("write-string", write_string_, 33 | MakeMeta( 34 | NewListFrom(NewVectorFrom(MakeSymbol("v"))), 35 | `Returns the JSON encoding of v.`, "1.0")) 36 | 37 | } 38 | -------------------------------------------------------------------------------- /std/markdown.joke: -------------------------------------------------------------------------------- 1 | (ns ^{:go-imports [] 2 | :doc "Implements GitHub Flavored Markdown rendering."} 3 | markdown) 4 | 5 | (defn ^String convert-string 6 | "Returns the HTML rendering of Markdown string s. 7 | opts is an optional map of boolean rendering options (all default to true) 8 | 9 | :with-hard-wraps? - Render newlines as
    . 10 | :with-xhtml? - Render as XHTML. 11 | :with-unsafe? - When false, all raw html will be omitted from the output. When true html is passed through unchanged. 12 | " 13 | {:added "1.0" 14 | :go {1 "convertString(s)" 15 | 2 "convertStringOpts(s, opts)"}} 16 | ([^String s]) 17 | ([^String s ^Map opts])) 18 | -------------------------------------------------------------------------------- /std/markdown/a_markdown.go: -------------------------------------------------------------------------------- 1 | // This file is generated by generate-std.joke script. Do not edit manually! 2 | 3 | package markdown 4 | 5 | import ( 6 | . "github.com/candid82/joker/core" 7 | ) 8 | 9 | var __convert_string__P ProcFn = __convert_string_ 10 | var convert_string_ Proc = Proc{Fn: __convert_string__P, Name: "convert_string_", Package: "std/markdown"} 11 | 12 | func __convert_string_(_args []Object) Object { 13 | _c := len(_args) 14 | switch { 15 | case _c == 1: 16 | s := ExtractString(_args, 0) 17 | _res := convertString(s) 18 | return MakeString(_res) 19 | 20 | case _c == 2: 21 | s := ExtractString(_args, 0) 22 | opts := ExtractMap(_args, 1) 23 | _res := convertStringOpts(s, opts) 24 | return MakeString(_res) 25 | 26 | default: 27 | PanicArity(_c) 28 | } 29 | return NIL 30 | } 31 | 32 | func Init() { 33 | 34 | InternsOrThunks() 35 | } 36 | 37 | var markdownNamespace = GLOBAL_ENV.EnsureSymbolIsLib(MakeSymbol("joker.markdown")) 38 | 39 | func init() { 40 | markdownNamespace.Lazy = Init 41 | } 42 | -------------------------------------------------------------------------------- /std/markdown/a_markdown_slow_init.go: -------------------------------------------------------------------------------- 1 | // This file is generated by generate-std.joke script. Do not edit manually! 2 | 3 | package markdown 4 | 5 | import ( 6 | "fmt" 7 | . "github.com/candid82/joker/core" 8 | "os" 9 | ) 10 | 11 | func InternsOrThunks() { 12 | if VerbosityLevel > 0 { 13 | fmt.Fprintln(os.Stderr, "Lazily running slow version of markdown.InternsOrThunks().") 14 | } 15 | markdownNamespace.ResetMeta(MakeMeta(nil, `Implements GitHub Flavored Markdown rendering.`, "1.0")) 16 | 17 | markdownNamespace.InternVar("convert-string", convert_string_, 18 | MakeMeta( 19 | NewListFrom(NewVectorFrom(MakeSymbol("s")), NewVectorFrom(MakeSymbol("s"), MakeSymbol("opts"))), 20 | `Returns the HTML rendering of Markdown string s. 21 | opts is an optional map of boolean rendering options (all default to true) 22 | 23 | :with-hard-wraps? - Render newlines as
    . 24 | :with-xhtml? - Render as XHTML. 25 | :with-unsafe? - When false, all raw html will be omitted from the output. When true html is passed through unchanged. 26 | `, "1.0").Plus(MakeKeyword("tag"), String{S: "String"})) 27 | 28 | } 29 | -------------------------------------------------------------------------------- /std/markdown/markdown_native.go: -------------------------------------------------------------------------------- 1 | package markdown 2 | 3 | import ( 4 | "bytes" 5 | 6 | "github.com/yuin/goldmark" 7 | "github.com/yuin/goldmark/extension" 8 | "github.com/yuin/goldmark/parser" 9 | "github.com/yuin/goldmark/renderer" 10 | "github.com/yuin/goldmark/renderer/html" 11 | 12 | . "github.com/candid82/joker/core" 13 | ) 14 | 15 | func convertString(source string) string { 16 | return convert(source, []renderer.Option{html.WithHardWraps(), html.WithXHTML(), html.WithUnsafe()}) 17 | } 18 | 19 | func getKeywordFlag(opts Map, name string, def bool) bool { 20 | ok, entry := opts.Get(MakeKeyword(name)) 21 | if !ok { 22 | return def 23 | } 24 | return EnsureObjectIsBoolean(entry, name+": %s").B 25 | } 26 | 27 | func convertStringOpts(source string, options Map) string { 28 | renderOptions := []renderer.Option{} 29 | if flag := getKeywordFlag(options, "with-hard-wraps?", true); flag { 30 | renderOptions = append(renderOptions, html.WithHardWraps()) 31 | } 32 | if flag := getKeywordFlag(options, "with-xhtml?", true); flag { 33 | renderOptions = append(renderOptions, html.WithXHTML()) 34 | } 35 | if flag := getKeywordFlag(options, "with-unsafe?", true); flag { 36 | renderOptions = append(renderOptions, html.WithUnsafe()) 37 | } 38 | return convert(source, renderOptions) 39 | } 40 | 41 | func convert(source string, renderOptions []renderer.Option) string { 42 | md := goldmark.New( 43 | goldmark.WithExtensions( 44 | extension.GFM, 45 | extension.Table, 46 | extension.DefinitionList, 47 | extension.Footnote, 48 | extension.Typographer, 49 | ), 50 | goldmark.WithParserOptions( 51 | parser.WithAutoHeadingID(), 52 | ), 53 | goldmark.WithRendererOptions(renderOptions...), 54 | ) 55 | var buf bytes.Buffer 56 | if err := md.Convert([]byte(source), &buf); err != nil { 57 | panic(err) 58 | } 59 | return buf.String() 60 | } 61 | -------------------------------------------------------------------------------- /std/math/math_native.go: -------------------------------------------------------------------------------- 1 | package math 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | "math/big" 7 | 8 | . "github.com/candid82/joker/core" 9 | ) 10 | 11 | func modf(x float64) Object { 12 | i, f := math.Modf(x) 13 | res := EmptyVector() 14 | res = res.Conjoin(MakeDouble(i)) 15 | res = res.Conjoin(MakeDouble(f)) 16 | return res 17 | } 18 | 19 | func precision(x Number) *big.Int { 20 | switch n := x.(type) { 21 | case Precision: 22 | return n.Precision() 23 | default: 24 | panic(RT.NewArgTypeError(0, x, "BigInt, BigFloat, Int, or Double")) 25 | } 26 | } 27 | 28 | func setPrecision(prec Number, n *big.Float) *big.Float { 29 | p := prec.Int().I 30 | if p < 0 { 31 | panic(RT.NewError(fmt.Sprintf("prec must be a non-negative Int, but is %d", p))) 32 | } 33 | return big.NewFloat(0).Copy(n).SetPrec(uint(p)) 34 | } 35 | -------------------------------------------------------------------------------- /std/os/sh.go: -------------------------------------------------------------------------------- 1 | //go:build !plan9 2 | // +build !plan9 3 | 4 | package os 5 | 6 | import ( 7 | "bytes" 8 | "io" 9 | "os/exec" 10 | "syscall" 11 | 12 | . "github.com/candid82/joker/core" 13 | ) 14 | 15 | func sh(dir string, stdin io.Reader, stdout io.Writer, stderr io.Writer, name string, args []string) Object { 16 | cmd := exec.Command(name, args...) 17 | cmd.Dir = dir 18 | cmd.Stdin = stdin 19 | 20 | var stdoutBuffer, stderrBuffer bytes.Buffer 21 | if stdout != nil { 22 | cmd.Stdout = stdout 23 | } else { 24 | cmd.Stdout = &stdoutBuffer 25 | } 26 | if stderr != nil { 27 | cmd.Stderr = stderr 28 | } else { 29 | cmd.Stderr = &stderrBuffer 30 | } 31 | 32 | err := cmd.Start() 33 | PanicOnErr(err) 34 | 35 | RT.GIL.Unlock() 36 | err = cmd.Wait() 37 | RT.GIL.Lock() 38 | 39 | res := EmptyArrayMap() 40 | res.Add(MakeKeyword("success"), Boolean{B: err == nil}) 41 | 42 | var exitCode int 43 | if err != nil { 44 | res.Add(MakeKeyword("err-msg"), String{S: err.Error()}) 45 | if exiterr, ok := err.(*exec.ExitError); ok { 46 | ws := exiterr.Sys().(syscall.WaitStatus) 47 | exitCode = ws.ExitStatus() 48 | } else { 49 | exitCode = defaultFailedCode 50 | } 51 | } else { 52 | ws := cmd.ProcessState.Sys().(syscall.WaitStatus) 53 | exitCode = ws.ExitStatus() 54 | } 55 | res.Add(MakeKeyword("exit"), Int{I: exitCode}) 56 | if stdout == nil { 57 | res.Add(MakeKeyword("out"), String{S: string(stdoutBuffer.Bytes())}) 58 | } 59 | if stderr == nil { 60 | res.Add(MakeKeyword("err"), String{S: string(stderrBuffer.Bytes())}) 61 | } 62 | return res 63 | } 64 | -------------------------------------------------------------------------------- /std/os/sh_plan9.go: -------------------------------------------------------------------------------- 1 | package os 2 | 3 | import ( 4 | "bytes" 5 | "io" 6 | "os/exec" 7 | 8 | . "github.com/candid82/joker/core" 9 | ) 10 | 11 | func sh(dir string, stdin io.Reader, stdout io.Writer, stderr io.Writer, name string, args []string) Object { 12 | cmd := exec.Command(name, args...) 13 | cmd.Dir = dir 14 | cmd.Stdin = stdin 15 | 16 | var stdoutBuffer, stderrBuffer bytes.Buffer 17 | if stdout != nil { 18 | cmd.Stdout = stdout 19 | } else { 20 | cmd.Stdout = &stdoutBuffer 21 | } 22 | if stderr != nil { 23 | cmd.Stderr = stderr 24 | } else { 25 | cmd.Stderr = &stderrBuffer 26 | } 27 | 28 | err := cmd.Start() 29 | PanicOnErr(err) 30 | 31 | RT.GIL.Unlock() 32 | err = cmd.Wait() 33 | RT.GIL.Lock() 34 | 35 | res := EmptyArrayMap() 36 | res.Add(MakeKeyword("success"), Boolean{B: err == nil}) 37 | 38 | var exitCode int 39 | if err != nil { 40 | res.Add(MakeKeyword("err-msg"), String{S: err.Error()}) 41 | exitCode = defaultFailedCode 42 | } else { 43 | exitCode = 0 44 | } 45 | res.Add(MakeKeyword("exit"), Int{I: exitCode}) 46 | if stdout == nil { 47 | res.Add(MakeKeyword("out"), String{S: string(stdoutBuffer.Bytes())}) 48 | } 49 | if stderr == nil { 50 | res.Add(MakeKeyword("err"), String{S: string(stderrBuffer.Bytes())}) 51 | } 52 | return res 53 | } 54 | -------------------------------------------------------------------------------- /std/package-fast.tmpl: -------------------------------------------------------------------------------- 1 | // This file is generated by generate-std.joke script. Do not edit manually! 2 | 3 | //go:build !gen_code 4 | // +build !gen_code 5 | 6 | package {nsName} 7 | 8 | import ( 9 | {imports} 10 | "fmt" 11 | "os" 12 | ) 13 | 14 | func InternsOrThunks() { 15 | if VerbosityLevel > 0 { 16 | fmt.Fprintln(os.Stderr, "Lazily running fast version of {nsName}.InternsOrThunks().") 17 | } 18 | {thunks} 19 | } 20 | -------------------------------------------------------------------------------- /std/package-slow.tmpl: -------------------------------------------------------------------------------- 1 | // This file is generated by generate-std.joke script. Do not edit manually! 2 | 3 | {maybeSlowOnly} 4 | package {nsName} 5 | 6 | import ( 7 | {imports} 8 | "fmt" 9 | "os" 10 | ) 11 | 12 | func InternsOrThunks() { 13 | if VerbosityLevel > 0 { 14 | fmt.Fprintln(os.Stderr, "Lazily running slow version of {nsName}.InternsOrThunks().") 15 | } 16 | {nsName}Namespace.ResetMeta(MakeMeta(nil, {nsDocstring}, "1.0")) 17 | 18 | {non-fn-interns} 19 | {fn-interns} 20 | } 21 | -------------------------------------------------------------------------------- /std/package.tmpl: -------------------------------------------------------------------------------- 1 | // This file is generated by generate-std.joke script. Do not edit manually! 2 | 3 | package {nsName} 4 | 5 | import ( 6 | {imports} 7 | ) 8 | 9 | {non-fn-decls} 10 | {fn-decls} 11 | func Init() { 12 | {non-fn-inits} 13 | InternsOrThunks() 14 | } 15 | 16 | var {nsName}Namespace = GLOBAL_ENV.EnsureSymbolIsLib(MakeSymbol("joker.{nsFullName}")) 17 | 18 | func init() { 19 | {nsName}Namespace.Lazy = Init 20 | } 21 | -------------------------------------------------------------------------------- /std/runtime.joke: -------------------------------------------------------------------------------- 1 | (ns ^{:go-imports ["runtime"] 2 | :doc "Provides access to Go and Joker runtime information."} 3 | runtime) 4 | 5 | (defn ^String go-root 6 | "Returns the GOROOT string (as returned by runtime/GOROOT())." 7 | {:added "1.0" 8 | :go "runtime.GOROOT()"} 9 | []) 10 | 11 | (defn ^String go-version 12 | "Returns the Go version string (as returned by runtime/Version())." 13 | {:added "1.0" 14 | :go "runtime.Version()"} 15 | []) 16 | 17 | (defn ^String joker-version 18 | "Returns the raw Joker version string (including the leading 'v', 19 | which joker.core/joker-version omits)." 20 | {:added "1.0" 21 | :go "VERSION"} 22 | []) 23 | -------------------------------------------------------------------------------- /std/runtime/a_runtime.go: -------------------------------------------------------------------------------- 1 | // This file is generated by generate-std.joke script. Do not edit manually! 2 | 3 | package runtime 4 | 5 | import ( 6 | . "github.com/candid82/joker/core" 7 | "runtime" 8 | ) 9 | 10 | var __go_root__P ProcFn = __go_root_ 11 | var go_root_ Proc = Proc{Fn: __go_root__P, Name: "go_root_", Package: "std/runtime"} 12 | 13 | func __go_root_(_args []Object) Object { 14 | _c := len(_args) 15 | switch { 16 | case _c == 0: 17 | _res := runtime.GOROOT() 18 | return MakeString(_res) 19 | 20 | default: 21 | PanicArity(_c) 22 | } 23 | return NIL 24 | } 25 | 26 | var __go_version__P ProcFn = __go_version_ 27 | var go_version_ Proc = Proc{Fn: __go_version__P, Name: "go_version_", Package: "std/runtime"} 28 | 29 | func __go_version_(_args []Object) Object { 30 | _c := len(_args) 31 | switch { 32 | case _c == 0: 33 | _res := runtime.Version() 34 | return MakeString(_res) 35 | 36 | default: 37 | PanicArity(_c) 38 | } 39 | return NIL 40 | } 41 | 42 | var __joker_version__P ProcFn = __joker_version_ 43 | var joker_version_ Proc = Proc{Fn: __joker_version__P, Name: "joker_version_", Package: "std/runtime"} 44 | 45 | func __joker_version_(_args []Object) Object { 46 | _c := len(_args) 47 | switch { 48 | case _c == 0: 49 | _res := VERSION 50 | return MakeString(_res) 51 | 52 | default: 53 | PanicArity(_c) 54 | } 55 | return NIL 56 | } 57 | 58 | func Init() { 59 | 60 | InternsOrThunks() 61 | } 62 | 63 | var runtimeNamespace = GLOBAL_ENV.EnsureSymbolIsLib(MakeSymbol("joker.runtime")) 64 | 65 | func init() { 66 | runtimeNamespace.Lazy = Init 67 | } 68 | -------------------------------------------------------------------------------- /std/runtime/a_runtime_slow_init.go: -------------------------------------------------------------------------------- 1 | // This file is generated by generate-std.joke script. Do not edit manually! 2 | 3 | package runtime 4 | 5 | import ( 6 | "fmt" 7 | . "github.com/candid82/joker/core" 8 | "os" 9 | ) 10 | 11 | func InternsOrThunks() { 12 | if VerbosityLevel > 0 { 13 | fmt.Fprintln(os.Stderr, "Lazily running slow version of runtime.InternsOrThunks().") 14 | } 15 | runtimeNamespace.ResetMeta(MakeMeta(nil, `Provides access to Go and Joker runtime information.`, "1.0")) 16 | 17 | runtimeNamespace.InternVar("go-root", go_root_, 18 | MakeMeta( 19 | NewListFrom(NewVectorFrom()), 20 | `Returns the GOROOT string (as returned by runtime/GOROOT()).`, "1.0").Plus(MakeKeyword("tag"), String{S: "String"})) 21 | 22 | runtimeNamespace.InternVar("go-version", go_version_, 23 | MakeMeta( 24 | NewListFrom(NewVectorFrom()), 25 | `Returns the Go version string (as returned by runtime/Version()).`, "1.0").Plus(MakeKeyword("tag"), String{S: "String"})) 26 | 27 | runtimeNamespace.InternVar("joker-version", joker_version_, 28 | MakeMeta( 29 | NewListFrom(NewVectorFrom()), 30 | `Returns the raw Joker version string (including the leading 'v', 31 | which joker.core/joker-version omits).`, "1.0").Plus(MakeKeyword("tag"), String{S: "String"})) 32 | 33 | } 34 | -------------------------------------------------------------------------------- /std/string/a_string_fast_init.go: -------------------------------------------------------------------------------- 1 | // This file is generated by generate-std.joke script. Do not edit manually! 2 | 3 | //go:build !gen_code 4 | // +build !gen_code 5 | 6 | package string 7 | 8 | import ( 9 | "fmt" 10 | . "github.com/candid82/joker/core" 11 | "os" 12 | ) 13 | 14 | func InternsOrThunks() { 15 | if VerbosityLevel > 0 { 16 | fmt.Fprintln(os.Stderr, "Lazily running fast version of string.InternsOrThunks().") 17 | } 18 | STD_thunk_string_isblank__var = __isblank_ 19 | STD_thunk_string_capitalize__var = __capitalize_ 20 | STD_thunk_string_isends_with__var = __isends_with_ 21 | STD_thunk_string_escape__var = __escape_ 22 | STD_thunk_string_isincludes__var = __isincludes_ 23 | STD_thunk_string_index_of__var = __index_of_ 24 | STD_thunk_string_join__var = __join_ 25 | STD_thunk_string_last_index_of__var = __last_index_of_ 26 | STD_thunk_string_lower_case__var = __lower_case_ 27 | STD_thunk_string_pad_left__var = __pad_left_ 28 | STD_thunk_string_pad_right__var = __pad_right_ 29 | STD_thunk_string_re_quote__var = __re_quote_ 30 | STD_thunk_string_replace__var = __replace_ 31 | STD_thunk_string_replace_first__var = __replace_first_ 32 | STD_thunk_string_reverse__var = __reverse_ 33 | STD_thunk_string_split__var = __split_ 34 | STD_thunk_string_split_lines__var = __split_lines_ 35 | STD_thunk_string_isstarts_with__var = __isstarts_with_ 36 | STD_thunk_string_trim__var = __trim_ 37 | STD_thunk_string_trim_left__var = __trim_left_ 38 | STD_thunk_string_trim_newline__var = __trim_newline_ 39 | STD_thunk_string_trim_right__var = __trim_right_ 40 | STD_thunk_string_triml__var = __triml_ 41 | STD_thunk_string_trimr__var = __trimr_ 42 | STD_thunk_string_upper_case__var = __upper_case_ 43 | } 44 | -------------------------------------------------------------------------------- /std/time/time_native.go: -------------------------------------------------------------------------------- 1 | package time 2 | 3 | import ( 4 | . "github.com/candid82/joker/core" 5 | "time" 6 | ) 7 | 8 | func inTimezone(t time.Time, tz string) time.Time { 9 | loc, err := time.LoadLocation(tz) 10 | PanicOnErr(err) 11 | return t.In(loc) 12 | } 13 | -------------------------------------------------------------------------------- /std/url.joke: -------------------------------------------------------------------------------- 1 | (ns ^{:go-imports ["net/url"] 2 | :doc "Parses URLs and implements query escaping."} 3 | url) 4 | 5 | (defn ^String path-escape 6 | "Escapes the string so it can be safely placed inside a URL path segment." 7 | {:added "1.0" 8 | :go "url.PathEscape(s)"} 9 | [^String s]) 10 | 11 | (defn ^String path-unescape 12 | "Does the inverse transformation of path-escape, converting each 3-byte encoded 13 | substring of the form \"%AB\" into the hex-decoded byte 0xAB. It also converts 14 | '+' into ' ' (space). It returns an error if any % is not followed by two hexadecimal digits. 15 | 16 | PathUnescape is identical to QueryUnescape except that it does not unescape '+' to ' ' (space)." 17 | {:added "1.0" 18 | :go "pathUnescape(s)"} 19 | [^String s]) 20 | 21 | (defn ^String query-escape 22 | "Escapes the string so it can be safely placed inside a URL query." 23 | {:added "1.0" 24 | :go "url.QueryEscape(s)"} 25 | [^String s]) 26 | 27 | (defn ^String query-unescape 28 | "Does the inverse transformation of query-escape, converting each 3-byte encoded 29 | substring of the form \"%AB\" into the hex-decoded byte 0xAB. It also converts 30 | '+' into ' ' (space). It returns an error if any % is not followed by two hexadecimal digits." 31 | {:added "1.0" 32 | :go "queryUnescape(s)"} 33 | [^String s]) 34 | 35 | (defn parse-query 36 | "Parses the URL-encoded query string and returns a map listing the vectors of values specified for each key. 37 | Always returns a non-nil map containing all the valid query parameters found. 38 | Query is expected to be a list of key=value settings separated by ampersands. A setting without 39 | an equals sign is interpreted as a key set to an empty value. Settings containing a non-URL-encoded 40 | semicolon are considered invalid. " 41 | {:added "1.3.6" 42 | :go "parseQuery(s)"} 43 | [^String s]) 44 | -------------------------------------------------------------------------------- /std/url/a_url_slow_init.go: -------------------------------------------------------------------------------- 1 | // This file is generated by generate-std.joke script. Do not edit manually! 2 | 3 | package url 4 | 5 | import ( 6 | "fmt" 7 | . "github.com/candid82/joker/core" 8 | "os" 9 | ) 10 | 11 | func InternsOrThunks() { 12 | if VerbosityLevel > 0 { 13 | fmt.Fprintln(os.Stderr, "Lazily running slow version of url.InternsOrThunks().") 14 | } 15 | urlNamespace.ResetMeta(MakeMeta(nil, `Parses URLs and implements query escaping.`, "1.0")) 16 | 17 | urlNamespace.InternVar("parse-query", parse_query_, 18 | MakeMeta( 19 | NewListFrom(NewVectorFrom(MakeSymbol("s"))), 20 | `Parses the URL-encoded query string and returns a map listing the vectors of values specified for each key. 21 | Always returns a non-nil map containing all the valid query parameters found. 22 | Query is expected to be a list of key=value settings separated by ampersands. A setting without 23 | an equals sign is interpreted as a key set to an empty value. Settings containing a non-URL-encoded 24 | semicolon are considered invalid. `, "1.3.6")) 25 | 26 | urlNamespace.InternVar("path-escape", path_escape_, 27 | MakeMeta( 28 | NewListFrom(NewVectorFrom(MakeSymbol("s"))), 29 | `Escapes the string so it can be safely placed inside a URL path segment.`, "1.0").Plus(MakeKeyword("tag"), String{S: "String"})) 30 | 31 | urlNamespace.InternVar("path-unescape", path_unescape_, 32 | MakeMeta( 33 | NewListFrom(NewVectorFrom(MakeSymbol("s"))), 34 | `Does the inverse transformation of path-escape, converting each 3-byte encoded 35 | substring of the form "%AB" into the hex-decoded byte 0xAB. It also converts 36 | '+' into ' ' (space). It returns an error if any % is not followed by two hexadecimal digits. 37 | 38 | PathUnescape is identical to QueryUnescape except that it does not unescape '+' to ' ' (space).`, "1.0").Plus(MakeKeyword("tag"), String{S: "String"})) 39 | 40 | urlNamespace.InternVar("query-escape", query_escape_, 41 | MakeMeta( 42 | NewListFrom(NewVectorFrom(MakeSymbol("s"))), 43 | `Escapes the string so it can be safely placed inside a URL query.`, "1.0").Plus(MakeKeyword("tag"), String{S: "String"})) 44 | 45 | urlNamespace.InternVar("query-unescape", query_unescape_, 46 | MakeMeta( 47 | NewListFrom(NewVectorFrom(MakeSymbol("s"))), 48 | `Does the inverse transformation of query-escape, converting each 3-byte encoded 49 | substring of the form "%AB" into the hex-decoded byte 0xAB. It also converts 50 | '+' into ' ' (space). It returns an error if any % is not followed by two hexadecimal digits.`, "1.0").Plus(MakeKeyword("tag"), String{S: "String"})) 51 | 52 | } 53 | -------------------------------------------------------------------------------- /std/url/url_native.go: -------------------------------------------------------------------------------- 1 | package url 2 | 3 | import ( 4 | "net/url" 5 | 6 | . "github.com/candid82/joker/core" 7 | ) 8 | 9 | func pathUnescape(s string) string { 10 | res, err := url.PathUnescape(s) 11 | if err != nil { 12 | panic(RT.NewError("Error unescaping string: " + err.Error())) 13 | } 14 | return res 15 | } 16 | 17 | func queryUnescape(s string) string { 18 | res, err := url.QueryUnescape(s) 19 | if err != nil { 20 | panic(RT.NewError("Error unescaping string: " + err.Error())) 21 | } 22 | return res 23 | } 24 | 25 | func parseQuery(s string) Object { 26 | values, _ := url.ParseQuery(s) 27 | res := EmptyArrayMap() 28 | for k, v := range values { 29 | res.Add(MakeString(k), MakeStringVector(v)) 30 | } 31 | return res 32 | } 33 | -------------------------------------------------------------------------------- /std/uuid.joke: -------------------------------------------------------------------------------- 1 | (ns ^{:doc "Generates UUIDs."} 2 | uuid) 3 | 4 | (defn ^String new 5 | "Creates a new random UUID." 6 | {:added "1.0" 7 | :go "new()"} 8 | []) 9 | -------------------------------------------------------------------------------- /std/uuid/a_uuid.go: -------------------------------------------------------------------------------- 1 | // This file is generated by generate-std.joke script. Do not edit manually! 2 | 3 | package uuid 4 | 5 | import ( 6 | . "github.com/candid82/joker/core" 7 | ) 8 | 9 | var __new__P ProcFn = __new_ 10 | var new_ Proc = Proc{Fn: __new__P, Name: "new_", Package: "std/uuid"} 11 | 12 | func __new_(_args []Object) Object { 13 | _c := len(_args) 14 | switch { 15 | case _c == 0: 16 | _res := new() 17 | return MakeString(_res) 18 | 19 | default: 20 | PanicArity(_c) 21 | } 22 | return NIL 23 | } 24 | 25 | func Init() { 26 | 27 | InternsOrThunks() 28 | } 29 | 30 | var uuidNamespace = GLOBAL_ENV.EnsureSymbolIsLib(MakeSymbol("joker.uuid")) 31 | 32 | func init() { 33 | uuidNamespace.Lazy = Init 34 | } 35 | -------------------------------------------------------------------------------- /std/uuid/a_uuid_slow_init.go: -------------------------------------------------------------------------------- 1 | // This file is generated by generate-std.joke script. Do not edit manually! 2 | 3 | package uuid 4 | 5 | import ( 6 | "fmt" 7 | . "github.com/candid82/joker/core" 8 | "os" 9 | ) 10 | 11 | func InternsOrThunks() { 12 | if VerbosityLevel > 0 { 13 | fmt.Fprintln(os.Stderr, "Lazily running slow version of uuid.InternsOrThunks().") 14 | } 15 | uuidNamespace.ResetMeta(MakeMeta(nil, `Generates UUIDs.`, "1.0")) 16 | 17 | uuidNamespace.InternVar("new", new_, 18 | MakeMeta( 19 | NewListFrom(NewVectorFrom()), 20 | `Creates a new random UUID.`, "1.0").Plus(MakeKeyword("tag"), String{S: "String"})) 21 | 22 | } 23 | -------------------------------------------------------------------------------- /std/uuid/uuid_native.go: -------------------------------------------------------------------------------- 1 | // Based on https://github.com/google/uuid 2 | // Copyright (c) 2009,2014 Google Inc. All rights reserved. 3 | 4 | package uuid 5 | 6 | import ( 7 | "crypto/rand" 8 | "encoding/hex" 9 | . "github.com/candid82/joker/core" 10 | "io" 11 | ) 12 | 13 | type UUID [16]byte 14 | 15 | var rander = rand.Reader // random function 16 | 17 | func (uuid UUID) String() string { 18 | var buf [36]byte 19 | encodeHex(buf[:], uuid) 20 | return string(buf[:]) 21 | } 22 | 23 | func encodeHex(dst []byte, uuid UUID) { 24 | hex.Encode(dst, uuid[:4]) 25 | dst[8] = '-' 26 | hex.Encode(dst[9:13], uuid[4:6]) 27 | dst[13] = '-' 28 | hex.Encode(dst[14:18], uuid[6:8]) 29 | dst[18] = '-' 30 | hex.Encode(dst[19:23], uuid[8:10]) 31 | dst[23] = '-' 32 | hex.Encode(dst[24:], uuid[10:]) 33 | } 34 | 35 | func new() string { 36 | var uuid UUID 37 | _, err := io.ReadFull(rander, uuid[:]) 38 | if err != nil { 39 | panic(RT.NewError("Error generating UUID: " + err.Error())) 40 | } 41 | uuid[6] = (uuid[6] & 0x0f) | 0x40 // Version 4 42 | uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10 43 | return uuid.String() 44 | } 45 | -------------------------------------------------------------------------------- /std/yaml.joke: -------------------------------------------------------------------------------- 1 | (ns ^{:go-imports [] 2 | :doc "Implements encoding and decoding of YAML."} 3 | yaml) 4 | 5 | (defn read-string 6 | "Parses the YAML-encoded data and return the result as a Joker value." 7 | {:added "1.0" 8 | :go "readString(s)"} 9 | [^String s]) 10 | 11 | (defn write-string 12 | "Returns the YAML encoding of v." 13 | {:added "1.0" 14 | :go "writeString(v)"} 15 | [^Object v]) 16 | 17 | 18 | -------------------------------------------------------------------------------- /std/yaml/a_yaml.go: -------------------------------------------------------------------------------- 1 | // This file is generated by generate-std.joke script. Do not edit manually! 2 | 3 | package yaml 4 | 5 | import ( 6 | . "github.com/candid82/joker/core" 7 | ) 8 | 9 | var __read_string__P ProcFn = __read_string_ 10 | var read_string_ Proc = Proc{Fn: __read_string__P, Name: "read_string_", Package: "std/yaml"} 11 | 12 | func __read_string_(_args []Object) Object { 13 | _c := len(_args) 14 | switch { 15 | case _c == 1: 16 | s := ExtractString(_args, 0) 17 | _res := readString(s) 18 | return _res 19 | 20 | default: 21 | PanicArity(_c) 22 | } 23 | return NIL 24 | } 25 | 26 | var __write_string__P ProcFn = __write_string_ 27 | var write_string_ Proc = Proc{Fn: __write_string__P, Name: "write_string_", Package: "std/yaml"} 28 | 29 | func __write_string_(_args []Object) Object { 30 | _c := len(_args) 31 | switch { 32 | case _c == 1: 33 | v := ExtractObject(_args, 0) 34 | _res := writeString(v) 35 | return _res 36 | 37 | default: 38 | PanicArity(_c) 39 | } 40 | return NIL 41 | } 42 | 43 | func Init() { 44 | 45 | InternsOrThunks() 46 | } 47 | 48 | var yamlNamespace = GLOBAL_ENV.EnsureSymbolIsLib(MakeSymbol("joker.yaml")) 49 | 50 | func init() { 51 | yamlNamespace.Lazy = Init 52 | } 53 | -------------------------------------------------------------------------------- /std/yaml/a_yaml_slow_init.go: -------------------------------------------------------------------------------- 1 | // This file is generated by generate-std.joke script. Do not edit manually! 2 | 3 | package yaml 4 | 5 | import ( 6 | "fmt" 7 | . "github.com/candid82/joker/core" 8 | "os" 9 | ) 10 | 11 | func InternsOrThunks() { 12 | if VerbosityLevel > 0 { 13 | fmt.Fprintln(os.Stderr, "Lazily running slow version of yaml.InternsOrThunks().") 14 | } 15 | yamlNamespace.ResetMeta(MakeMeta(nil, `Implements encoding and decoding of YAML.`, "1.0")) 16 | 17 | yamlNamespace.InternVar("read-string", read_string_, 18 | MakeMeta( 19 | NewListFrom(NewVectorFrom(MakeSymbol("s"))), 20 | `Parses the YAML-encoded data and return the result as a Joker value.`, "1.0")) 21 | 22 | yamlNamespace.InternVar("write-string", write_string_, 23 | MakeMeta( 24 | NewListFrom(NewVectorFrom(MakeSymbol("v"))), 25 | `Returns the YAML encoding of v.`, "1.0")) 26 | 27 | } 28 | -------------------------------------------------------------------------------- /std/yaml/yaml_native.go: -------------------------------------------------------------------------------- 1 | package yaml 2 | 3 | import ( 4 | "fmt" 5 | 6 | "gopkg.in/yaml.v2" 7 | 8 | . "github.com/candid82/joker/core" 9 | ) 10 | 11 | func fromObject(obj Object) interface{} { 12 | switch obj := obj.(type) { 13 | case Keyword: 14 | return obj.ToString(false)[1:] 15 | case Boolean: 16 | return obj.B 17 | case Number: 18 | return obj.Double().D 19 | case Nil: 20 | return nil 21 | case Vec: 22 | cnt := obj.Count() 23 | res := make([]interface{}, cnt) 24 | for i := 0; i < cnt; i++ { 25 | res[i] = fromObject(obj.Nth(i)) 26 | } 27 | return res 28 | case Map: 29 | res := make(map[string]interface{}) 30 | for iter := obj.Iter(); iter.HasNext(); { 31 | p := iter.Next() 32 | var k string 33 | switch p.Key.(type) { 34 | case Keyword: 35 | k = p.Key.ToString(false)[1:] 36 | default: 37 | k = p.Key.ToString(false) 38 | } 39 | res[k] = fromObject(p.Value) 40 | } 41 | return res 42 | default: 43 | return obj.ToString(false) 44 | } 45 | } 46 | 47 | func toObject(v interface{}) Object { 48 | switch v := v.(type) { 49 | case string: 50 | return MakeString(v) 51 | case float64: 52 | return Double{D: v} 53 | case int: 54 | return Int{I: v} 55 | case bool: 56 | return Boolean{B: v} 57 | case nil: 58 | return NIL 59 | case []interface{}: 60 | res := EmptyVector() 61 | for _, v := range v { 62 | res = res.Conjoin(toObject(v)) 63 | } 64 | return res 65 | case map[interface{}]interface{}: 66 | res := EmptyArrayMap() 67 | for k, v := range v { 68 | res.Add(toObject(k), toObject(v)) 69 | } 70 | return res 71 | default: 72 | panic(RT.NewError(fmt.Sprintf("Unknown yaml value: %v", v))) 73 | } 74 | } 75 | 76 | func readString(s string) Object { 77 | var v interface{} 78 | if err := yaml.Unmarshal([]byte(s), &v); err != nil { 79 | panic(RT.NewError("Invalid yaml: " + err.Error())) 80 | } 81 | return toObject(v) 82 | } 83 | 84 | func writeString(obj Object) String { 85 | res, err := yaml.Marshal(fromObject(obj)) 86 | if err != nil { 87 | panic(RT.NewError("Cannot encode value to yaml: " + err.Error())) 88 | } 89 | return String{S: string(res)} 90 | } 91 | -------------------------------------------------------------------------------- /test-format.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | filelist=$(find $1 -type f -name "*.clj") 4 | 5 | for f in $filelist 6 | do 7 | ./joker --format $f > /tmp/joker-format.clj 8 | cat /tmp/joker-format.clj > $f 9 | done 10 | -------------------------------------------------------------------------------- /test-linter.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | filelist=$(find $1 -type f -name "$2" -not -name "project.clj" -not -name "user.clj") 4 | 5 | for f in $filelist 6 | do 7 | ERROR=$(./joker --lint $f 2>&1) 8 | if [ -n "$ERROR" ]; then 9 | echo $f 10 | echo "$ERROR" 11 | fi 12 | done 13 | -------------------------------------------------------------------------------- /tests/1.clj: -------------------------------------------------------------------------------- 1 | (def f 2 | (fn [a] 3 | (def f1 4 | (fn [b] 5 | (def f11 (fn [] b)) 6 | (+ a b))) 7 | (def f2 (fn [c] (+ a c))) 8 | (f1 10) 9 | (f2 20) 10 | (f11))) 11 | -------------------------------------------------------------------------------- /tests/eval/atoms.joke: -------------------------------------------------------------------------------- 1 | ; Copyright (c) Rich Hickey. All rights reserved. 2 | ; The use and distribution terms for this software are covered by the 3 | ; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ; which can be found in the file epl-v10.html at the root of this distribution. 5 | ; By using this software in any fashion, you are agreeing to be bound by 6 | ; the terms of this license. 7 | ; You must not remove this notice, or any other, from this software. 8 | 9 | ;;Author: Frantisek Sodomka 10 | 11 | (ns joker.test-clojure.atoms 12 | (:require [joker.test :refer [deftest is are testing]])) 13 | 14 | ; http://clojure.org/atoms 15 | 16 | ; atom 17 | ; deref, @-reader-macro 18 | ; swap! reset! 19 | ; compare-and-set! 20 | 21 | (def ^:dynamic *warn-on-reflection* false) 22 | 23 | (deftest swap-vals-returns-old-value 24 | (let [a (atom 0)] 25 | (is (= [0 1] (swap-vals! a inc))) 26 | (is (= [1 2] (swap-vals! a inc))) 27 | (is (= 2 @a)))) 28 | 29 | (deftest deref-swap-arities 30 | (binding [*warn-on-reflection* true] 31 | (let [a (atom 0)] 32 | (is (= [0 1] (swap-vals! a + 1))) 33 | (is (= [1 3] (swap-vals! a + 1 1))) 34 | (is (= [3 6] (swap-vals! a + 1 1 1))) 35 | (is (= [6 10] (swap-vals! a + 1 1 1 1))) 36 | (is (= 10 @a))))) 37 | 38 | (deftest deref-reset-returns-old-value 39 | (let [a (atom 0)] 40 | (is (= [0 :b] (reset-vals! a :b))) 41 | (is (= [:b 45M] (reset-vals! a 45M))) 42 | (is (= 45M @a)))) 43 | 44 | (deftest reset-on-deref-reset-equality 45 | (let [a (atom :usual-value)] 46 | (is (= :usual-value (reset! a (first (reset-vals! a :almost-never-seen-value))))))) 47 | -------------------------------------------------------------------------------- /tests/eval/better-cond-test.joke: -------------------------------------------------------------------------------- 1 | (ns joker.better-cond-test 2 | (:refer-clojure :exclude [cond if-let when-let if-some when-some]) 3 | (:require [joker.test :refer [deftest are]] 4 | [joker.better-cond :refer [cond]])) 5 | 6 | (deftest better-cond 7 | (are [x y] (= x y) 8 | 2 (cond (even? 3) 5 9 | (odd? 3) 2) 10 | 2 (cond (even? 3) 5 11 | :else 2) 12 | 2 (cond 13 | :let [x 2] 14 | x) 15 | 2 (cond 16 | :when-let [x 2] 17 | x) 18 | 2 (cond 19 | :when-some [x 2] 20 | x) 21 | nil (cond 22 | :when-let [x false] 23 | 2) 24 | 2 (cond 25 | :when-let [x true] 26 | 2) 27 | nil (cond 28 | :when-let [x nil] 29 | 2) 30 | 2 (cond 31 | :when-some [x false] 32 | 2) 33 | 2 (cond 34 | :when (even? 4) 35 | 2) 36 | nil (cond 37 | :when (even? 3) 38 | 2))) 39 | -------------------------------------------------------------------------------- /tests/eval/bolt.joke: -------------------------------------------------------------------------------- 1 | (ns joker.test-joker.bolt 2 | (:require 3 | [joker.test :refer [deftest is testing]] 4 | [joker.bolt :refer [open close create-bucket next-sequence put get]] 5 | [joker.os :refer [create-temp remove]] 6 | [joker.json :refer [write-string read-string]])) 7 | 8 | (deftest example-1 9 | (testing "example in docstring" 10 | (let [f (create-temp "" "bolt-test-") 11 | db-name (name f) 12 | _ (joker.os/close f) ; Windows requires the file itself to be closed for remove to work. 13 | db (open db-name 0600) 14 | users "users" 15 | joe "Joe Black"] 16 | (try 17 | (is (nil? (create-bucket db users))) 18 | (let [id (next-sequence db users) 19 | _ (is (= id 1)) 20 | _ (is (nil? (put db users (str id) (write-string {:id id :name joe})))) 21 | m (read-string (get db users (str id))) 22 | s (str (sort m))] 23 | (is (= (format "([\"id\" %d] [\"name\" \"%s\"])" id joe) 24 | s))) 25 | (finally (close db) 26 | (remove db-name)))))) 27 | -------------------------------------------------------------------------------- /tests/eval/classpath/a/b/c.joke: -------------------------------------------------------------------------------- 1 | (ns a.b.c) 2 | 3 | (println "this is a/b/c.joke") 4 | -------------------------------------------------------------------------------- /tests/eval/classpath/b/c.joke: -------------------------------------------------------------------------------- 1 | (ns b.c) 2 | 3 | (println "this is b/c.joke") 4 | -------------------------------------------------------------------------------- /tests/eval/classpath/d/e/f.joke: -------------------------------------------------------------------------------- 1 | (ns d.e.f 2 | (:require g.h.i)) 3 | 4 | (println "this is d/e/f.joke") 5 | -------------------------------------------------------------------------------- /tests/eval/classpath/g/h/i.joke: -------------------------------------------------------------------------------- 1 | (ns g.h.i) 2 | 3 | (println "this is g/h/i.joke") 4 | -------------------------------------------------------------------------------- /tests/eval/classpath/input.joke: -------------------------------------------------------------------------------- 1 | (require 'a.b.c) 2 | 3 | (binding [joker.core/*classpath* ["."]] 4 | (require 'd.e.f)) 5 | 6 | (binding [joker.core/*classpath* ["." "a"]] 7 | (require 'b.c)) 8 | 9 | (binding [joker.core/*classpath* ["x/y"]] 10 | (require 'z)) 11 | -------------------------------------------------------------------------------- /tests/eval/classpath/stdout.txt: -------------------------------------------------------------------------------- 1 | this is a/b/c.joke 2 | this is g/h/i.joke 3 | this is d/e/f.joke 4 | this is b/c.joke 5 | this is x/y/z.joke 6 | this is x/y/q/r/s.joke 7 | -------------------------------------------------------------------------------- /tests/eval/classpath/x/y/q/r/s.joke: -------------------------------------------------------------------------------- 1 | (ns q.r.s) 2 | 3 | (println "this is x/y/q/r/s.joke") 4 | -------------------------------------------------------------------------------- /tests/eval/classpath/x/y/z.joke: -------------------------------------------------------------------------------- 1 | (ns y.z) 2 | 3 | (println "this is x/y/z.joke") 4 | 5 | (require 'q.r.s) 6 | -------------------------------------------------------------------------------- /tests/eval/core.joke: -------------------------------------------------------------------------------- 1 | (ns joker.test-joker.core 2 | (:require [joker.test :refer [deftest is]])) 3 | 4 | (deftest test-meta 5 | (is (= (try (meta) (catch Error e "caught error")) "caught error"))) 6 | -------------------------------------------------------------------------------- /tests/eval/corelibs/input.joke: -------------------------------------------------------------------------------- 1 | (doseq [ns (remove #(= % 'user) joker.core/*core-namespaces*)] (require ns)) 2 | -------------------------------------------------------------------------------- /tests/eval/corelibs/stdout.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/candid82/joker/1eed1c02c38ef1373640c42af1423808826820cc/tests/eval/corelibs/stdout.txt -------------------------------------------------------------------------------- /tests/eval/csv.joke: -------------------------------------------------------------------------------- 1 | (ns joker.test-joker.csv 2 | (:require [joker.csv :as csv] 3 | [joker.test :refer [deftest is]])) 4 | 5 | (deftest test-csv-seq 6 | (is (= (csv/csv-seq "a,b,c\nd,e,f") '(["a" "b" "c"] ["d" "e" "f"])))) 7 | -------------------------------------------------------------------------------- /tests/eval/deps-test.joke: -------------------------------------------------------------------------------- 1 | (ns deps-test 2 | (:require 3 | [joker.test :refer [deftest is are testing]] 4 | [deps] 5 | [test-local.lib :as lib-local])) 6 | 7 | (deftest local-lib-test 8 | (testing "require from a local source" 9 | (is (= lib-local/b :b)))) 10 | -------------------------------------------------------------------------------- /tests/eval/deps.joke: -------------------------------------------------------------------------------- 1 | (ns deps 2 | (:require [joker.os :as os] 3 | [joker.filepath :as fp] 4 | [joker.string :as str])) 5 | 6 | (def lib-dir 7 | (-> *main-file* 8 | (str/split fp/separator) 9 | (butlast) 10 | (concat ["lib"]) 11 | ((fn [x] (apply str (interpose fp/separator x)))))) 12 | 13 | (ns-sources 14 | {"test-local.*" {:url lib-dir}}) 15 | -------------------------------------------------------------------------------- /tests/eval/format.joke: -------------------------------------------------------------------------------- 1 | (ns joker.test-joker.format 2 | (:require [joker.test :refer [deftest is are]])) 3 | 4 | (defonce FF 1.23299299999999999999999999999999999999M) 5 | 6 | (deftest test-native 7 | (are [x y] (= x y) 8 | (format "%x" 0xFFFFFFFFFFFFFFFF) "ffffffffffffffff" 9 | (format "%g" (- FF FF)) "0" 10 | (format "%g" 1/2) "0.5" )) 11 | -------------------------------------------------------------------------------- /tests/eval/hash.joke: -------------------------------------------------------------------------------- 1 | (ns joker.test-joker.hash 2 | (:require 3 | [joker.test :refer [deftest is testing]])) 4 | 5 | (deftest stable-hashes 6 | (testing "stable string hashes" 7 | (is (= (hash "hey") 4290027229)) 8 | (is (= (hash "there") 3102463325))) 9 | (testing "stable symbol hashes" 10 | (is (= (hash 'hey) 3793824397)) 11 | (is (= (hash 'there) 2940266537)) 12 | (is (= (hash 'joker.core/cond) 3232247079)) 13 | (is (= (hash 'joker.repl/doc) 3494663759)) 14 | (is (= (hash 'user/foo) 2980260858))) 15 | (testing "stable keyword hashes" 16 | (is (= (hash :hey) 819820356)) 17 | (is (= (hash :there) 1648208352)) 18 | (is (= (hash ::you) 3944753178)) 19 | (is (= (hash :user/foo) 1616868817)))) 20 | -------------------------------------------------------------------------------- /tests/eval/json.joke: -------------------------------------------------------------------------------- 1 | (ns joker.test-joker.json 2 | (:require [joker.json :as json] 3 | [joker.test :refer [deftest is]])) 4 | 5 | (deftest write-string 6 | (is (= "1" (json/write-string 1))) 7 | (is (= "1.2" (json/write-string 1.2))) 8 | (is (= "0.2" (json/write-string 1/5))) 9 | (is (= "1" (json/write-string 1.0))) 10 | (is (= "false" (json/write-string false))) 11 | (is (= "true" (json/write-string true))) 12 | (is (= "\"keyword\"" (json/write-string :keyword))) 13 | (is (= "null" (json/write-string nil))) 14 | (is (= "\"string\"" (json/write-string "string"))) 15 | (is (= "[]" (json/write-string []))) 16 | (is (= "{\"keyword\":\"string\"}" (json/write-string {:keyword "string"}))) 17 | (is (= "[1,true]" (json/write-string [1 true]))) 18 | (is (= "[\"string\",null]" (json/write-string (drop 2 [1 true "string" nil])))) 19 | (is (= "[4,5]" (json/write-string (list 4 5)))) 20 | (is (= "{\"m\":{\"k\":\"foo\"},\"s\":[\"string\",null],\"v\":[3]}" 21 | (json/write-string {:s (drop 2 [1 true "string" nil]) 22 | :v [3] 23 | :m {:k "foo"}})))) 24 | -------------------------------------------------------------------------------- /tests/eval/large-forked-stdout/input.joke: -------------------------------------------------------------------------------- 1 | (ns joker.tests.large-forked-stdout 2 | (:require [joker.os :as os] 3 | [joker.string :as s])) 4 | 5 | (let [exe (nth *command-line-args* 0) 6 | res (os/sh exe "lots-of-stderr.joke")] 7 | (print (:out res)) 8 | (let [ev (s/split-lines (:err res))] 9 | (println-err (ev 0)) 10 | (println-err (ev 1)) 11 | (println-err (ev (- (count ev) 2))))) 12 | -------------------------------------------------------------------------------- /tests/eval/large-forked-stdout/lots-of-stderr.joke: -------------------------------------------------------------------------------- 1 | (println "this is stdout; just one line.") 2 | (doseq [n (range 10000) ; Should be enough to block writing stderr until parent reads it. 3 | ] 4 | (println-err (format "A lazy dog knows %d tricks." n))) 5 | -------------------------------------------------------------------------------- /tests/eval/large-forked-stdout/stderr.txt: -------------------------------------------------------------------------------- 1 | A lazy dog knows 0 tricks. 2 | A lazy dog knows 1 tricks. 3 | A lazy dog knows 9999 tricks. 4 | -------------------------------------------------------------------------------- /tests/eval/large-forked-stdout/stdout.txt: -------------------------------------------------------------------------------- 1 | this is stdout; just one line. 2 | -------------------------------------------------------------------------------- /tests/eval/load-file/input.joke: -------------------------------------------------------------------------------- 1 | (try (load-file "parse-error.joke") (catch Error e (println "Caught parse error"))) 2 | -------------------------------------------------------------------------------- /tests/eval/load-file/parse-error.joke: -------------------------------------------------------------------------------- 1 | foo 2 | -------------------------------------------------------------------------------- /tests/eval/load-file/stdout.txt: -------------------------------------------------------------------------------- 1 | Caught parse error 2 | -------------------------------------------------------------------------------- /tests/eval/macro-test.joke: -------------------------------------------------------------------------------- 1 | (ns joker.macro-test 2 | (:require [joker.test :refer [deftest is]] 3 | [joker.string :as s])) 4 | 5 | (defmacro try-macro [ & body ] `(try ~@body (catch Error))) 6 | (def try-macro-expand (macroexpand '(try-macro))) 7 | 8 | (deftest try-log-test 9 | (is (= '(try (catch Error)) 10 | try-macro-expand) 11 | "should properly syntax-quote types")) 12 | 13 | (defmacro try-return [ & body ] `(try ~@body (catch Error t# t#))) 14 | (deftest try-expanding-typename 15 | (is (s/includes? (str (try-return (throw (ex-info "Ouch" {})))) "Exception: Ouch"))) 16 | 17 | (defmacro make-fn [] (fn [] nil)) 18 | (deftest try-expanding-literal 19 | (is (macroexpand '(make-fn)) "#object[Fn]") 20 | (is (str (make-fn)) "#object[Fn]")) 21 | -------------------------------------------------------------------------------- /tests/eval/map-test.joke: -------------------------------------------------------------------------------- 1 | (ns joker.map-test 2 | (:require [joker.test :refer [deftest is]])) 3 | 4 | 5 | (deftest hash-map-conversion 6 | (let [m {1 0 2 0 3 0 4 0 5 0 6 0 7 0 8 0}] 7 | (is (= ArrayMap (type m))) 8 | (is (= ArrayMap (type (merge m {1 1})))) 9 | (is (= ArrayMap (type (assoc m 1 2)))) 10 | (is (= HashMap (type (merge m {9 0})))) 11 | (is (= HashMap (type (assoc m 9 0)))))) 12 | -------------------------------------------------------------------------------- /tests/eval/markdown.joke: -------------------------------------------------------------------------------- 1 | (ns joker.test-joker.markdown 2 | (:require [joker.test :refer [deftest is are]] 3 | [joker.markdown :as md])) 4 | 5 | (def input "one\ntwo") 6 | 7 | (deftest with-hard-wraps? 8 | (is (= (md/convert-string input {:with-hard-wraps? true}) 9 | "

    one
    \ntwo

    \n") 10 | "add line breaks when true") 11 | (is (= (md/convert-string input {:with-hard-wraps? false}) 12 | "

    one\ntwo

    \n") 13 | "no line breaks when off") 14 | (is (= (md/convert-string input) 15 | "

    one
    \ntwo

    \n") 16 | "should default to on for backwards compatibility") 17 | (is (= (md/convert-string input {:with-hard-wraps? true}) 18 | (md/convert-string input)) 19 | "no flag and 'true' should be the same") 20 | (is (thrown-with-msg? EvalError 21 | #"Expected Boolean, got String" 22 | (md/convert-string input {:with-hard-wraps? "foo"})))) 23 | 24 | (deftest with-xhtml? 25 | (is (= (md/convert-string input {:with-xhtml? true}) 26 | "

    one
    \ntwo

    \n")) 27 | (is (= (md/convert-string input {:with-xhtml? false}) 28 | "

    one
    \ntwo

    \n")) 29 | (is (= (md/convert-string input) 30 | (md/convert-string input {:with-xhtml? true})))) 31 | 32 | 33 | (def unsafe-input "") 34 | 35 | (deftest with-unsafe? 36 | (is (= (md/convert-string unsafe-input {:with-unsafe? true}) 37 | "")) 38 | (is (= (md/convert-string unsafe-input {:with-unsafe? false}) 39 | "\n")) 40 | (is (= (md/convert-string unsafe-input) 41 | (md/convert-string unsafe-input {:with-unsafe? true})))) 42 | -------------------------------------------------------------------------------- /tests/eval/multi-methods/stdout.txt: -------------------------------------------------------------------------------- 1 | Hello! 2 | Bonjour! 3 | I don't know the Spanish language 4 | 1 5 | 1 6 | 6 7 | 5040 8 | (120 24 6 2 1) 9 | str: mink and stoat 10 | str: bear, skunk and sloth 11 | str: dog, cat, cow and horse 12 | numbers: 1 and 2 13 | Caught this expected exception: :0:0: Exception: No method in multimethod 'bat' for dispatch value: [Keyword Keyword] 14 | default: :hey then :there and finally (:you) 15 | 1 16 | 1 17 | -1 18 | Great function 19 | Better function 20 | (:foo 1 :bar) 21 | {:foo 1, :baz hello} 22 | Bob Dobbs 23 | Bob Dobbs 24 | ??? 25 | Drawing a red square 26 | Drawing a green triangle 27 | 1500 28 | 1099 29 | Drawing a red square 30 | Drawing a green triangle 31 | #object[Fn] 32 | nil 33 | -------------------------------------------------------------------------------- /tests/eval/os.joke: -------------------------------------------------------------------------------- 1 | (ns joker.test-joker.os 2 | (:require [joker.os :as os] 3 | [joker.test :refer [deftest is]])) 4 | 5 | (deftest exec-pipe 6 | (if (= (get (os/env) "TTY_TESTS") "1") 7 | (is (= 0 (:exit (os/exec "stty" {:args ["echo"] :stdin *in*})))) 8 | (println "Skipping tty tests (STDIN is not a tty)"))) 9 | -------------------------------------------------------------------------------- /tests/eval/printer.joke: -------------------------------------------------------------------------------- 1 | ; Copyright (c) Rich Hickey. All rights reserved. 2 | ; The use and distribution terms for this software are covered by the 3 | ; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ; which can be found in the file epl-v10.html at the root of this distribution. 5 | ; By using this software in any fashion, you are agreeing to be bound by 6 | ; the terms of this license. 7 | ; You must not remove this notice, or any other, from this software. 8 | 9 | ; Author: Stephen C. Gilardi 10 | 11 | (ns joker.test-joker.printer 12 | (:require [joker.test :refer [deftest is are]])) 13 | 14 | (deftest print-symbol-values 15 | (are [s v] (= s (pr-str v)) 16 | "##Inf" (joker.math/inf 1) 17 | "##-Inf" (joker.math/inf -1) 18 | "##NaN" (joker.math/nan))) 19 | 20 | (deftest print-double-values 21 | (are [s v] (= s (pr-str v)) 22 | "1.9999999" 1.9999999 23 | "1e-09" 1e-9 24 | "1e-07" 1e-7 25 | "0.001" 1e-3 26 | "1.0" 1e0)) 27 | 28 | (deftest print-and-confirm-double-values 29 | (are [v] (= v (read-string (pr-str v))) 30 | 1.9999999 31 | 1e-9 32 | 1e-7 33 | 1e-3 34 | 1e0 35 | 99999999.0 36 | 1.0e-100 37 | 1.0e+100 38 | -2.5 39 | -2.5e-3)) 40 | -------------------------------------------------------------------------------- /tests/eval/read-line/input.joke: -------------------------------------------------------------------------------- 1 | ;;; Move this into core.joke? 2 | (defmacro while-let 3 | "Continue processing an expression as long as it is true" 4 | [binding & forms] 5 | `(loop [] 6 | (when-let ~binding 7 | ~@forms 8 | (recur)))) 9 | 10 | ;; Read from a String. 11 | (with-in-str (slurp "lines.txt") 12 | (while-let [line (read-line)] 13 | (println "|" line))) 14 | 15 | ;; Now read from actual stdin.txt. 16 | (while-let [line (read-line)] 17 | (println "|" line)) 18 | -------------------------------------------------------------------------------- /tests/eval/read-line/lines.txt: -------------------------------------------------------------------------------- 1 | This is test input. 2 | It is substituted for :stdin via with-in-str in the test itself. 3 | -------------------------------------------------------------------------------- /tests/eval/read-line/stdin.txt: -------------------------------------------------------------------------------- 1 | This file becomes :stdin during the exec of Joker via run-forked-test. 2 | So it should appear in the output too. 3 | -------------------------------------------------------------------------------- /tests/eval/read-line/stdout.txt: -------------------------------------------------------------------------------- 1 | | This is test input. 2 | | It is substituted for :stdin via with-in-str in the test itself. 3 | | This file becomes :stdin during the exec of Joker via run-forked-test. 4 | | So it should appear in the output too. 5 | -------------------------------------------------------------------------------- /tests/eval/stdin-pipe/input.joke: -------------------------------------------------------------------------------- 1 | (ns stdin-pipe-test 2 | (:require [joker.os :as os])) 3 | 4 | (let [result (os/exec "cat" {:stdin *in*})] 5 | (print "|" (:out result))) 6 | -------------------------------------------------------------------------------- /tests/eval/stdin-pipe/stdin.txt: -------------------------------------------------------------------------------- 1 | stdin pipe test 2 | -------------------------------------------------------------------------------- /tests/eval/stdin-pipe/stdout.txt: -------------------------------------------------------------------------------- 1 | | stdin pipe test 2 | -------------------------------------------------------------------------------- /tests/eval/string.joke: -------------------------------------------------------------------------------- 1 | (ns joker.test-joker.string 2 | (:require [joker.string :as str] 3 | [joker.test :refer [deftest is]])) 4 | 5 | (deftest quoted 6 | (is (= (str #"a\.\(b\.c\)") (str (str/re-quote "a.(b.c)"))))) 7 | 8 | (deftest string 9 | (is (= "a.(b.c)" (str #"a.(b.c)")))) 10 | 11 | (deftest regex 12 | (is (= (str #"a.b.c") (str #"a.b.c")))) 13 | 14 | (deftest split-of-regex 15 | (is (= ["a" "c" "ef"] (str/split "abcdef" #"(b|d)")))) 16 | 17 | (deftest split-of-string 18 | (is (= ["ab" "def"] (str/split "abcdef" "c"))) 19 | (is (= ["abcdef"] (str/split "abcdef" "(b|d)")))) 20 | 21 | (deftest split-N-of-regex 22 | (is (= ["a" "b/c/d"] (str/split "a/b/c/d" #"/" 2)))) 23 | 24 | (deftest split-N-of-string 25 | (is (= ["a" "b/c/d"] (str/split "a/b/c/d" "/" 2)))) 26 | -------------------------------------------------------------------------------- /tests/eval/types.joke: -------------------------------------------------------------------------------- 1 | (ns joker.test-clojure.types 2 | (:require [joker.test :refer [deftest is]])) 3 | 4 | (deftest test-types 5 | (is (= (get (joker.core/types__) "Boolean") Boolean)) 6 | (is (= (get (joker.core/types__) "Int") Int))) 7 | 8 | (deftest stdin 9 | (is (instance? BufferedReader *in*) 10 | "*in* must be BufferedReader (technically it must implement StringReader interface) 11 | for (read-line) to work")) 12 | -------------------------------------------------------------------------------- /tests/eval/url-test.joke: -------------------------------------------------------------------------------- 1 | (ns joker.test-joker.url 2 | (:require [joker.test :refer [deftest is]] 3 | [joker.url :as url])) 4 | 5 | (deftest parse-query 6 | (is (= {} (url/parse-query ""))) 7 | (is (= {"q" ["1"]} (url/parse-query "q=1"))) 8 | (is (= {"q" [""]} (url/parse-query "q"))) 9 | (is (= {"foo" ["1"] "bar" ["2" "3"]} (url/parse-query "foo=1&bar=2&bar=3")))) 10 | -------------------------------------------------------------------------------- /tests/flags/config/.joker: -------------------------------------------------------------------------------- 1 | {:known-macros [foobar.macros/defthing]} 2 | -------------------------------------------------------------------------------- /tests/flags/input-warning.clj: -------------------------------------------------------------------------------- 1 | (let [a 1] "foo") 2 | -------------------------------------------------------------------------------- /tests/flags/input.clj: -------------------------------------------------------------------------------- 1 | (clojure.string/split "foobar") 2 | -------------------------------------------------------------------------------- /tests/flags/input.cljs: -------------------------------------------------------------------------------- 1 | (.log js/console) 2 | -------------------------------------------------------------------------------- /tests/flags/input.edn: -------------------------------------------------------------------------------- 1 | {:foobar #foo/bar "something something"} 2 | -------------------------------------------------------------------------------- /tests/flags/input.joke: -------------------------------------------------------------------------------- 1 | (joker.string/split "ha-ha-ha-ha" #"-") 2 | -------------------------------------------------------------------------------- /tests/flags/macro.clj: -------------------------------------------------------------------------------- 1 | (ns test.foo 2 | (:require [foobar.macros :refer [defthing]])) 3 | 4 | (defthing something 5 | "pewpew") 6 | 7 | -------------------------------------------------------------------------------- /tests/flags/script-flags.joke: -------------------------------------------------------------------------------- 1 | (println (subvec (joker.os/args) 2)) 2 | -------------------------------------------------------------------------------- /tests/lib/test-local/lib.joke: -------------------------------------------------------------------------------- 1 | (ns test-local.lib) 2 | 3 | (def b :b) 4 | -------------------------------------------------------------------------------- /tests/lib1.joke: -------------------------------------------------------------------------------- 1 | (in-ns 'tests.lib1) 2 | (joker.core/refer 'joker.core) 3 | (println "in lib1") 4 | 5 | (require ['tests.lib2]) 6 | 7 | (def v1 1) 8 | 9 | (defn f1 10 | [s] 11 | (count s)) 12 | -------------------------------------------------------------------------------- /tests/lib2.joke: -------------------------------------------------------------------------------- 1 | (in-ns 'tests.lib2) 2 | (joker.core/refer 'joker.core) 3 | 4 | (println "in lib2") 5 | 6 | (def v2 2) 7 | -------------------------------------------------------------------------------- /tests/lib3.joke: -------------------------------------------------------------------------------- 1 | (ns tests.lib3 2 | "Test namespace" 3 | (:require tests.lib2)) 4 | -------------------------------------------------------------------------------- /tests/linter/anonymous-fn/input.clj: -------------------------------------------------------------------------------- 1 | (#(prn {:a %, :b 2}) :c) 2 | (#(prn {:a %% :b 2}) :c) 3 | -------------------------------------------------------------------------------- /tests/linter/anonymous-fn/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/anonymous-fn/input.clj:2:13: Read error: Arg literal must be %, %& or %integer 2 | -------------------------------------------------------------------------------- /tests/linter/case/input.clj: -------------------------------------------------------------------------------- 1 | ;; Should PASS 2 | (case 1 ("F") false true) 3 | 4 | ;; Should FAIL 5 | (case 1 (1 2) false (2 4) true 4 false) 6 | -------------------------------------------------------------------------------- /tests/linter/case/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/case/input.clj:5:21: Parse error: Duplicate case test constant: (2 4) 2 | tests/linter/case/input.clj:5:32: Parse error: Duplicate case test constant: 4 3 | -------------------------------------------------------------------------------- /tests/linter/classnames/input.clj: -------------------------------------------------------------------------------- 1 | (ns test.ns) 2 | 3 | (defprotocol ITest 4 | (x [this])) 5 | 6 | (extend-protocol ITest 7 | datomic.db.Db 8 | (x [this] (println "do something"))) 9 | -------------------------------------------------------------------------------- /tests/linter/classnames/output.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/candid82/joker/1eed1c02c38ef1373640c42af1423808826820cc/tests/linter/classnames/output.txt -------------------------------------------------------------------------------- /tests/linter/cljs-interop/input.cljs: -------------------------------------------------------------------------------- 1 | (ns foo.bar 2 | (:import goog.net.XhrIo)) 3 | 4 | (XhrIo.send "http://example.com" (constantly nil)) 5 | js/Math.PI 6 | (def m js/Math) 7 | m.PI 8 | (.log js/console) 9 | (Math/PI) 10 | 11 | g.PI 12 | -------------------------------------------------------------------------------- /tests/linter/cljs-interop/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/cljs-interop/input.cljs:11:1: Parse error: Unable to resolve symbol: g 2 | -------------------------------------------------------------------------------- /tests/linter/cond/input.clj: -------------------------------------------------------------------------------- 1 | ;; Should PASS 2 | (cond 1 2) 3 | 4 | ;; Should FAIL 5 | (cond) 6 | (cond 1) 7 | 8 | -------------------------------------------------------------------------------- /tests/linter/cond/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/cond/input.clj:5:1: Parse warning: Empty cond 2 | tests/linter/cond/input.clj:6:7: Exception: cond requires an even number of forms 3 | -------------------------------------------------------------------------------- /tests/linter/conditionals-clj-1/input.clj: -------------------------------------------------------------------------------- 1 | ;; Should PASS 2 | 3 | [#?(:cljs 1)] 4 | (#?(:cljs 1)) 5 | {#?(:cljs 1)} 6 | {#?@(:clj [1 2])} 7 | {#?@(:clj [])} 8 | #?(:clj 1) 9 | #?@(:cljs 3) 10 | (def regexp #?(:clj re-pattern :cljs js/XRegExp)) 11 | 12 | 13 | ;; Should FAIL 14 | 15 | #?(:cljs) 16 | #?(:cljs (let [] 1) :default (let [] 1)) 17 | [#?@(:clj 1)] 18 | #?(:clj 234ewr :cljs 2) 19 | 20 | -------------------------------------------------------------------------------- /tests/linter/conditionals-clj-1/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/conditionals-clj-1/input.clj:15:9: Read error: Reader conditional requires an even number of forms 2 | tests/linter/conditionals-clj-1/input.clj:16:30: Parse warning: let form with empty bindings vector 3 | tests/linter/conditionals-clj-1/input.clj:17:12: Read error: Spliced form in reader conditional must be Seqable, got Int 4 | tests/linter/conditionals-clj-1/input.clj:18:14: Read error: Invalid number: 234ewr 5 | -------------------------------------------------------------------------------- /tests/linter/conditionals-clj-2/input.clj: -------------------------------------------------------------------------------- 1 | #?@(:cljs 3 :clj (let [_ 1])) 2 | -------------------------------------------------------------------------------- /tests/linter/conditionals-clj-2/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/conditionals-clj-2/input.clj:1:29: Read error: Reader conditional splicing not allowed at the top level. 2 | -------------------------------------------------------------------------------- /tests/linter/conditionals-clj-3/input.clj: -------------------------------------------------------------------------------- 1 | #?(:test 1) 2 | -------------------------------------------------------------------------------- /tests/linter/conditionals-clj-3/output.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/candid82/joker/1eed1c02c38ef1373640c42af1423808826820cc/tests/linter/conditionals-clj-3/output.txt -------------------------------------------------------------------------------- /tests/linter/conditionals-cljs/input.cljs: -------------------------------------------------------------------------------- 1 | ;; Should PASS 2 | 3 | #?(:clj 1) 4 | (def regexp #?(:clj re-pattern :cljs js/XRegExp)) 5 | #?(:clj (let [a 1]) :cljs 3) 6 | 7 | 8 | ;; Should FAIL 9 | 10 | #?(:cljs (let [] 1) :default (let [] 1)) 11 | #?@(:cljs 3) 12 | #?(:clj 234ewr :cljs 2) 13 | 14 | -------------------------------------------------------------------------------- /tests/linter/conditionals-cljs/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/conditionals-cljs/input.cljs:10:10: Parse warning: let form with empty bindings vector 2 | tests/linter/conditionals-cljs/input.cljs:11:12: Read error: Spliced form in reader conditional must be Seqable, got Int 3 | tests/linter/conditionals-cljs/input.cljs:12:14: Read error: Invalid number: 234ewr 4 | -------------------------------------------------------------------------------- /tests/linter/condp/input.clj: -------------------------------------------------------------------------------- 1 | (condp = 1) 2 | (condp = 1 2) 3 | 4 | (condp = 1 2 3) 5 | -------------------------------------------------------------------------------- /tests/linter/condp/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/condp/input.clj:1:1: Parse error: condp with no clauses 2 | :0:0: Parse warning: unused binding: pred__1 3 | tests/linter/condp/input.clj:2:1: Parse warning: condp with default expression only 4 | :0:0: Parse warning: unused binding: expr__4 5 | :0:0: Parse warning: unused binding: pred__3 6 | -------------------------------------------------------------------------------- /tests/linter/constr-call-cljs/input.cljs: -------------------------------------------------------------------------------- 1 | (def ^:private stream (js/require "stream")) 2 | (def ^:private ReadableStream (.-ReadableStream stream)) 3 | (ReadableStream. #js {:read (fn [_] ())}) 4 | -------------------------------------------------------------------------------- /tests/linter/constr-call-cljs/output.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/candid82/joker/1eed1c02c38ef1373640c42af1423808826820cc/tests/linter/constr-call-cljs/output.txt -------------------------------------------------------------------------------- /tests/linter/def/input.clj: -------------------------------------------------------------------------------- 1 | (def String "safsa") 2 | (def Comparable "asdf") 3 | -------------------------------------------------------------------------------- /tests/linter/def/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/def/input.clj:1:6: Parse warning: Expecting var, but String is a type 2 | tests/linter/def/input.clj:2:6: Parse warning: Expecting var, but Comparable is a type 3 | -------------------------------------------------------------------------------- /tests/linter/defmethod/input.clj: -------------------------------------------------------------------------------- 1 | (defmulti m1) 2 | 3 | ;; Should FAIL 4 | (defmethod m1 :v 5 | [_a] 6 | b) 7 | 8 | (defmethod m2 :v [] nil) 9 | 10 | (defmethod m3) 11 | -------------------------------------------------------------------------------- /tests/linter/defmethod/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/defmethod/input.clj:6:3: Parse error: Unable to resolve symbol: b 2 | tests/linter/defmethod/input.clj:8:12: Parse error: Unable to resolve symbol: m2 3 | tests/linter/defmethod/input.clj:10:1: Eval error: Wrong number of args (1) passed to core/defmethod; expects at least 3 4 | -------------------------------------------------------------------------------- /tests/linter/defn-0/input.clj: -------------------------------------------------------------------------------- 1 | (defn f 2) 2 | -------------------------------------------------------------------------------- /tests/linter/defn-0/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/defn-0/input.clj:1:1: Exception: Parameter declaration "2" must be a vector 2 | -------------------------------------------------------------------------------- /tests/linter/defn-1/input.clj: -------------------------------------------------------------------------------- 1 | (defn f "sdfsf") 2 | -------------------------------------------------------------------------------- /tests/linter/defn-1/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/defn-1/input.clj:1:1: Exception: Parameter declaration missing 2 | -------------------------------------------------------------------------------- /tests/linter/defn-2/input.clj: -------------------------------------------------------------------------------- 1 | (defn f ([]) 1) 2 | -------------------------------------------------------------------------------- /tests/linter/defn-2/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/defn-2/input.clj:1:1: Exception: Invalid signature: "1" must be a list 2 | -------------------------------------------------------------------------------- /tests/linter/defn-3/input.clj: -------------------------------------------------------------------------------- 1 | (defn f ([]) (1)) 2 | -------------------------------------------------------------------------------- /tests/linter/defn-3/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/defn-3/input.clj:1:1: Exception: Parameter declaration "1" must be a vector 2 | -------------------------------------------------------------------------------- /tests/linter/defprotocol/.joker: -------------------------------------------------------------------------------- 1 | {:rules {:unused-fn-parameters true}} 2 | -------------------------------------------------------------------------------- /tests/linter/defprotocol/input.clj: -------------------------------------------------------------------------------- 1 | (defprotocol P 2 | "P docstring" 3 | (m1 [a] [a b] 4 | "m1 docstring") 5 | 6 | (m2 [a]) 7 | 8 | (m3) 9 | 10 | (m4 []) 11 | 12 | (m1 [a])) 13 | -------------------------------------------------------------------------------- /tests/linter/defprotocol/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/defprotocol/input.clj:10:4: Exception: Definition of function m4 in protocol P must take at least one arg. 2 | tests/linter/defprotocol/input.clj:12:4: Exception: Function m1 in protocol P was redefined. Specify all arities in single definition. 3 | tests/linter/defprotocol/input.clj:8:4: Exception: Parameter declaration missing 4 | -------------------------------------------------------------------------------- /tests/linter/defrecord/input.clj: -------------------------------------------------------------------------------- 1 | (ns test.test 2 | (:require [test.prococol1 :refer [TestProtocol1]])) 3 | 4 | (defprotocol TestProtocol) 5 | 6 | (defn test-method 7 | [] nil) 8 | 9 | (defrecord TestRecord [a b] 10 | TestProtocol 11 | (test-method [x y] 12 | (println x) 13 | (println x y a b c) 14 | (println a b)) 15 | 16 | TestProtocol1 17 | (test-method1 [x y] 18 | (println x) 19 | (println x y a b c d) 20 | (println a b)) 21 | 22 | TestProtocol2 23 | (test-method2 [x _y] 24 | (println x))) 25 | 26 | (instance? TestRecord 1) 27 | (map->TestRecord 1) 28 | (->TestRecord 1 2) 29 | 30 | (map->TestRecord 1 2) 31 | (->TestRecord 1) 32 | 33 | (defrecord ^:private TestRecord1 []) 34 | (deftype TestType []) 35 | 36 | (->TestType) 37 | -------------------------------------------------------------------------------- /tests/linter/defrecord/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/defrecord/input.clj:13:22: Parse error: Unable to resolve symbol: c 2 | tests/linter/defrecord/input.clj:19:24: Parse error: Unable to resolve symbol: d 3 | tests/linter/defrecord/input.clj:22:3: Parse error: Unable to resolve symbol: TestProtocol2 4 | tests/linter/defrecord/input.clj:30:1: Parse warning: Wrong number of args (2) passed to test.test/map->TestRecord 5 | tests/linter/defrecord/input.clj:31:1: Parse warning: Wrong number of args (1) passed to test.test/->TestRecord 6 | -------------------------------------------------------------------------------- /tests/linter/deftest/input.clj: -------------------------------------------------------------------------------- 1 | (ns my 2 | (:require [clojure.test :refer [deftest]])) 3 | 4 | (deftest my-test 5 | (foo/bar 1)) 6 | -------------------------------------------------------------------------------- /tests/linter/deftest/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/deftest/input.clj:5:4: Parse error: Unable to resolve symbol: foo/bar 2 | -------------------------------------------------------------------------------- /tests/linter/do/input.clj: -------------------------------------------------------------------------------- 1 | (do) 2 | 3 | (do 1) 4 | 5 | (do 1 2) 6 | -------------------------------------------------------------------------------- /tests/linter/do/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/do/input.clj:1:1: Parse warning: do form with empty body 2 | tests/linter/do/input.clj:3:1: Parse warning: redundant do form 3 | -------------------------------------------------------------------------------- /tests/linter/doseq/input.clj: -------------------------------------------------------------------------------- 1 | ;; Should PASS 2 | (doseq [_ (range 10)] 1) 3 | 4 | ;; Should FAIL 5 | (doseq [_ (range 10)]) 6 | -------------------------------------------------------------------------------- /tests/linter/doseq/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/doseq/input.clj:5:8: Parse warning: doseq with empty body 2 | -------------------------------------------------------------------------------- /tests/linter/duplicate-def/input.clj: -------------------------------------------------------------------------------- 1 | (def b) 2 | (def b 1) 3 | 4 | (declare f) 5 | (defn f [] nil) 6 | 7 | (def a 1) 8 | (def a 2) 9 | 10 | (defn f1 [] nil) 11 | (defn f1 [_a] nil) 12 | -------------------------------------------------------------------------------- /tests/linter/duplicate-def/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/duplicate-def/input.clj:8:6: Parse warning: Duplicate def of #'user/a 2 | tests/linter/duplicate-def/input.clj:11:7: Parse warning: Duplicate def of #'user/f1 3 | -------------------------------------------------------------------------------- /tests/linter/exclude-list/input.clj: -------------------------------------------------------------------------------- 1 | (ns foo 2 | (:refer-clojure :exclude [list])) 3 | 4 | (defmacro bar [baz] 5 | `(identity ~baz)) 6 | -------------------------------------------------------------------------------- /tests/linter/exclude-list/output.txt: -------------------------------------------------------------------------------- 1 | :0:0: Parse error: Unable to resolve symbol: list 2 | -------------------------------------------------------------------------------- /tests/linter/extend-protocol/input.clj: -------------------------------------------------------------------------------- 1 | (defprotocol P) 2 | 3 | (extend-protocol P 4 | Integer 5 | (m1 [_a] nil) 6 | (m2)) 7 | -------------------------------------------------------------------------------- /tests/linter/extend-protocol/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/extend-protocol/input.clj:6:4: Exception: Parameter declaration missing 2 | -------------------------------------------------------------------------------- /tests/linter/extend-type-cljs/input.cljs: -------------------------------------------------------------------------------- 1 | (defprotocol P 2 | (m [_this _a])) 3 | 4 | (extend-type object 5 | P 6 | (m [_this a] 7 | (pr-str a)) 8 | (m1 )) 9 | -------------------------------------------------------------------------------- /tests/linter/extend-type-cljs/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/extend-type-cljs/input.cljs:8:4: Exception: Parameter declaration missing 2 | -------------------------------------------------------------------------------- /tests/linter/extend-type/input.clj: -------------------------------------------------------------------------------- 1 | (defprotocol P 2 | (m [_this _a])) 3 | 4 | (extend-type Object 5 | P 6 | (m [_this a] 7 | (pr-str a)) 8 | (m1 )) 9 | -------------------------------------------------------------------------------- /tests/linter/extend-type/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/extend-type/input.clj:8:4: Exception: Parameter declaration missing 2 | -------------------------------------------------------------------------------- /tests/linter/fn-0/input.clj: -------------------------------------------------------------------------------- 1 | (fn 2) 2 | -------------------------------------------------------------------------------- /tests/linter/fn-0/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/fn-0/input.clj:1:1: Exception: Parameter declaration "2" must be a vector 2 | -------------------------------------------------------------------------------- /tests/linter/fn-1/input.clj: -------------------------------------------------------------------------------- 1 | (fn "sdfsf") 2 | -------------------------------------------------------------------------------- /tests/linter/fn-1/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/fn-1/input.clj:1:1: Exception: Parameter declaration "sdfsf" must be a vector 2 | -------------------------------------------------------------------------------- /tests/linter/fn-2/input.clj: -------------------------------------------------------------------------------- 1 | (fn ([]) 1) 2 | -------------------------------------------------------------------------------- /tests/linter/fn-2/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/fn-2/input.clj:1:1: Exception: Invalid signature: "1" must be a list 2 | -------------------------------------------------------------------------------- /tests/linter/fn-3/input.clj: -------------------------------------------------------------------------------- 1 | (fn f ([]) (1)) 2 | -------------------------------------------------------------------------------- /tests/linter/fn-3/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/fn-3/input.clj:1:1: Exception: Parameter declaration "1" must be a vector 2 | -------------------------------------------------------------------------------- /tests/linter/fn-unused-parameters/.joker: -------------------------------------------------------------------------------- 1 | {:rules {:unused-fn-parameters true}} 2 | -------------------------------------------------------------------------------- /tests/linter/fn-unused-parameters/input.clj: -------------------------------------------------------------------------------- 1 | (defn f [_ _a b] 1) 2 | -------------------------------------------------------------------------------- /tests/linter/fn-unused-parameters/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/fn-unused-parameters/input.clj:1:15: Parse warning: unused parameter: b 2 | -------------------------------------------------------------------------------- /tests/linter/fn-with-empty-body/input.clj: -------------------------------------------------------------------------------- 1 | (defn f []) 2 | -------------------------------------------------------------------------------- /tests/linter/fn-with-empty-body/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/fn-with-empty-body/input.clj:1:1: Parse warning: fn form with empty body 2 | -------------------------------------------------------------------------------- /tests/linter/for-1/input.clj: -------------------------------------------------------------------------------- 1 | (for [a [1 2] :le [a 1]] a) 2 | -------------------------------------------------------------------------------- /tests/linter/for-1/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/for-1/input.clj:1:15: Exception: Invalid keyword :le in "for" form 2 | -------------------------------------------------------------------------------- /tests/linter/for-2/input.clj: -------------------------------------------------------------------------------- 1 | (for 1 2) 2 | -------------------------------------------------------------------------------- /tests/linter/for-2/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/for-2/input.clj:1:1: Exception: for requires a vector for its binding 2 | -------------------------------------------------------------------------------- /tests/linter/for-3/input.clj: -------------------------------------------------------------------------------- 1 | (for [1] 2) 2 | -------------------------------------------------------------------------------- /tests/linter/for-3/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/for-3/input.clj:1:1: Exception: for requires an even number of forms in binding vector 2 | -------------------------------------------------------------------------------- /tests/linter/function-call-cljs/input.cljs: -------------------------------------------------------------------------------- 1 | ;; Should PASS 2 | 3 | (array 1 2) 4 | -------------------------------------------------------------------------------- /tests/linter/function-call-cljs/output.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/candid82/joker/1eed1c02c38ef1373640c42af1423808826820cc/tests/linter/function-call-cljs/output.txt -------------------------------------------------------------------------------- /tests/linter/function-call/input.clj: -------------------------------------------------------------------------------- 1 | (defn f1 [] nil) 2 | (defn f2 [_x] nil) 3 | (def v1 1) 4 | (def k :t) 5 | (def m {}) 6 | (def s #{}) 7 | ;; Should PASS 8 | 9 | (spit "test" "test" :append true) 10 | (map identity) 11 | (f1) 12 | (f2 1) 13 | (k {}) 14 | ((fn [] nil)) 15 | (#{} 1) 16 | (s 1) 17 | ([] 1) 18 | ([]) ;; we don't check vector call arity as it would cause false positives in some macros (like alt!!) 19 | ({1 2} 1) 20 | ({1 2} 1 2) 21 | (m 1) 22 | (m 1 2) 23 | (:t 1 2) 24 | (:t 1) 25 | 26 | ;; Should FAIL 27 | (map) 28 | (*assert*) 29 | (f1 1) 30 | (f2) 31 | (v1) 32 | (1) 33 | ("") 34 | ((fn [] nil) 1) 35 | (:t) 36 | (:t 1 2 3) 37 | ({}) 38 | ({} 1 2 3) 39 | (m) 40 | (m 1 2 3) 41 | (#{}) 42 | (#{} 1 2) 43 | (s) 44 | (s 1 2) 45 | (= 1) 46 | (not= 1) 47 | (< 1) 48 | (> 1) 49 | (<= 1) 50 | (>= 1) 51 | (== 1) 52 | -------------------------------------------------------------------------------- /tests/linter/function-call/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/function-call/input.clj:27:1: Parse warning: Wrong number of args (0) passed to core/map 2 | tests/linter/function-call/input.clj:28:1: Parse warning: core/*assert* is not a function 3 | tests/linter/function-call/input.clj:29:1: Parse warning: Wrong number of args (1) passed to user/f1 4 | tests/linter/function-call/input.clj:30:1: Parse warning: Wrong number of args (0) passed to user/f2 5 | tests/linter/function-call/input.clj:31:1: Parse warning: user/v1 is not a function 6 | tests/linter/function-call/input.clj:32:1: Parse warning: 1 is not a function 7 | tests/linter/function-call/input.clj:33:1: Parse warning: is not a function 8 | tests/linter/function-call/input.clj:34:1: Parse warning: Wrong number of args (1) passed to fn 9 | tests/linter/function-call/input.clj:35:1: Parse warning: Wrong number of args (0) passed to :t 10 | tests/linter/function-call/input.clj:36:1: Parse warning: Wrong number of args (3) passed to :t 11 | tests/linter/function-call/input.clj:37:1: Parse warning: Wrong number of args (0) passed to a map 12 | tests/linter/function-call/input.clj:38:1: Parse warning: Wrong number of args (3) passed to a map 13 | tests/linter/function-call/input.clj:39:1: Parse warning: Wrong number of args (0) passed to a map 14 | tests/linter/function-call/input.clj:40:1: Parse warning: Wrong number of args (3) passed to a map 15 | tests/linter/function-call/input.clj:41:1: Parse warning: Wrong number of args (0) passed to a set 16 | tests/linter/function-call/input.clj:42:1: Parse warning: Wrong number of args (2) passed to a set 17 | tests/linter/function-call/input.clj:43:1: Parse warning: Wrong number of args (0) passed to a set 18 | tests/linter/function-call/input.clj:44:1: Parse warning: Wrong number of args (2) passed to a set 19 | tests/linter/function-call/input.clj:45:1: Parse warning: Wrong number of args (1) passed to core/= 20 | tests/linter/function-call/input.clj:46:1: Parse warning: Wrong number of args (1) passed to core/not= 21 | tests/linter/function-call/input.clj:47:1: Parse warning: Wrong number of args (1) passed to core/< 22 | tests/linter/function-call/input.clj:48:1: Parse warning: Wrong number of args (1) passed to core/> 23 | tests/linter/function-call/input.clj:49:1: Parse warning: Wrong number of args (1) passed to core/<= 24 | tests/linter/function-call/input.clj:50:1: Parse warning: Wrong number of args (1) passed to core/>= 25 | tests/linter/function-call/input.clj:51:1: Parse warning: Wrong number of args (1) passed to core/== 26 | -------------------------------------------------------------------------------- /tests/linter/if-let-1/input.clj: -------------------------------------------------------------------------------- 1 | (if-let 1 2) 2 | -------------------------------------------------------------------------------- /tests/linter/if-let-1/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/if-let-1/input.clj:1:1: Exception: if-let requires a vector for its binding 2 | -------------------------------------------------------------------------------- /tests/linter/if-let-2/input.clj: -------------------------------------------------------------------------------- 1 | (if-let [1] 2) 2 | -------------------------------------------------------------------------------- /tests/linter/if-let-2/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/if-let-2/input.clj:1:1: Exception: if-let requires exactly 2 forms in binding vector 2 | -------------------------------------------------------------------------------- /tests/linter/if-let-3/input.clj: -------------------------------------------------------------------------------- 1 | (if-let [a 1] 1 2 3) 2 | -------------------------------------------------------------------------------- /tests/linter/if-let-3/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/if-let-3/input.clj:1:1: Exception: if-let requires 1 or 2 forms after binding vector 2 | -------------------------------------------------------------------------------- /tests/linter/if/.joker: -------------------------------------------------------------------------------- 1 | {:rules {:if-without-else true}} 2 | -------------------------------------------------------------------------------- /tests/linter/if/input.clj: -------------------------------------------------------------------------------- 1 | ;; Should PASS 2 | 3 | (if 1 2 3) 4 | (if-let [a 1] a 2) 5 | 6 | ;; Should FAIL 7 | 8 | (if 1 2) 9 | (if-let [a 1] a) 10 | -------------------------------------------------------------------------------- /tests/linter/if/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/if/input.clj:8:1: Parse warning: missing else branch 2 | tests/linter/if/input.clj:9:1: Parse warning: missing else branch 3 | -------------------------------------------------------------------------------- /tests/linter/inline-def/input.clj: -------------------------------------------------------------------------------- 1 | (let [a 1] 2 | (def t) 3 | a) 4 | -------------------------------------------------------------------------------- /tests/linter/inline-def/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/inline-def/input.clj:2:3: Parse warning: inline def 2 | -------------------------------------------------------------------------------- /tests/linter/learned-macros/.jokerd/linter.clj: -------------------------------------------------------------------------------- 1 | (ns rum.core) 2 | 3 | (defmacro defc [& params] 4 | (let [[name _ _ arg-vector & body] params] 5 | `(defn ~name ~arg-vector ~@body))) 6 | 7 | (ns rum.core1) 8 | 9 | (defmacro defc1 [& params] 10 | (let [[name _ _ arg-vector & body] params] 11 | `(defn ~name ~arg-vector ~@body))) 12 | 13 | (ns rum.core2) 14 | 15 | (defmacro defc2 [& params] 16 | (let [[name _ _ arg-vector & body] params] 17 | `(defn ~name ~arg-vector ~@body))) 18 | 19 | 20 | (in-ns 'user) 21 | -------------------------------------------------------------------------------- /tests/linter/learned-macros/input.clj: -------------------------------------------------------------------------------- 1 | (require '[rum.core :as rum]) 2 | (require '[rum.core1 :as rum1]) 3 | 4 | (rum/defc Foo < rum/static [{:keys [bar/foo]}] [:div (str foo)]) 5 | -------------------------------------------------------------------------------- /tests/linter/learned-macros/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/learned-macros/input.clj:2:12: Parse warning: unused namespace rum.core1 2 | -------------------------------------------------------------------------------- /tests/linter/let-1/input.clj: -------------------------------------------------------------------------------- 1 | (let ["sdf" 1]) 2 | -------------------------------------------------------------------------------- /tests/linter/let-1/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/let-1/input.clj:1:7: Exception: Unsupported binding form: sdf 2 | -------------------------------------------------------------------------------- /tests/linter/let/input.clj: -------------------------------------------------------------------------------- 1 | ;; Should PASS 2 | 3 | (require '[test.ns1 :as ns1]) 4 | 5 | (let [a 1] 6 | a) 7 | 8 | (let [{:keys [:t ::g]} {:t 1 :user/g 2}] [t g]) 9 | (let [{::ns1/keys [a]} {:test.ns1/a 1}] a) 10 | 11 | ;; Should FAIL 12 | (let [_ 1]) 13 | (let [] 1) 14 | (let [foo/bar 2] bar) 15 | (let [[a & more 1] []] 1) 16 | -------------------------------------------------------------------------------- /tests/linter/let/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/let/input.clj:12:1: Parse warning: let form with empty body 2 | tests/linter/let/input.clj:13:1: Parse warning: let form with empty bindings vector 3 | tests/linter/let/input.clj:14:7: Parse error: Can't let qualified name: foo/bar 4 | tests/linter/let/input.clj:15:6: Exception: Unsupported binding form, only :as can follow & parameter 5 | -------------------------------------------------------------------------------- /tests/linter/letfn/input.clj: -------------------------------------------------------------------------------- 1 | (letfn [(neven? [n] (if (zero? n) true (nodd? (dec n)))) 2 | (nodd? [n] (if (zero? n) false (neven? (dec n))))] 3 | (neven? 10)) 4 | 5 | 6 | (letfn) 7 | -------------------------------------------------------------------------------- /tests/linter/letfn/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/letfn/input.clj:6:1: Eval error: Wrong number of args (0) passed to core/letfn; expects at least 2 2 | -------------------------------------------------------------------------------- /tests/linter/linter-files/.joker: -------------------------------------------------------------------------------- 1 | {:ignored-unused-namespaces [com.rpl.specter]} 2 | -------------------------------------------------------------------------------- /tests/linter/linter-files/.jokerd/linter.clj: -------------------------------------------------------------------------------- 1 | (in-ns 'joker.core) 2 | (defn transform [apath transform-fn structure]) 3 | (def MAP-VALS) 4 | (def ALL) 5 | -------------------------------------------------------------------------------- /tests/linter/linter-files/input.clj: -------------------------------------------------------------------------------- 1 | (ns my.test (:require [com.rpl.specter :refer :all])) 2 | 3 | (transform [MAP-VALS ALL MAP-VALS even?] inc []) 4 | -------------------------------------------------------------------------------- /tests/linter/linter-files/output.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/candid82/joker/1eed1c02c38ef1373640c42af1423808826820cc/tests/linter/linter-files/output.txt -------------------------------------------------------------------------------- /tests/linter/macro-call-1/input.clj: -------------------------------------------------------------------------------- 1 | (deftype) 2 | -------------------------------------------------------------------------------- /tests/linter/macro-call-1/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/macro-call-1/input.clj:1:1: Eval error: Wrong number of args (0) passed to core/deftype; expects at least 3 2 | -------------------------------------------------------------------------------- /tests/linter/macro-call/input.clj: -------------------------------------------------------------------------------- 1 | (memfn) 2 | (..) 3 | (defstruct) 4 | (with-local-vars) 5 | (definline) 6 | (definterface) 7 | (proxy-super) 8 | (with-open) 9 | (deftype) 10 | (defrecord) 11 | -------------------------------------------------------------------------------- /tests/linter/macro-call/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/macro-call/input.clj:1:1: Parse warning: Wrong number of args (0) passed to core/memfn 2 | tests/linter/macro-call/input.clj:2:1: Parse warning: Wrong number of args (0) passed to core/.. 3 | tests/linter/macro-call/input.clj:3:1: Parse warning: Wrong number of args (0) passed to core/defstruct 4 | tests/linter/macro-call/input.clj:4:1: Parse warning: Wrong number of args (0) passed to core/with-local-vars 5 | tests/linter/macro-call/input.clj:5:1: Parse warning: Wrong number of args (0) passed to core/definline 6 | tests/linter/macro-call/input.clj:6:1: Parse warning: Wrong number of args (0) passed to core/definterface 7 | tests/linter/macro-call/input.clj:7:1: Parse warning: Wrong number of args (0) passed to core/proxy-super 8 | tests/linter/macro-call/input.clj:8:1: Parse warning: Wrong number of args (0) passed to core/with-open 9 | tests/linter/macro-call/input.clj:9:1: Eval error: Wrong number of args (0) passed to core/deftype; expects at least 3 10 | tests/linter/macro-call/input.clj:10:1: Eval error: Wrong number of args (0) passed to core/defrecord; expects at least 3 11 | -------------------------------------------------------------------------------- /tests/linter/merge/input.clj: -------------------------------------------------------------------------------- 1 | (ns test 2 | (:require [clojure.core.async :as async])) 3 | 4 | (async/merge []) 5 | -------------------------------------------------------------------------------- /tests/linter/merge/output.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/candid82/joker/1eed1c02c38ef1373640c42af1423808826820cc/tests/linter/merge/output.txt -------------------------------------------------------------------------------- /tests/linter/namespaced-maps/input.clj: -------------------------------------------------------------------------------- 1 | (require 'joker.string) 2 | (alias 's 'joker.string) 3 | ;; Should PASS 4 | #:t{:g 1} 5 | #::{:g 1} 6 | #:t{:_/g 1} 7 | #:t{:h/g 1} 8 | #::s{:g 1} 9 | 10 | ;; Should FAIL 11 | #::{g 1} 12 | -------------------------------------------------------------------------------- /tests/linter/namespaced-maps/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/namespaced-maps/input.clj:11:5: Parse error: Unable to resolve symbol: user/g 2 | -------------------------------------------------------------------------------- /tests/linter/ns-1/input.clj: -------------------------------------------------------------------------------- 1 | (ns test 2 | (:require [test.n1 :as n1])) 3 | 4 | (n1/t) 5 | -------------------------------------------------------------------------------- /tests/linter/ns-1/output.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/candid82/joker/1eed1c02c38ef1373640c42af1423808826820cc/tests/linter/ns-1/output.txt -------------------------------------------------------------------------------- /tests/linter/ns-10/input.clj: -------------------------------------------------------------------------------- 1 | (ns my-ns 2 | (:require [clojure.java.io :as io] 3 | [clojure.string :as str] 4 | [clojure.test :as t])) 5 | -------------------------------------------------------------------------------- /tests/linter/ns-10/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/ns-10/input.clj:2:14: Parse warning: unused namespace clojure.java.io 2 | tests/linter/ns-10/input.clj:3:14: Parse warning: unused namespace clojure.string 3 | tests/linter/ns-10/input.clj:4:14: Parse warning: unused namespace clojure.test 4 | -------------------------------------------------------------------------------- /tests/linter/ns-2/input.clj: -------------------------------------------------------------------------------- 1 | (ns test 2 | (:require [test.n1 :as n1] 3 | [test.n2 :as n1])) 4 | 5 | (n1/f) 6 | (f) 7 | -------------------------------------------------------------------------------- /tests/linter/ns-2/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/ns-2/input.clj:3:26: Parse error: Alias n1 already exists in namespace test, aliasing test.n1 2 | tests/linter/ns-2/input.clj:6:2: Parse error: Unable to resolve symbol: f 3 | tests/linter/ns-2/input.clj:3:14: Parse warning: unused namespace test.n2 4 | -------------------------------------------------------------------------------- /tests/linter/ns-3/input.clj: -------------------------------------------------------------------------------- 1 | (ns test 2 | (:require [test.n1 :as n1 3 | test.n2 :as n2])) 4 | 5 | -------------------------------------------------------------------------------- /tests/linter/ns-3/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/ns-3/input.clj:1:1: Eval error: No value supplied for key true 2 | -------------------------------------------------------------------------------- /tests/linter/ns-4/input.clj: -------------------------------------------------------------------------------- 1 | (ns test 2 | (:require [tt :refer g])) 3 | -------------------------------------------------------------------------------- /tests/linter/ns-4/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/ns-4/input.clj:2:14: Exception: :only/:refer value must be a sequential collection of symbols 2 | tests/linter/ns-4/input.clj:2:14: Parse warning: unused namespace tt 3 | -------------------------------------------------------------------------------- /tests/linter/ns-5/input.clj: -------------------------------------------------------------------------------- 1 | (ns test 2 | (:require [test.ns1] 3 | [test.ns1] 4 | [left-pad :as lp])) 5 | 6 | (test.ns1/f) 7 | (left-pad) 8 | (lp) 9 | -------------------------------------------------------------------------------- /tests/linter/ns-5/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/ns-5/input.clj:3:14: Parse warning: duplicate require for test.ns1 2 | tests/linter/ns-5/input.clj:7:2: Parse error: Unable to resolve symbol: left-pad 3 | tests/linter/ns-5/input.clj:8:2: Parse error: Unable to resolve symbol: lp 4 | tests/linter/ns-5/input.clj:4:14: Parse warning: unused namespace left-pad 5 | -------------------------------------------------------------------------------- /tests/linter/ns-6/input.clj: -------------------------------------------------------------------------------- 1 | (ns test 2 | (:require [other.ns :refer [f]])) 3 | 4 | (defn map [] nil) 5 | (def f (fn [])) 6 | -------------------------------------------------------------------------------- /tests/linter/ns-6/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/ns-6/input.clj:4:7: Parse warning: WARNING: map already refers to: #'joker.core/map in namespace test, being replaced by: #'test/map 2 | 3 | tests/linter/ns-6/input.clj:5:6: Eval error: WARNING: f already refers to: #'other.ns/f in namespace test 4 | tests/linter/ns-6/input.clj:2:14: Parse warning: unused namespace other.ns 5 | -------------------------------------------------------------------------------- /tests/linter/ns-7/input.clj: -------------------------------------------------------------------------------- 1 | (create-ns 'test.test) 2 | (require 'joker.os) 3 | (alias 'os 'joker.os) 4 | (alias 't 'test.test) 5 | 6 | (os/sh "ls") 7 | (test.test/foo) 8 | (t/bar) 9 | -------------------------------------------------------------------------------- /tests/linter/ns-7/output.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/candid82/joker/1eed1c02c38ef1373640c42af1423808826820cc/tests/linter/ns-7/output.txt -------------------------------------------------------------------------------- /tests/linter/ns-8/input.clj: -------------------------------------------------------------------------------- 1 | (ns example.macros 2 | (:require [ttt :default RaisedButton] :verbose)) 3 | 4 | RaisedButton/t 5 | -------------------------------------------------------------------------------- /tests/linter/ns-8/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/ns-8/input.clj:2:18: Exception: Unsupported option(s) supplied: :default 2 | tests/linter/ns-8/input.clj:4:1: Parse error: Unable to resolve symbol: RaisedButton/t 3 | -------------------------------------------------------------------------------- /tests/linter/ns-9/input.clj: -------------------------------------------------------------------------------- 1 | (ns test 2 | (:require [clojure.string :refer [starts-with?] :rename {starts-with? begins-with?}])) 3 | 4 | (begins-with? "test" "t") 5 | (starts-with? "test" "t") 6 | -------------------------------------------------------------------------------- /tests/linter/ns-9/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/ns-9/input.clj:5:2: Parse error: Unable to resolve symbol: starts-with? 2 | -------------------------------------------------------------------------------- /tests/linter/ns-cljs-1/input.cljs: -------------------------------------------------------------------------------- 1 | (ns hello.world 2 | (:require [om.next :as om :refer-macros [defui]] 3 | [om.dom :as dom])) 4 | 5 | (defui HelloWorld 6 | Object 7 | (render [this] 8 | (dom/div nil "Hello, World!"))) 9 | -------------------------------------------------------------------------------- /tests/linter/ns-cljs-1/output.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/candid82/joker/1eed1c02c38ef1373640c42af1423808826820cc/tests/linter/ns-cljs-1/output.txt -------------------------------------------------------------------------------- /tests/linter/ns-cljs-2/.joker: -------------------------------------------------------------------------------- 1 | {:known-namespaces [clojure.spec.gen.test]} 2 | -------------------------------------------------------------------------------- /tests/linter/ns-cljs-2/input.cljs: -------------------------------------------------------------------------------- 1 | (alias 'stc 'clojure.spec.gen.test) 2 | (alias 'stc1 'clojure.spec.gen.test1) 3 | 4 | (stc/t) 5 | (stc1/t) 6 | -------------------------------------------------------------------------------- /tests/linter/ns-cljs-2/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/ns-cljs-2/input.cljs:2:15: Parse warning: No namespace: clojure.spec.gen.test1 found 2 | -------------------------------------------------------------------------------- /tests/linter/ns-cljs-3/input.cljs: -------------------------------------------------------------------------------- 1 | (ns example.macros 2 | (:require-macros [cljs.core.async.macros :as async]) 3 | (:require [cljs.core.async :as async] 4 | [left-pad :as lp] 5 | ["material-ui/RaisedButton" :default RaisedButton])) 6 | 7 | (async/t) 8 | (left-pad) 9 | (lp 1) 10 | RaisedButton 11 | (t) 12 | -------------------------------------------------------------------------------- /tests/linter/ns-cljs-3/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/ns-cljs-3/input.cljs:3:34: Parse error: Alias async already exists in namespace example.macros, aliasing cljs.core.async.macros 2 | tests/linter/ns-cljs-3/input.cljs:11:2: Parse error: Unable to resolve symbol: t 3 | tests/linter/ns-cljs-3/input.cljs:3:14: Parse warning: unused namespace cljs.core.async 4 | -------------------------------------------------------------------------------- /tests/linter/ns-cljs-4/input.cljs: -------------------------------------------------------------------------------- 1 | (ns test 2 | (:require ["m1" :as ns1] 3 | ["m2/t"] 4 | ["m3/t" :as ns3] 5 | [ns1] 6 | [m1])) 7 | 8 | (ns3/f) 9 | -------------------------------------------------------------------------------- /tests/linter/ns-cljs-4/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/ns-cljs-4/input.cljs:6:14: Parse warning: duplicate require for m1 2 | tests/linter/ns-cljs-4/input.cljs:2:14: Parse warning: unused namespace m1 3 | tests/linter/ns-cljs-4/input.cljs:3:14: Parse warning: unused namespace m2_t 4 | tests/linter/ns-cljs-4/input.cljs:5:14: Parse warning: unused namespace ns1 5 | -------------------------------------------------------------------------------- /tests/linter/ns-cljs-5/input.cljs: -------------------------------------------------------------------------------- 1 | (ns bar.core 2 | (:require [foo.core :as foo :include-macros true :exclude [t] :only g :refer [k] :rename {} :refer-macros []])) 3 | 4 | (foo/bar) 5 | -------------------------------------------------------------------------------- /tests/linter/ns-cljs-5/output.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/candid82/joker/1eed1c02c38ef1373640c42af1423808826820cc/tests/linter/ns-cljs-5/output.txt -------------------------------------------------------------------------------- /tests/linter/proxy/input.clj: -------------------------------------------------------------------------------- 1 | (import org.apache.kafka.clients.producer.KafkaProducer) 2 | 3 | (proxy [KafkaProducer] [] 4 | (send [producer-record] :whatever)) 5 | 6 | (proxy [String] [] 7 | (toString [] 8 | (println this))) 9 | 10 | 11 | -------------------------------------------------------------------------------- /tests/linter/proxy/output.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/candid82/joker/1eed1c02c38ef1373640c42af1423808826820cc/tests/linter/proxy/output.txt -------------------------------------------------------------------------------- /tests/linter/reader/input.clj: -------------------------------------------------------------------------------- 1 | "\033[31mFOO\033[0m" 2 | (= {} (:)) 3 | -------------------------------------------------------------------------------- /tests/linter/reader/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/reader/input.clj:2:8: Read error: Invalid keyword: : 2 | -------------------------------------------------------------------------------- /tests/linter/recur/input.clj: -------------------------------------------------------------------------------- 1 | ;; Should PASS 2 | (try 1 (catch Error e (loop [a 1] (recur (inc a))))) 3 | 4 | ;; Should FAIL 5 | (loop [a 1] 6 | (recur (inc a) nil)) 7 | -------------------------------------------------------------------------------- /tests/linter/recur/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/recur/input.clj:6:3: Parse error: Mismatched argument count to recur, expected: 1 args, got: 2 2 | -------------------------------------------------------------------------------- /tests/linter/redundant-do/input.clj: -------------------------------------------------------------------------------- 1 | (do 2 | 3 3 | (do 4 | 1 5 | 2)) 6 | 7 | (let [a 1] 8 | (do 9 | 1 10 | a)) 11 | 12 | (defn f 13 | [] 14 | (do 1 2)) 15 | 16 | (if-let [a 1] 17 | a 18 | 2) 19 | 20 | (if-let [a 1] 21 | (do 3 a) 22 | 2) 23 | 24 | (when-let [a 1] 25 | (do 1 a)) 26 | 27 | #(do (println 1) (println 2)) 28 | 29 | #(do (println 1)) 30 | -------------------------------------------------------------------------------- /tests/linter/redundant-do/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/redundant-do/input.clj:3:3: Parse warning: redundant do form 2 | tests/linter/redundant-do/input.clj:8:3: Parse warning: redundant do form 3 | tests/linter/redundant-do/input.clj:14:3: Parse warning: redundant do form 4 | tests/linter/redundant-do/input.clj:25:3: Parse warning: redundant do form 5 | tests/linter/redundant-do/input.clj:29:2: Parse warning: redundant do form 6 | -------------------------------------------------------------------------------- /tests/linter/regex/input.clj: -------------------------------------------------------------------------------- 1 | #"(paginate_by:\s*)\"(\w+)\"" 2 | -------------------------------------------------------------------------------- /tests/linter/regex/output.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/candid82/joker/1eed1c02c38ef1373640c42af1423808826820cc/tests/linter/regex/output.txt -------------------------------------------------------------------------------- /tests/linter/reify/input.clj: -------------------------------------------------------------------------------- 1 | (defprotocol P1) 2 | (defprotocol P2) 3 | 4 | (reify 5 | P1 6 | (m1 [_a] nil) 7 | (m2 [_b] nil) 8 | 9 | P2 10 | (m3 [_c] nil) 11 | (m4)) 12 | -------------------------------------------------------------------------------- /tests/linter/reify/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/reify/input.clj:11:4: Exception: Parameter declaration "" must be a vector 2 | -------------------------------------------------------------------------------- /tests/linter/symbol-resolution-1/.joker: -------------------------------------------------------------------------------- 1 | {:known-macros [[test.ns/test-macro [foo]]]} 2 | -------------------------------------------------------------------------------- /tests/linter/symbol-resolution-1/input.clj: -------------------------------------------------------------------------------- 1 | (require '[test.ns :refer [test-macro]]) 2 | (test-macro (do foo 1)) 3 | -------------------------------------------------------------------------------- /tests/linter/symbol-resolution-1/output.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/candid82/joker/1eed1c02c38ef1373640c42af1423808826820cc/tests/linter/symbol-resolution-1/output.txt -------------------------------------------------------------------------------- /tests/linter/symbol-resolution-2/input.clj: -------------------------------------------------------------------------------- 1 | (ns my) 2 | 3 | (defn my-test 4 | ([] 5 | (my-test bar))) 6 | -------------------------------------------------------------------------------- /tests/linter/symbol-resolution-2/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/symbol-resolution-2/input.clj:5:13: Parse error: Unable to resolve symbol: bar 2 | -------------------------------------------------------------------------------- /tests/linter/symbol-resolution-cljs/input.cljs: -------------------------------------------------------------------------------- 1 | (ns tests.symbols-lint 2 | (:require [cljs.test :refer [is]])) 3 | 4 | (is (thrown? c body)) 5 | -------------------------------------------------------------------------------- /tests/linter/symbol-resolution-cljs/output.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/candid82/joker/1eed1c02c38ef1373640c42af1423808826820cc/tests/linter/symbol-resolution-cljs/output.txt -------------------------------------------------------------------------------- /tests/linter/symbol-resolution/input.clj: -------------------------------------------------------------------------------- 1 | (ns tests.symbols-lint 2 | (:require [tests.ext1] 3 | [tests.ext2 :as ext2] 4 | [tests.ext3 :as ext3 :refer [e3]] 5 | [spec :as s] 6 | [clojure.test :refer [testing is]]) 7 | (:import [java.security Security])) 8 | 9 | ;; Should PASS 10 | 11 | (defmacro test-macro [_x] nil) 12 | 13 | (def f []) 14 | 15 | (f 1) 16 | (tests.ext1/f 1) 17 | (e3 1) 18 | (ext2/g 4) 19 | (defprotocol Person) 20 | (java.security.Security/tt) 21 | (Security/hh) 22 | (Integer/parseInt 10) 23 | (java.lang.Integer/parseInt 10) 24 | (instance? Integer 10) 25 | (instance? java.lang.Integer 10) 26 | (test-macro uuu) 27 | (defrecord u5 []) 28 | (clojure.test/deftest u7) 29 | #'s/v 30 | #'e3 31 | (case 1 r 2 1 3) 32 | uuu 33 | (is (thrown? c body)) 34 | 35 | ;; Should FAIL 36 | 37 | (s/def :t hh) 38 | (f jj/u1) 39 | (f u8) 40 | (f1 1) 41 | (tt/g 3) 42 | (test-macro (f u2)) 43 | (test-macro (load u3)) 44 | (test-macro (ext2/f u4)) 45 | (testing u9) 46 | (defmethod u6 1 [] nil) 47 | #'g 48 | #'ns/gg 49 | (joker.string/split "1" #"2") 50 | (joker.os/env) 51 | (joker.time/sleep 10) 52 | (joker.json/read-string "") 53 | (joker.base64/decode-string "") 54 | (pprint 1) 55 | (pr-err 1) 56 | (prn-err 1) 57 | (print-err 1) 58 | (println-err 1) 59 | (case 1 r 2 1 h) 60 | -------------------------------------------------------------------------------- /tests/linter/symbol-resolution/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/symbol-resolution/input.clj:28:1: Parse warning: fn form with empty body 2 | tests/linter/symbol-resolution/input.clj:37:11: Parse error: Unable to resolve symbol: hh 3 | tests/linter/symbol-resolution/input.clj:38:4: Parse error: Unable to resolve symbol: jj/u1 4 | tests/linter/symbol-resolution/input.clj:39:4: Parse error: Unable to resolve symbol: u8 5 | tests/linter/symbol-resolution/input.clj:40:2: Parse error: Unable to resolve symbol: f1 6 | tests/linter/symbol-resolution/input.clj:41:2: Parse error: Unable to resolve symbol: tt/g 7 | tests/linter/symbol-resolution/input.clj:42:16: Parse error: Unable to resolve symbol: u2 8 | tests/linter/symbol-resolution/input.clj:43:19: Parse error: Unable to resolve symbol: u3 9 | tests/linter/symbol-resolution/input.clj:44:21: Parse error: Unable to resolve symbol: u4 10 | tests/linter/symbol-resolution/input.clj:45:10: Parse error: Unable to resolve symbol: u9 11 | tests/linter/symbol-resolution/input.clj:46:12: Parse error: Unable to resolve symbol: u6 12 | tests/linter/symbol-resolution/input.clj:47:3: Parse error: Unable to resolve symbol: g 13 | tests/linter/symbol-resolution/input.clj:48:3: Parse error: Unable to resolve symbol: ns/gg 14 | tests/linter/symbol-resolution/input.clj:49:2: Parse error: Unable to resolve symbol: joker.string/split 15 | tests/linter/symbol-resolution/input.clj:50:2: Parse error: Unable to resolve symbol: joker.os/env 16 | tests/linter/symbol-resolution/input.clj:51:2: Parse error: Unable to resolve symbol: joker.time/sleep 17 | tests/linter/symbol-resolution/input.clj:52:2: Parse error: Unable to resolve symbol: joker.json/read-string 18 | tests/linter/symbol-resolution/input.clj:53:2: Parse error: Unable to resolve symbol: joker.base64/decode-string 19 | tests/linter/symbol-resolution/input.clj:54:2: Parse error: Unable to resolve symbol: pprint 20 | tests/linter/symbol-resolution/input.clj:55:2: Parse error: Unable to resolve symbol: pr-err 21 | tests/linter/symbol-resolution/input.clj:56:2: Parse error: Unable to resolve symbol: prn-err 22 | tests/linter/symbol-resolution/input.clj:57:2: Parse error: Unable to resolve symbol: print-err 23 | tests/linter/symbol-resolution/input.clj:58:2: Parse error: Unable to resolve symbol: println-err 24 | tests/linter/symbol-resolution/input.clj:59:15: Parse error: Unable to resolve symbol: h 25 | -------------------------------------------------------------------------------- /tests/linter/tagged-literals/.joker: -------------------------------------------------------------------------------- 1 | {:known-tags [db/fn]} 2 | -------------------------------------------------------------------------------- /tests/linter/tagged-literals/input.clj: -------------------------------------------------------------------------------- 1 | ;; Should PASS 2 | 3 | [{:db/fn #db/fn {:lang "java" 4 | :params [db e doc] 5 | :code "return list(list(\":db/add\", e, \":db/doc\", doc))"}}] 6 | 7 | #inst 1 8 | #uuid 2 9 | 10 | ;; Should FAIL 11 | 12 | #t 4 13 | #g [a] 14 | -------------------------------------------------------------------------------- /tests/linter/tagged-literals/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/tagged-literals/input.clj:12:2: Read warning: No reader function for tag t 2 | tests/linter/tagged-literals/input.clj:13:2: Read warning: No reader function for tag g 3 | tests/linter/tagged-literals/input.clj:13:5: Parse error: Unable to resolve symbol: a 4 | -------------------------------------------------------------------------------- /tests/linter/threading-macros-1/.joker: -------------------------------------------------------------------------------- 1 | {:rules {:no-forms-threading false}} 2 | -------------------------------------------------------------------------------- /tests/linter/threading-macros-1/input.clj: -------------------------------------------------------------------------------- 1 | (-> 1) 2 | (->> 1) 3 | (cond-> 1) 4 | (cond->> 1) 5 | (as-> 0 n) 6 | (some-> 1) 7 | (some->> 1) 8 | -------------------------------------------------------------------------------- /tests/linter/threading-macros-1/output.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/candid82/joker/1eed1c02c38ef1373640c42af1423808826820cc/tests/linter/threading-macros-1/output.txt -------------------------------------------------------------------------------- /tests/linter/threading-macros/input.clj: -------------------------------------------------------------------------------- 1 | (-> 1) 2 | (->> 1) 3 | (cond-> 1) 4 | (cond->> 1) 5 | (as-> 0 n) 6 | (some-> 1) 7 | (some->> 1) 8 | (cond->> [1 2 3] true (filter odd?) false) 9 | (cond-> odd? true (filter [1 2 3]) false) 10 | -------------------------------------------------------------------------------- /tests/linter/threading-macros/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/threading-macros/input.clj:1:1: Parse warning: No forms in -> 2 | tests/linter/threading-macros/input.clj:2:1: Parse warning: No forms in ->> 3 | tests/linter/threading-macros/input.clj:3:1: Parse warning: No forms in cond-> 4 | tests/linter/threading-macros/input.clj:4:1: Parse warning: No forms in cond->> 5 | tests/linter/threading-macros/input.clj:5:1: Parse warning: No forms in as-> 6 | tests/linter/threading-macros/input.clj:6:1: Parse warning: No forms in some-> 7 | tests/linter/threading-macros/input.clj:7:1: Parse warning: No forms in some->> 8 | tests/linter/threading-macros/input.clj:8:1: Parse warning: Odd number of clauses in cond->> 9 | tests/linter/threading-macros/input.clj:9:1: Parse warning: Odd number of clauses in cond-> 10 | -------------------------------------------------------------------------------- /tests/linter/try/input.clj: -------------------------------------------------------------------------------- 1 | (try 1) 2 | 3 | (try (catch Error e)) 4 | 5 | (try 1 (finally)) 6 | -------------------------------------------------------------------------------- /tests/linter/try/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/try/input.clj:1:1: Parse warning: try form without catch or finally 2 | tests/linter/try/input.clj:3:1: Parse warning: try form with empty body 3 | tests/linter/try/input.clj:5:8: Parse warning: finally form with empty body 4 | -------------------------------------------------------------------------------- /tests/linter/types-2/input.clj: -------------------------------------------------------------------------------- 1 | ;; Should PASS 2 | 3 | (seq {}) 4 | (seq []) 5 | (seq #{}) 6 | 7 | ;; Should FAIL 8 | 9 | (seq 1) 10 | (seq first) 11 | (def v 1) 12 | (seq v) 13 | (def ^Int v1 (if 1 2 3)) 14 | (seq v1) 15 | (def ^:dynamic dv 1) 16 | (seq dv) 17 | -------------------------------------------------------------------------------- /tests/linter/types-2/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/types-2/input.clj:9:6: Parse warning: arg[0] of core/seq must have type Seqable, got Int 2 | tests/linter/types-2/input.clj:10:6: Parse warning: arg[0] of core/seq must have type Seqable, got Fn 3 | tests/linter/types-2/input.clj:12:6: Parse warning: arg[0] of core/seq must have type Seqable, got Int 4 | -------------------------------------------------------------------------------- /tests/linter/types-4/input.clj: -------------------------------------------------------------------------------- 1 | (let [a "test"] 2 | (+ a 1)) 3 | -------------------------------------------------------------------------------- /tests/linter/types-4/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/types-4/input.clj:2:6: Parse warning: arg[0] of core/+ must have type Number, got String 2 | -------------------------------------------------------------------------------- /tests/linter/types-cljs/input.cljs: -------------------------------------------------------------------------------- 1 | (instance? ExceptionInfo nil) 2 | 3 | (instance? 1 nil) 4 | -------------------------------------------------------------------------------- /tests/linter/types-cljs/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/types-cljs/input.cljs:3:12: Parse warning: arg[0] of core/instance? must have type Type or Fn, got Int 2 | -------------------------------------------------------------------------------- /tests/linter/unused-as/.joker: -------------------------------------------------------------------------------- 1 | {:rules {:unused-as false}} 2 | -------------------------------------------------------------------------------- /tests/linter/unused-as/input.clj: -------------------------------------------------------------------------------- 1 | (let [{:keys [k] :as m} {}] k) 2 | (let [[f :as v] []] f) 3 | -------------------------------------------------------------------------------- /tests/linter/unused-as/output.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/candid82/joker/1eed1c02c38ef1373640c42af1423808826820cc/tests/linter/unused-as/output.txt -------------------------------------------------------------------------------- /tests/linter/unused-bindings/input.clj: -------------------------------------------------------------------------------- 1 | ;; Should PASS 2 | (let [a 1] a) 3 | (let [_ 1] 1) 4 | (loop [[a & b] [1 2]] 5 | (if a 6 | (recur b) 7 | 1)) 8 | 9 | ;; Should FAIL 10 | 11 | (let [a 1 b 2] b) 12 | (let [a 1] 13 | (let [a 2] 14 | a)) 15 | 16 | (loop [[a & b] [1 2]] 17 | (if 1 18 | (recur b) 19 | 1)) 20 | 21 | (let [{:keys [a b]} {}] (println a)) 22 | 23 | (let [_ 1 _ 2 a 1 a 2] a) 24 | 25 | (defn f [{}] 1) 26 | (defn f1 [[]] 1) 27 | -------------------------------------------------------------------------------- /tests/linter/unused-bindings/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/unused-bindings/input.clj:11:7: Parse warning: unused binding: a 2 | tests/linter/unused-bindings/input.clj:12:7: Parse warning: unused binding: a 3 | tests/linter/unused-bindings/input.clj:16:9: Parse warning: unused binding: a 4 | tests/linter/unused-bindings/input.clj:21:17: Parse warning: unused binding: b 5 | tests/linter/unused-bindings/input.clj:23:15: Parse warning: Unused binding: a 6 | tests/linter/unused-bindings/input.clj:25:10: Parse warning: destructuring with no bindings 7 | tests/linter/unused-bindings/input.clj:26:11: Parse warning: destructuring with no bindings 8 | -------------------------------------------------------------------------------- /tests/linter/unused-keys/.joker: -------------------------------------------------------------------------------- 1 | {:rules {:unused-keys false}} 2 | -------------------------------------------------------------------------------- /tests/linter/unused-keys/input.clj: -------------------------------------------------------------------------------- 1 | (let [{:keys [k] :as m} {} 2 | {:strs [st]} {} 3 | {:syms [s]} {} 4 | a 1 5 | b 2] 6 | a) 7 | -------------------------------------------------------------------------------- /tests/linter/unused-keys/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/unused-keys/input.clj:5:7: Parse warning: unused binding: b 2 | tests/linter/unused-keys/input.clj:1:22: Parse warning: unused binding: m 3 | -------------------------------------------------------------------------------- /tests/linter/unused-ns/.joker: -------------------------------------------------------------------------------- 1 | {:ignored-unused-namespaces [ignored.test]} 2 | -------------------------------------------------------------------------------- /tests/linter/unused-ns/input.clj: -------------------------------------------------------------------------------- 1 | (ns test 2 | (:require [test.ns1] 3 | [test.ns2 :as ns2] 4 | [test.ns3 :as ns3 :refer [f3]] 5 | [test.ns4 :as ns4 :refer [f4]] 6 | [test.ns5] 7 | [test.ns6 :as ns6] 8 | [test.ns7 :as n7] 9 | [test.ns8 :as n8] 10 | [test.ns9 :as n9] 11 | [test.n10] 12 | [ignored.test]) 13 | (:import my.JavaClass)) 14 | 15 | (f4) 16 | (test.ns5/f5) 17 | (ns6/f6) 18 | (#'n7/f7) 19 | 20 | (defmacro m8 21 | [& body] 22 | `(do 23 | (n8/f) 24 | ~@body)) 25 | 26 | ::n9/k 27 | 28 | `(test.n10/f10) 29 | -------------------------------------------------------------------------------- /tests/linter/unused-ns/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/unused-ns/input.clj:2:14: Parse warning: unused namespace test.ns1 2 | tests/linter/unused-ns/input.clj:3:14: Parse warning: unused namespace test.ns2 3 | tests/linter/unused-ns/input.clj:4:14: Parse warning: unused namespace test.ns3 4 | -------------------------------------------------------------------------------- /tests/linter/unused-vars-1/input.clj: -------------------------------------------------------------------------------- 1 | ;; Should PASS 2 | 3 | (def v1) 4 | (def v2 1) 5 | 6 | ;; Should FAIL 7 | 8 | (def ^:private v3) 9 | (def ^:private v4 1) 10 | (defmacro ^:private m [x] x) 11 | (defn ^:private f [] 1) 12 | -------------------------------------------------------------------------------- /tests/linter/unused-vars-1/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/unused-vars-1/input.clj:11:1: Parse warning: unused var f 2 | tests/linter/unused-vars-1/input.clj:10:1: Parse warning: unused var m 3 | tests/linter/unused-vars-1/input.clj:8:1: Parse warning: unused var v3 4 | tests/linter/unused-vars-1/input.clj:9:1: Parse warning: unused var v4 5 | -------------------------------------------------------------------------------- /tests/linter/unused-vars/input.clj: -------------------------------------------------------------------------------- 1 | (ns my.test) 2 | ;; Should PASS 3 | 4 | (def v1) 5 | (def v2 1) 6 | 7 | ;; Should FAIL 8 | 9 | (def ^:private v3) 10 | (def ^:private v4 1) 11 | (defmacro ^:private m [x] x) 12 | (defn ^:private f [] 1) 13 | 14 | 15 | -------------------------------------------------------------------------------- /tests/linter/unused-vars/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/unused-vars/input.clj:12:1: Parse warning: unused var f 2 | tests/linter/unused-vars/input.clj:11:1: Parse warning: unused var m 3 | tests/linter/unused-vars/input.clj:9:1: Parse warning: unused var v3 4 | tests/linter/unused-vars/input.clj:10:1: Parse warning: unused var v4 5 | -------------------------------------------------------------------------------- /tests/linter/valid-indent/input.clj: -------------------------------------------------------------------------------- 1 | (def valid+& 1) 2 | 3 | (def non-ascii-да 2) 4 | 5 | (def non-core-#$:%| 3) 6 | -------------------------------------------------------------------------------- /tests/linter/valid-indent/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/valid-indent/input.clj:3:17: Read warning: Impermissible character 'д' at 10 in "non-ascii-да" (not a (7-bit) ASCII character) 2 | tests/linter/valid-indent/input.clj:3:17: Read warning: Impermissible character 'а' at 11 in "non-ascii-да" (not a (7-bit) ASCII character) 3 | tests/linter/valid-indent/input.clj:5:19: Read warning: Impermissible character '|' at 13 in "non-core-#$:%|" (not a Letter nor (Decimal) Digit (category L nor Nd)) 4 | -------------------------------------------------------------------------------- /tests/linter/when/input.clj: -------------------------------------------------------------------------------- 1 | (when 1) 2 | (when-not 1) 3 | -------------------------------------------------------------------------------- /tests/linter/when/output.txt: -------------------------------------------------------------------------------- 1 | tests/linter/when/input.clj:1:1: Parse warning: when form with empty body 2 | tests/linter/when/input.clj:2:1: Parse warning: when-not form with empty body 3 | -------------------------------------------------------------------------------- /tests/main.clj: -------------------------------------------------------------------------------- 1 | (def t "docstring" 1) 2 | (fn [x] x) 3 | `(let) 4 | -------------------------------------------------------------------------------- /tests/run-tests.joke: -------------------------------------------------------------------------------- 1 | (def exit-code 0) 2 | 3 | (defn first-diff-index 4 | [s1 s2] 5 | (let [c1 (count s1) 6 | c2 (count s2)] 7 | (loop [i 0] 8 | (if (or (= i c1) 9 | (= i c2)) 10 | (if (= c1 c2) 11 | -1 12 | i) 13 | (if (= (nth s1 i) (nth s2 i)) 14 | (recur (inc i)) 15 | i))))) 16 | 17 | (let [[cmd root-dir output-k output-file-name] *command-line-args* 18 | output-k (keyword output-k) 19 | test-dirs (->> (joker.os/ls root-dir) 20 | (filter :dir?) 21 | (map :name)) 22 | pwd (get (joker.os/env) "PWD") 23 | exe (str pwd "/joker")] 24 | (doseq [test-dir test-dirs] 25 | (let [dir (str root-dir "/" test-dir "/") 26 | filename (if (joker.os/exists? (str dir "input.clj")) 27 | (str dir "input.clj") 28 | (str dir "input.cljs")) 29 | res (joker.os/sh exe cmd filename) 30 | output (output-k res) 31 | expected (slurp (str dir output-file-name))] 32 | (when (and (= :err output-k) 33 | (not= "" output) 34 | (:success res)) 35 | (println "FAILED:" test-dir "(zero exit code, yet stderr written to)") 36 | (var-set #'exit-code 1)) 37 | (when (and (= :err output-k) 38 | (= "" output) 39 | (not (:success res))) 40 | (println "FAILED:" test-dir "(nonzero exit code, yet no stderr output)") 41 | (var-set #'exit-code 1)) 42 | (when-not (= expected output) 43 | (println "FAILED:" test-dir) 44 | (println "EXPECTED:") 45 | (println expected) 46 | (println "ACTUAL:") 47 | (let [diff-i (first-diff-index expected output) 48 | output' (if (= -1 diff-i) 49 | output 50 | (str (subs output 0 diff-i) "❌" (subs output diff-i)))] 51 | (println output')) 52 | (var-set #'exit-code 1))))) 53 | 54 | (joker.os/exit exit-code) 55 | -------------------------------------------------------------------------------- /tools/cljs-macros.joke: -------------------------------------------------------------------------------- 1 | (load-file "../core/data/linter_cljx.joke") 2 | (load-file "../core/data/linter_cljs.joke") 3 | (def interns (ns-interns 'joker.core)) 4 | 5 | (defn exists? 6 | [line] 7 | (let [parts (joker.string/split (joker.string/trim-space line) #" ") 8 | name (second (rest parts))] 9 | (if name 10 | (get interns (symbol name)) 11 | false))) 12 | 13 | (let [input (slurp "cljs-macros.input") 14 | lines (joker.string/split-lines input)] 15 | (doseq [line (remove exists? lines)] 16 | (println line))) 17 | -------------------------------------------------------------------------------- /tools/sum256dir/.gitignore: -------------------------------------------------------------------------------- 1 | sum256dir 2 | -------------------------------------------------------------------------------- /tools/sum256dir/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "crypto/sha256" 5 | "fmt" 6 | "io" 7 | "os" 8 | "path/filepath" 9 | ) 10 | 11 | var h = sha256.New() 12 | 13 | func processFile(file string) (err error) { 14 | f, err := os.Open(file) 15 | if err != nil { 16 | return 17 | } 18 | _, err = io.Copy(h, f) 19 | return 20 | } 21 | 22 | func processEntry(path string, info os.FileInfo, err error) error { 23 | if err != nil { 24 | return err 25 | } 26 | name := info.Name() 27 | if name == ".git" { 28 | return filepath.SkipDir 29 | } 30 | if !info.IsDir() { 31 | err = processFile(path) 32 | } 33 | return err 34 | } 35 | 36 | func main() { 37 | for _, arg := range os.Args[1:] { 38 | info, err := os.Stat(arg) 39 | if err != nil { 40 | fmt.Fprintf(os.Stderr, "os.Stat: %v\n", err) 41 | os.Exit(3) 42 | } 43 | if info.IsDir() { 44 | err = filepath.Walk(arg, processEntry) 45 | } else { 46 | err = processFile(arg) 47 | } 48 | if err != nil { 49 | fmt.Fprintf(os.Stderr, "%v\n", err) 50 | os.Exit(3) 51 | } 52 | } 53 | 54 | fmt.Printf("%x\n", h.Sum(nil)) 55 | } 56 | --------------------------------------------------------------------------------