├── .gitignore ├── .travis.yml ├── COPYING ├── COPYING.LESSER ├── Dockerfile ├── Makefile ├── README.md ├── benchmarks ├── apply_varargs.pxi ├── deftype_fields.pxi ├── dynamic_vars.pxi ├── ffi_test.pxi ├── get_from_hashmap.pxi ├── read-line.pxi ├── reduce_varargs.pxi ├── transduce_range_iterator.pxi ├── transduce_range_reduced.pxi ├── vector_assoc.pxi └── vector_build_and_hash.pxi ├── examples ├── gen-docs.pxi ├── hello-world.pxi ├── mandelbrot.pxi ├── mu-kanren.pxi └── tty-io-test.pxi ├── find_externals_name.py ├── generate-docs.pxi ├── lib_pixie.py ├── pixie ├── PixieChecker.hpp ├── __init__.py ├── async.pxi ├── buffers.pxi ├── channels.pxi ├── csp.pxi ├── data │ └── json.pxi ├── ffi-infer.pxi ├── fs.pxi ├── io-blocking.pxi ├── io.pxi ├── io │ ├── common.pxi │ ├── tcp.pxi │ ├── tty.pxi │ └── uv-common.pxi ├── lib_pixie.py ├── math.pxi ├── parser.pxi ├── parser │ └── json.pxi ├── repl.pxi ├── set.pxi ├── stacklets.pxi ├── stdlib.pxi ├── streams.pxi ├── streams │ ├── utf8.pxi │ ├── zlib.pxi │ └── zlib │ │ └── ffi.pxi ├── string.pxi ├── system.pxi ├── test.pxi ├── time.pxi ├── uv.pxi ├── vm │ ├── __init__.py │ ├── array.py │ ├── atom.py │ ├── bits.py │ ├── bootstrap.py │ ├── c_api.py │ ├── code.py │ ├── compiler.py │ ├── cons.py │ ├── custom_types.py │ ├── interpreter.py │ ├── keyword.py │ ├── lazy_seq.py │ ├── libs │ │ ├── __init__.py │ │ ├── c │ │ │ ├── uv_ffi.c │ │ │ └── uv_ffi.h │ │ ├── env.py │ │ ├── ffi.py │ │ ├── libedit.py │ │ ├── path.py │ │ ├── platform.py │ │ ├── pxic │ │ │ ├── __init__.py │ │ │ ├── reader.py │ │ │ ├── tags.py │ │ │ ├── util.py │ │ │ └── writer.py │ │ ├── ring_buffer.py │ │ └── string.py │ ├── map_entry.py │ ├── numbers.py │ ├── object.py │ ├── persistent_hash_map.py │ ├── persistent_hash_set.py │ ├── persistent_list.py │ ├── persistent_vector.py │ ├── primitives.py │ ├── reader.py │ ├── reduced.py │ ├── rt.py │ ├── stacklet.py │ ├── stdlib.py │ ├── string.py │ ├── string_builder.py │ ├── symbol.py │ ├── test │ │ ├── __init__.py │ │ ├── test_compile.py │ │ ├── test_hashmaps.py │ │ ├── test_reader.py │ │ ├── test_vars.py │ │ └── test_vectors.py │ ├── threads.py │ └── util.py └── walk.pxi ├── run-tests.pxi ├── target.py └── tests └── pixie └── tests ├── collections ├── test-maps.pxi ├── test-seqables.pxi ├── test-sets.pxi └── test-vectors.pxi ├── data └── test-json.pxi ├── fs └── parent │ ├── bar.txt │ ├── child │ ├── bar.txt │ └── foo.txt │ └── foo.txt ├── io └── test-tcp.pxi ├── parser └── test-json.pxi ├── streams └── test-utf8.pxi ├── test-arrays.pxi ├── test-async.pxi ├── test-bits.pxi ├── test-buffers.pxi ├── test-channels.pxi ├── test-compare.pxi ├── test-compiler.pxi ├── test-csp.pxi ├── test-defrecord.pxi ├── test-deftype.pxi ├── test-destructuring.pxi ├── test-docs.pxi ├── test-errors.pxi ├── test-ffi.pxi ├── test-fns.pxi ├── test-forms.pxi ├── test-fs.pxi ├── test-io-utf8.txt ├── test-io.pxi ├── test-io.txt ├── test-ith.pxi ├── test-keywords.pxi ├── test-macros.pxi ├── test-numbers.pxi ├── test-object.pxi ├── test-parser.pxi ├── test-readeval.pxi ├── test-sets.pxi ├── test-stdlib.pxi ├── test-strings.pxi ├── test-utf8.pxi ├── test-walk.pxi └── utils.pxi /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | externals* 3 | pixie-vm 4 | .idea 5 | lib 6 | include 7 | *.pxic 8 | test.tmp 9 | /libuv* 10 | /uv-* 11 | /uv.h 12 | /*compressed-output.* 13 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | env: 3 | - JIT_OPTS='--opt=jit' TARGET_OPTS='target.py' 4 | - JIT_OPTS='' TARGET_OPTS='target.py' 5 | 6 | matrix: 7 | fast_finish: true 8 | 9 | script: 10 | - make PYTHON=python build 11 | - make compile_src 12 | - make compile_tests 13 | - make run_built_tests 14 | 15 | addons: 16 | apt: 17 | packages: 18 | - libffi-dev 19 | - libedit-dev 20 | - libboost-all-dev 21 | - zlib1g-dev 22 | - zlib-bin 23 | 24 | notifications: 25 | irc: "chat.freenode.net#pixie-lang" 26 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:sid 2 | 3 | # install dependencies 4 | RUN apt-get update \ 5 | && apt-get install -y gcc g++ libboost-dev pkg-config make curl bzip2 python2.7 \ 6 | && apt-get install -y libffi-dev libuv-dev libedit-dev 7 | 8 | ADD . /usr/src/pixie 9 | 10 | # build the thing 11 | RUN cd /usr/src/pixie \ 12 | && make PYTHON=python2.7 build_with_jit \ 13 | && ln -s /usr/src/pixie/pixie-vm /usr/bin/pxi 14 | 15 | ENTRYPOINT ["/usr/bin/pxi"] 16 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: help 2 | 3 | EXTERNALS=externals 4 | 5 | PYTHON ?= `env which -a python2 python2.7 | head -n1` 6 | PYTHONPATH=$$PYTHONPATH:$(EXTERNALS)/pypy 7 | 8 | 9 | COMMON_BUILD_OPTS?=--thread --gcrootfinder=shadowstack --continuation 10 | JIT_OPTS?=--opt=jit 11 | TARGET_OPTS?=target.py 12 | 13 | help: 14 | @echo "make help - display this message" 15 | @echo "make run - run the compiled interpreter" 16 | @echo "make run_interactive - run without compiling (slow)" 17 | @echo "make build_with_jit - build with jit enabled" 18 | @echo "make build_no_jit - build without jit" 19 | @echo "make fetch_externals - download and unpack external deps" 20 | 21 | build_with_jit: fetch_externals 22 | @if [ ! -d /usr/local/include/boost -a ! -d /usr/include/boost ] ; then echo "Boost C++ Library not found" && false; fi && \ 23 | $(PYTHON) $(EXTERNALS)/pypy/rpython/bin/rpython $(COMMON_BUILD_OPTS) --opt=jit target.py && \ 24 | make compile_basics 25 | 26 | build_no_jit: fetch_externals 27 | @if [ ! -d /usr/local/include/boost -a ! -d /usr/include/boost ] ; then echo "Boost C++ Library not found" && false; fi && \ 28 | $(PYTHON) $(EXTERNALS)/pypy/rpython/bin/rpython $(COMMON_BUILD_OPTS) target.py && \ 29 | make compile_basics 30 | 31 | build_no_jit_shared: fetch_externals 32 | @if [ ! -d /usr/local/include/boost -a ! -d /usr/include/boost ] ; then echo "Boost C++ Library not found" && false; fi && \ 33 | $(PYTHON) $(EXTERNALS)/pypy/rpython/bin/rpython $(COMMON_BUILD_OPTS) --shared target.py && \ 34 | make compile_basics 35 | 36 | 37 | compile_basics: 38 | @echo -e "\n\n\n\nWARNING: Compiling core libs. If you want to modify one of these files delete the .pxic files first\n\n\n\n" 39 | ./pixie-vm -c pixie/uv.pxi -c pixie/io.pxi -c pixie/stacklets.pxi -c pixie/stdlib.pxi -c pixie/repl.pxi 40 | 41 | build: fetch_externals 42 | $(PYTHON) $(EXTERNALS)/pypy/rpython/bin/rpython $(COMMON_BUILD_OPTS) $(JIT_OPTS) $(TARGET_OPTS) 43 | 44 | fetch_externals: $(EXTERNALS)/pypy externals.fetched 45 | 46 | externals.fetched: 47 | echo https://github.com/pixie-lang/external-deps/releases/download/1.0/`uname -s`-`uname -m`.tar.bz2 48 | curl -L https://github.com/pixie-lang/external-deps/releases/download/1.0/`uname -s`-`uname -m`.tar.bz2 > /tmp/externals.tar.bz2 49 | tar -jxf /tmp/externals.tar.bz2 --strip-components=2 50 | touch externals.fetched 51 | 52 | 53 | $(EXTERNALS)/pypy: 54 | mkdir $(EXTERNALS); \ 55 | cd $(EXTERNALS); \ 56 | curl https://bitbucket.org/pypy/pypy/get/91db1a9.tar.bz2 > pypy.tar.bz2; \ 57 | mkdir pypy; \ 58 | cd pypy; \ 59 | tar -jxf ../pypy.tar.bz2 --strip-components=1 60 | 61 | run: 62 | ./pixie-vm 63 | 64 | 65 | run_interactive: 66 | @PYTHONPATH=$(PYTHONPATH) $(PYTHON) target.py 67 | 68 | run_interactive_stacklets: 69 | @PYTHONPATH=$(PYTHONPATH) $(PYTHON) target.py pixie/stacklets.pxi 70 | 71 | 72 | run_built_tests: pixie-vm 73 | ./pixie-vm run-tests.pxi 74 | 75 | run_interpreted_tests: target.py 76 | PYTHONPATH=$(PYTHONPATH) $(PYTHON) target.py run-tests.pxi 77 | 78 | compile_tests: 79 | find "tests" -name "*.pxi" | xargs -L1 ./pixie-vm -l "tests" -c 80 | 81 | compile_src: 82 | find * -name "*.pxi" | grep "^pixie/" | xargs -L1 ./pixie-vm $(EXTERNALS_FLAGS) -c 83 | 84 | clean_pxic: 85 | find * -name "*.pxic" | xargs --no-run-if-empty rm 86 | 87 | clean: clean_pxic 88 | rm -rf ./lib 89 | rm -rf ./include 90 | rm -rf ./externals* 91 | rm -f ./pixie-vm 92 | rm -f ./*.pyc 93 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/pixie-lang/pixie.svg?branch=master)](https://travis-ci.org/pixie-lang/pixie) 2 | [![License: LGPL](https://img.shields.io/badge/license-LGPL-green.svg)](https://img.shields.io/badge/license-LGPL-green.svg) 3 | # Pixie 4 | 5 | [![Join the chat at https://gitter.im/pixie-lang/pixie](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/pixie-lang/pixie?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 6 | 7 | ## Intro 8 | 9 | Pixie is a lightweight lisp suitable for both general use as well as shell scripting. The language is still in a "pre-alpha" phase and as such changes fairly quickly. 10 | The standard library is heavily inspired by Clojure as well as several other functional programming languages. It is written in RPython (http://pypy.readthedocs.org/en/latest/coding-guide.html) and as such supports a fairly fast GC and an amazingly fast tracing JIT. 11 | 12 | ## Features 13 | 14 | Some planned and implemented features: 15 | 16 | * Immutable datastructures 17 | * Protocols first implementation 18 | * Transducers at-the-bottom (most primitives are based off of reduce) 19 | * A "good enough" JIT (implemented, tuning still a WIP, but not bad performance today) 20 | * Easy FFI 21 | * Pattern matching (TODO) 22 | 23 | ## Dependencies 24 | 25 | * python or pypy to build 26 | * [libffi-dev](https://sourceware.org/libffi/) 27 | * [libedit-dev](http://thrysoee.dk/editline/) 28 | * [libuv-dev](https://github.com/libuv/libuv) Version 1.0 or higher 29 | * [libboost-all-dev](http://www.boost.org/) (`brew install boost` for Mac) 30 | 31 | ## Building 32 | 33 | make build_with_jit 34 | ./pixie-vm 35 | 36 | Note: Mac OS X does not come with the build tools required by default. Install the XCode Command Line tools ([Apple Developer Site](http://developer.apple.com)) or install them independently. 37 | 38 | 39 | ## Running the tests 40 | 41 | ./pixie-vm run-tests.pxi 42 | 43 | 44 | ## Examples 45 | 46 | There are examples in the /examples directory. 47 | Try out "Hello World" with: 48 | 49 | ./examples/hello-world.pxi 50 | 51 | 52 | ## Build Tool 53 | Pixie now comes with a build tool called [dust](https://github.com/pixie-lang/dust). Try it and start making magic of your own. 54 | 55 | ## FAQ 56 | 57 | ### So this is written in Python? 58 | 59 | It's actually written in RPython, the same language PyPy is written in. `make build_with_jit` will compile Pixie using the PyPy toolchain. After some time, it will produce an executable called `pixie-vm`. This executable is a full blown native interpreter with a JIT, GC, etc. So yes, the guts are written in RPython, just like the guts of most lisp interpreters are written in C. At runtime the only thing that is interpreted is the Pixie bytecode, that is until the JIT kicks in... 60 | 61 | 62 | ### What's this bit about "magical powers"? 63 | 64 | First of all, the word "magic" is in quotes as it's partly a play on words, pixies are small, light and often considered to have magical powers. 65 | 66 | However there are a few features of pixie that although may not be uncommon, are perhaps unexpected from a lisp. 67 | 68 | * Pixie implements its own virtual machine. It does not run on the JVM, CLR or Python VM. It implements its own bytecode, has its own GC and JIT. And it's small. Currently the interpreter, JIT, GC, and stdlib clock in at about 10.3MB once compiled down to an executable. 69 | 70 | * The JIT makes some things fast. Very fast. Code like the following compiles down to a loop with 6 CPU instructions. While this may not be too impressive for any language that uses a tracing jit, it is fairly unique for a language as young as Pixie. 71 | 72 | ```clojure 73 | 74 | ;; This code adds up to 10000 from 0 via calling a function that takes a variable number of arguments. 75 | ;; That function then reduces over the argument list to add up all given arguments. 76 | 77 | (defn add-fn [& args] 78 | (reduce -add 0 args)) 79 | 80 | (loop [x 0] 81 | (if (eq x 10000) 82 | x 83 | (recur (add-fn x 1)))) 84 | 85 | ``` 86 | 87 | 88 | 89 | * Math system is fully polymorphic. Math primitives (+,-, etc.) are built off of polymorphic functions that dispatch on the types of the first two arguments. This allows the math system to be extended to complex numbers, matrices, etc. The performance penalty of such a polymorphic call is completely removed by the RPython generated JIT. 90 | 91 | (Planned "magical" Features) 92 | 93 | * Influencing the JIT from user code. (Still in research) Eventually it would be nice to allow Pixie to hint to the JIT that certain values are constants, that certain functions are pure, etc. This can all be done from inside RPython, and the plan is to expose parts of that to the user via hints in the Pixie language, to what extent this will be possible is not yet known. 94 | 95 | * STM for parallelism. Once STM gets merged into the mainline branch of PyPy, we'll adopt it pretty quickly. 96 | 97 | * CSP for concurrency. We already have stacklets, it's not that hard to use them for CSP style concurrency as well. 98 | 99 | ## Where do the devs hangout? 100 | Mostly on FreeNode at `#pixie-lang` stop by and say "hello". 101 | 102 | ## Contributing 103 | 104 | We have a very open contribution process. If you have a feature you'd like to implement, submit a PR or file an issue and we'll see what we can do. Most PRs are either rejected (if there is a technical flaw) or accepted within a day, so send an improvement our way and see what happens. 105 | 106 | ## Implementation Notes 107 | 108 | Although parts of the language may be very close to Clojure (they are both lisps after all), language parity is not a design goal. We will take the features from Clojure or other languages that are suitable to our needs, and feel free to reject those that aren't. Therefore this should not be considered a "Clojure Dialect", but instead a "Clojure inspired lisp". 109 | 110 | ## Disclaimer 111 | This project is the personal work of Timothy Baldridge and contributors. It is not supported by any entity, including Timothy's employer, or any employers of any other contributors. 112 | 113 | ## Copying 114 | 115 | Free use of this software is granted under the terms of the GNU Lesser General Public License (LGPL). For details see the files `COPYING` and `COPYING.LESSER` included with the source distribution. All copyrights are owned by their respective authors. 116 | -------------------------------------------------------------------------------- /benchmarks/apply_varargs.pxi: -------------------------------------------------------------------------------- 1 | (loop [x 0] (if (eq x 10000) x (recur ((fn [& args] (apply + 1 args)) x)))) 2 | :exit-repl 3 | -------------------------------------------------------------------------------- /benchmarks/deftype_fields.pxi: -------------------------------------------------------------------------------- 1 | ;; Before Immutable Opt: 3.9 sec 2 | ;; After 3.2 sec 3 | 4 | (defprotocol IAdder 5 | (add-them [this])) 6 | 7 | (deftype Adder [a b] 8 | IAdder 9 | (add-them [this] 10 | (set-field! this :b (+ a b)) 11 | b)) 12 | 13 | 14 | (def adder (->Adder 1 0)) 15 | (dotimes [x (* 1024 1024 1024)] 16 | (assert (= (inc x) (add-them adder)))) 17 | -------------------------------------------------------------------------------- /benchmarks/dynamic_vars.pxi: -------------------------------------------------------------------------------- 1 | (defn add-fn [x] 2 | (inc (inc x))) 3 | 4 | 5 | (set-dynamic! (resolve 'pixie.stdlib/add-fn)) 6 | 7 | (set! (resolve 'pixie.stdlib/add-fn) inc) 8 | 9 | 10 | (loop [x 0] (if (eq x 10000) 11 | x (recur (add-fn x)))) 12 | :exit-repl 13 | -------------------------------------------------------------------------------- /benchmarks/ffi_test.pxi: -------------------------------------------------------------------------------- 1 | (loop [x 0] 2 | (if (= x 10000) 3 | x 4 | (do (printf ".") 5 | (recur (inc x))))) 6 | -------------------------------------------------------------------------------- /benchmarks/get_from_hashmap.pxi: -------------------------------------------------------------------------------- 1 | (let [map {:number 1}] 2 | (loop [x 0] 3 | (if (= x 10000) 4 | x 5 | (recur (+ x (get map :number)))))) 6 | -------------------------------------------------------------------------------- /benchmarks/read-line.pxi: -------------------------------------------------------------------------------- 1 | (ns benchmarks.readline 2 | (:require [pixie.time :as time] 3 | [pixie.io :as io] 4 | [pixie.streams.utf8 :as utf8])) 5 | 6 | (def file-name "/usr/share/dict/words") 7 | 8 | (println "Lazy line-seq") 9 | (time/time 10 | (->> file-name 11 | (io/open-read) 12 | (io/buffered-input-stream) 13 | (io/line-seq) 14 | (count))) 15 | 16 | (println "Reducing line-reader") 17 | (time/time 18 | (->> file-name 19 | (io/open-read) 20 | (io/buffered-input-stream) 21 | (io/line-reader) 22 | (into []) 23 | (count))) 24 | 25 | (println "Lazy UTF8 line-seq") 26 | (time/time 27 | (->> file-name 28 | (io/open-read) 29 | (io/buffered-input-stream) 30 | (utf8/utf8-input-stream) 31 | (io/line-seq) 32 | (count))) 33 | 34 | (println "Reducing UTF8 line-reader") 35 | (time/time 36 | (->> file-name 37 | (io/open-read) 38 | (io/buffered-input-stream) 39 | (utf8/utf8-input-stream) 40 | (io/line-reader) 41 | (into []) 42 | (count))) 43 | -------------------------------------------------------------------------------- /benchmarks/reduce_varargs.pxi: -------------------------------------------------------------------------------- 1 | (defn add-fn [& args] 2 | (reduce -add 0 args)) 3 | 4 | (loop [x 0] 5 | (if (eq x 10000) 6 | x 7 | (recur (add-fn x 1)))) 8 | 9 | 10 | 11 | :exit-repl 12 | -------------------------------------------------------------------------------- /benchmarks/transduce_range_iterator.pxi: -------------------------------------------------------------------------------- 1 | (reduce (fn [_ i] nil) nil (-iterator (range 10000000))) 2 | -------------------------------------------------------------------------------- /benchmarks/transduce_range_reduced.pxi: -------------------------------------------------------------------------------- 1 | (reduce (fn [_ i] nil) nil (range 10000000)) 2 | -------------------------------------------------------------------------------- /benchmarks/vector_assoc.pxi: -------------------------------------------------------------------------------- 1 | (let [v (loop [acc [] 2 | i 0] 3 | (if (= (count acc) 10000) 4 | acc 5 | (recur (conj acc i) (inc i))))] 6 | (loop [i 0] 7 | (if (= (nth acc i) i) 8 | (recur (inc i)) 9 | (throw "Assert failure"))) 10 | 11 | nil) 12 | 13 | 14 | 15 | :exit-repl 16 | -------------------------------------------------------------------------------- /benchmarks/vector_build_and_hash.pxi: -------------------------------------------------------------------------------- 1 | (loop [acc []] 2 | (if (= (count acc) 10000) 3 | (hash acc) 4 | (recur (conj acc (count acc))))) 5 | 6 | :exit-repl 7 | -------------------------------------------------------------------------------- /examples/gen-docs.pxi: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env pixie-vm 2 | 3 | ; generate html docs for a given namespace 4 | (ns gen-ns-docs 5 | (use 'hiccup.core)) 6 | 7 | (defn munge [nm] 8 | (-> (str nm) 9 | (replace "-" "_") 10 | (replace "?" ""))) 11 | 12 | (defn generate-docs [ns] 13 | (html [:html 14 | [:head 15 | [:title ns] 16 | [:meta {:charset "utf-8"}] 17 | [:style {:type "text/css"} 18 | " 19 | .version { 20 | color: #aaa; 21 | } 22 | 23 | #overview ul { 24 | -webkit-column-width: 15em; 25 | -moz-column-width: 15em; 26 | column-width: 15em; 27 | } 28 | "]] 29 | [:body 30 | [:h1 ns] 31 | (let [syms (ns-map ns) 32 | infos (transduce (comp (map (fn [sym] 33 | (when-let [info (meta @(resolve-in (the-ns ns) sym))] 34 | (assoc info :name (hiccup.util/escape-html sym))))) 35 | (filter (complement nil?))) 36 | conj 37 | (keys (ns-map ns)))] 38 | (list [:section#overview 39 | [:h2 "Overview"] 40 | [:ul 41 | (for [{name :name} infos] 42 | [:li [:a {:href (str "#" ns "/" name)} (str ns "/" name)]])]] 43 | (seq (map (fn [{:keys [name doc signatures added examples]}] 44 | [:article 45 | [:h2 {:id (str ns "/" name)} name (when added [:span.version (str " (since " added ")")])] 46 | (when signatures 47 | [:pre (pr-str (seq signatures))]) 48 | (when doc 49 | [:pre doc]) 50 | (when examples 51 | [:section.examples 52 | [:ul 53 | (for [[expr output result] examples] 54 | [:pre (str "user => " expr (as-str output) "\n" (as-str result))])]])]) 55 | infos))))]])) 56 | 57 | (defn main [file ns] 58 | (load-file file) 59 | (println (str "\n" (generate-docs (read-string ns))))) 60 | 61 | (if (< (count program-arguments) 2) 62 | (println "Usage: gen-ns-docs ") 63 | (let [[file ns] program-arguments] 64 | (main file ns))) 65 | -------------------------------------------------------------------------------- /examples/hello-world.pxi: -------------------------------------------------------------------------------- 1 | #!./pixie-vm 2 | 3 | (defn greet [name] 4 | (println (str "Hello, " (or name "World") "!"))) 5 | 6 | (greet (first program-arguments)) 7 | -------------------------------------------------------------------------------- /examples/mandelbrot.pxi: -------------------------------------------------------------------------------- 1 | ;; Mandelbrot demo from Timothy Baldridge's 2015 Strange Loop talk 2 | ;; https://www.youtube.com/watch?v=1AjhFZVfB9c 3 | 4 | 5 | (ns mandelbrot 6 | (:require [pixie.ffi-infer :refer :all] 7 | [pixie.ffi :as ffi] 8 | [pixie.time :refer [time]])) 9 | 10 | (with-config {:library "SDL2" 11 | :cxx-flags ["`sdl2-config --cflags`"] 12 | :includes ["SDL.h"]} 13 | 14 | (defcfn SDL_Init) 15 | 16 | (defconst SDL_INIT_VIDEO) 17 | (defconst SDL_WINDOWPOS_UNDEFINED) 18 | (defcfn SDL_CreateWindow) 19 | (defcfn SDL_CreateRenderer) 20 | (defcfn SDL_CreateTexture) 21 | (defconst SDL_PIXELFORMAT_RGBA8888) 22 | (defconst SDL_TEXTUREACCESS_STREAMING) 23 | (defcfn SDL_UpdateTexture) 24 | (defcfn SDL_RenderClear) 25 | (defcfn SDL_RenderCopy) 26 | 27 | (defconst SDL_WINDOW_SHOWN) 28 | (defcfn SDL_RenderPresent) 29 | (defcfn SDL_LockSurface)) 30 | 31 | (println "starting") 32 | (def WIDTH 1024) 33 | (def HEIGHT 512) 34 | 35 | (assert (>= (SDL_Init SDL_INIT_VIDEO) 0)) 36 | 37 | (def WINDOW (SDL_CreateWindow "Pixie MandelBrot" 38 | SDL_WINDOWPOS_UNDEFINED 39 | SDL_WINDOWPOS_UNDEFINED 40 | WIDTH 41 | HEIGHT 42 | SDL_WINDOW_SHOWN)) 43 | 44 | (assert WINDOW "Could not create window") 45 | 46 | (def RENDERER (SDL_CreateRenderer WINDOW -1 0)) 47 | 48 | (def DRAW_SURFACE (SDL_CreateTexture RENDERER 49 | SDL_PIXELFORMAT_RGBA8888 50 | SDL_TEXTUREACCESS_STREAMING 51 | WIDTH 52 | HEIGHT)) 53 | 54 | (defn bit-or [& args] 55 | (reduce pixie.stdlib/bit-or 0 args)) 56 | 57 | (defn put-pixel [ptr x y r g b] 58 | (let [loc (* 4 (+(* y WIDTH) x))] 59 | (ffi/pack! ptr loc CUInt32 (bit-or (bit-shift-left r 24) 60 | (bit-shift-left g 16) 61 | (bit-shift-left b 8) 62 | 255)))) 63 | 64 | (def BUFFER (buffer (* 4 WIDTH HEIGHT))) 65 | 66 | (defn mandel-point [x y width height max_iterations] 67 | (let [x0 (float (- (* (/ x width) 3.5) 2.5)) 68 | y0 (float (- (* (/ y height) 2) 1))] 69 | (loop [x 0.0 70 | y 0.0 71 | iteration 0] 72 | (let [xsq (* x x) 73 | ysq (* y y)] 74 | (if (and (< (+ xsq 75 | ysq) 76 | (* 2 2)) 77 | (< iteration max_iterations)) 78 | (let [xtemp (+ (- xsq 79 | ysq) 80 | x0) 81 | y (+ (* 2 x y) y0)] 82 | (recur xtemp y (inc iteration))) 83 | (- 1 (/ iteration max_iterations))))))) 84 | 85 | (dotimes [x 3] 86 | (time 87 | (let [max (* WIDTH HEIGHT)] 88 | (dotimes [y HEIGHT] 89 | (dotimes [x WIDTH] 90 | (let [result (mandel-point (float x) (float y) (float WIDTH) (float HEIGHT) 1000) 91 | color (int (* 16777216 result))] 92 | (put-pixel BUFFER x y 93 | (bit-shift-right color 16) 94 | (bit-and (bit-shift-right color 8) 0xff) 95 | (bit-and color 0xff)))))))) 96 | 97 | (SDL_UpdateTexture DRAW_SURFACE nil BUFFER (* 4 WIDTH)) 98 | (SDL_RenderCopy RENDERER DRAW_SURFACE nil nil) 99 | (SDL_RenderPresent RENDERER) 100 | 101 | (pixie.stacklets/sleep 10000) 102 | -------------------------------------------------------------------------------- /examples/mu-kanren.pxi: -------------------------------------------------------------------------------- 1 | (ns examples.mu-kanren) 2 | 3 | (defn lvar [] 4 | (gensym)) 5 | 6 | (defn lvar? [x] 7 | (symbol? x)) 8 | 9 | (defn walk [s u] 10 | (let [pr (get s u)] 11 | (if (lvar? pr) 12 | (recur s pr) 13 | pr) 14 | u)) 15 | 16 | (defn unify [s u v] 17 | (let [u (walk s u) 18 | v (walk s v)] 19 | (cond 20 | (and (lvar? u) 21 | (lvar? v) 22 | (= u v)) s 23 | (lvar? u) (assoc s u v) 24 | (lvar? v) (assoc s v u) 25 | :else (and (= u v) s)))) 26 | 27 | (defn == [a b] 28 | (keep (fn [s] (unify s a b)))) 29 | 30 | 31 | (defn -disj [& xforms] 32 | (fn [xf] 33 | (let [xforms (transduce (map (fn [xform] 34 | (xform xf))) 35 | conj 36 | xforms)] 37 | (fn 38 | ([] (xf)) 39 | ([acc] (xf acc)) 40 | ([acc i] (reduce 41 | (fn [acc xform] 42 | (xform acc i)) 43 | acc 44 | xforms)))))) 45 | 46 | (defn conde [& goals] 47 | (apply -disj (map (fn [goals] 48 | (apply comp goals)) 49 | goals))) 50 | 51 | 52 | "Use transduce to run eagerly" 53 | (transduce (conde 54 | [(== 'a 42)] 55 | [ (== 'b 1)]) 56 | conj 57 | [{}]) 58 | 59 | "Use sequence to make it lazy (via stacklets)" 60 | (sequence (conde 61 | [(== 'a 42)] 62 | [(== 'b 1)]) 63 | [{}]) 64 | -------------------------------------------------------------------------------- /examples/tty-io-test.pxi: -------------------------------------------------------------------------------- 1 | (ns io-test 2 | (:require [pixie.io :as io] 3 | [pixie.system :as sys] 4 | [pixie.io.tty :as tty])) 5 | 6 | (def history (atom [])) 7 | 8 | (defn depth [d] 9 | (apply str (take d (repeat " ")))) 10 | 11 | (defn nested-trace 12 | "Prints a stack trace with each level indented slightly" 13 | [e] 14 | (loop [d 0 traces (trace e)] 15 | (io/spit tty/stdout (str (depth d) (pr-str (first traces)) "\n")) 16 | (if (seq traces) 17 | (recur (inc d) (rest traces))))) 18 | 19 | (io/spit tty/stdout "TTY Demo REPL\n") 20 | (loop [] 21 | (let [command-number (count @history)] 22 | (io/spit tty/stdout (str "[ " command-number " ] < " )) 23 | (let [input (io/read-line tty/stdin) 24 | res (try (eval (read-string input)) 25 | (catch e 26 | (nested-trace e)))] 27 | (io/spit tty/stdout (str "[ " command-number " ] > " res "\n")) 28 | (swap! history conj input) 29 | (recur)))) 30 | -------------------------------------------------------------------------------- /find_externals_name.py: -------------------------------------------------------------------------------- 1 | from rpython.translator.platform import platform 2 | 3 | print "https://github.com/pixie-lang/external-deps/releases/download/1.0/externals-"+platform.name+".tar.bz2" -------------------------------------------------------------------------------- /generate-docs.pxi: -------------------------------------------------------------------------------- 1 | (ns pixie.generate-docs 2 | (:require [pixie.io :as io] 3 | [pixie.string :as string])) 4 | 5 | (let [[namespace] program-arguments] 6 | 7 | (println "==============") 8 | (println (name namespace)) 9 | (println "==============") 10 | 11 | (load-ns (symbol namespace)) 12 | (println) 13 | 14 | ;;Should be sorting the map 15 | ;;Like so: (sort-by first map) 16 | ;;However, I'm holding off until sort is properly supported 17 | (doseq [[k v] (ns-map (the-ns namespace))] 18 | (println (name k)) 19 | (println "====================================") 20 | (println) 21 | 22 | (if-let [m (meta @v)] 23 | (do 24 | ;(println m) 25 | (if-let [doc (:doc m)];; 26 | (println doc) 27 | (println "No doc available :(")) 28 | (println) 29 | 30 | (when-let (examples (:examples m)) 31 | (println "**Examples:**") 32 | (doseq [[code _ result] examples] 33 | (println) 34 | (println code) 35 | (println) 36 | (when (not (nil? result)) 37 | (println "=> " result))) 38 | (println)) 39 | 40 | (when-let (signatures (:signatures m)) 41 | (println "**Signatures:**") 42 | (println) 43 | (doseq [sig signatures] 44 | (println (str "- " sig))) 45 | (println)) 46 | 47 | (when (and (:line-number m) (:file m)) 48 | (let [file (str "pixie/" (last (string/split (:file m) "/")))] 49 | (println (str "http://github.com/pixie-lang/pixie/blob/master/" 50 | file "#L" (:line-number m)))) 51 | (println))) 52 | 53 | (println "No meta data available :(")) 54 | (println))) -------------------------------------------------------------------------------- /lib_pixie.py: -------------------------------------------------------------------------------- 1 | import ctypes, sys 2 | 3 | dll = ctypes.CDLL("libpixie-vm.dylib") 4 | 5 | dll.rpython_startup_code() 6 | dll.pixie_init(sys.argv[0]) 7 | 8 | def repl(): 9 | dll.pixie_execute_source("(ns user (:require [pixie.repl :as repl])) (pixie.repl/repl)") 10 | 11 | repl() -------------------------------------------------------------------------------- /pixie/__init__.py: -------------------------------------------------------------------------------- 1 | __author__ = 'tim' 2 | -------------------------------------------------------------------------------- /pixie/async.pxi: -------------------------------------------------------------------------------- 1 | (ns pixie.async 2 | (:require [pixie.stacklets :as st])) 3 | 4 | 5 | (deftype Promise [val pending-callbacks delivered?] 6 | IDeref 7 | (-deref [self] 8 | (if delivered? 9 | val 10 | (do 11 | (st/call-cc (fn [k] 12 | (swap! pending-callbacks conj 13 | (fn [v] 14 | (st/-run-later (partial st/run-and-process k v))))))))) 15 | IFn 16 | (-invoke [self v] 17 | (assert (not delivered?) "Can only deliver a promise once") 18 | (set-field! self :val v) 19 | (set-field! self :delivered? true) 20 | (doseq [f @pending-callbacks] 21 | (f v)) 22 | (reset! pending-callbacks nil) 23 | nil)) 24 | 25 | (defn promise [] 26 | (->Promise nil (atom []) false)) 27 | 28 | (defmacro future [& body] 29 | `(let [p# (promise)] 30 | (st/spawn (p# (do ~@body))) 31 | p#)) 32 | -------------------------------------------------------------------------------- /pixie/buffers.pxi: -------------------------------------------------------------------------------- 1 | (ns pixie.buffers) 2 | 3 | (defn acopy [src src-start dest dest-start len] 4 | (loop [cnt 0] 5 | (when (< cnt len) 6 | (aset dest 7 | (+ dest-start cnt) 8 | (aget src (+ src-start cnt))) 9 | (recur (inc cnt))))) 10 | 11 | 12 | (defprotocol IMutableBuffer 13 | (remove! [this]) 14 | (add! [this]) 15 | (full? [this])) 16 | 17 | (defprotocol IResizableMutableBuffer 18 | (add-unbounded! [this val]) 19 | (resize! [this new-size])) 20 | 21 | (deftype RingBuffer [head tail length arr] 22 | IMutableBuffer 23 | (remove! [this] 24 | (when-not (zero? length) 25 | (let [x (aget arr tail)] 26 | (aset arr tail nil) 27 | (set-field! this :tail (int (rem (inc tail) (alength arr)))) 28 | (set-field! this :length (dec length)) 29 | x))) 30 | (add! [this x] 31 | (assert (< length (alength arr))) 32 | (aset arr head x) 33 | (set-field! this :head (int (rem (inc head) (alength arr)))) 34 | (set-field! this :length (inc length)) 35 | nil) 36 | 37 | (full? [this] 38 | (= length (alength arr))) 39 | 40 | 41 | IResizableMutableBuffer 42 | (resize! [this new-size] 43 | (let [new-arr (make-array new-size)] 44 | (cond 45 | (< tail head) 46 | (do (acopy arr tail new-arr 0 length) 47 | (set-field! this :tail 0) 48 | (set-field! this :head length) 49 | (set-field! this :arr new-arr)) 50 | 51 | (> tail head) 52 | (do (acopy arr tail new-arr 0 (- (alength arr) tail)) 53 | (acopy arr 0 new-arr (- (alength arr) tail) head) 54 | (set-field! this :tail 0) 55 | (set-field! this :head length) 56 | (set-field! this :arr new-arr)) 57 | 58 | 59 | (full? this) 60 | (do (acopy arr tail new-arr 0 length) 61 | (set-field! this :tail 0) 62 | (set-field! this :head length) 63 | (set-field! this :arr new-arr)) 64 | 65 | 66 | :else 67 | (do (set-field! this :tail 0) 68 | (set-field! this :head 0) 69 | (set-field! this :arr new-arr))))) 70 | 71 | (add-unbounded! [this val] 72 | (when (full? this) 73 | (resize! this (* 2 length))) 74 | (add! this val)) 75 | 76 | ICounted 77 | (-count [this] 78 | length)) 79 | 80 | 81 | (defn ring-buffer [size] 82 | (assert (> size 0) "Can't create a ring buffer of size <= 0") 83 | (->RingBuffer 0 0 0 (make-array size))) 84 | 85 | 86 | (defn fixed-buffer [size] 87 | (ring-buffer size)) 88 | 89 | 90 | (deftype DroppingBuffer [buf] 91 | IMutableBuffer 92 | (full? [this] 93 | false) 94 | (remove! [this] 95 | (remove! buf)) 96 | (add! [this val] 97 | (when-not (full? buf) 98 | (add! buf val))) 99 | 100 | ICounted 101 | (-count [this] 102 | (count buf))) 103 | 104 | (defn dropping-buffer [size] 105 | (->DroppingBuffer (ring-buffer size))) 106 | 107 | 108 | (deftype SlidingBuffer [buf] 109 | IMutableBuffer 110 | (full? [this] 111 | false) 112 | (remove! [this] 113 | (remove! buf)) 114 | (add! [this val] 115 | (when (full? buf) 116 | (remove! buf)) 117 | (add! buf val)) 118 | 119 | ICounted 120 | (-count [this] 121 | (count buf))) 122 | 123 | (defn sliding-buffer [size] 124 | (->SlidingBuffer (ring-buffer size))) 125 | 126 | (defn empty-buffer? [buf] 127 | (= (count buf) 0)) 128 | 129 | 130 | (deftype NullBuffer [] 131 | IMutableBuffer 132 | (full? [this] 133 | true) 134 | ICounted 135 | (-count [this] 0)) 136 | 137 | (def null-buffer (->NullBuffer)) 138 | 139 | (extend -reduce IMutableBuffer 140 | (fn [buf f acc] 141 | (loop [acc acc] 142 | (if (reduced? acc) 143 | @acc 144 | (if (pos? (count buf)) 145 | (recur (f acc (remove! buf))) 146 | acc))))) 147 | -------------------------------------------------------------------------------- /pixie/csp.pxi: -------------------------------------------------------------------------------- 1 | (ns pixie.csp 2 | (:require [pixie.stacklets :as st] 3 | [pixie.buffers :as b] 4 | [pixie.channels :as chans])) 5 | 6 | (def chan chans/chan) 7 | (def timeout chans/timeout) 8 | 9 | (defn close! 10 | "Closes the channel, future writes will be rejected, future reads will 11 | drain the channel before returning nil." 12 | [c] 13 | (chans/-close! c)) 14 | 15 | (def -null-callback (fn [_] nil)) 16 | 17 | (defn put! 18 | "Puts the value into the channel, calling the optional callback when the operation has 19 | completed." 20 | ([c v] 21 | (chans/-put! c v -null-callback)) 22 | ([c v f] 23 | (chans/-put! c v f))) 24 | 25 | (defn take! 26 | "Takes a value from a channel, calling the provided callback when completed" 27 | ([c f] 28 | (chans/-take! c f))) 29 | 30 | (defn >! [c v] 31 | (st/call-cc (fn [k] 32 | (chans/-put! c v (partial st/run-and-process k))))) 33 | 34 | (defn > m 13 | (transduce (comp (map (fn [[k v]] (string/interp "$(write-string k)$: $(write-string v)$"))) 14 | (interpose ", ")) 15 | string-builder)) 16 | "}")) 17 | 18 | (defn- write-sequential [xs] 19 | (str "[" 20 | (->> xs 21 | (transduce (comp (map write-string) 22 | (interpose ", ")) 23 | string-builder)) 24 | "]")) 25 | 26 | (defn- write-str [s] 27 | (string/interp "\"$s$\"")) 28 | 29 | (extend-protocol IToJSON 30 | Character (write-string [this] (write-str this)) 31 | Cons (write-string [this] (write-sequential this)) 32 | EmptyList (write-string [this] (write-sequential this)) 33 | Nil (write-string [_] "null") 34 | Number (write-string [this] (str this)) 35 | Keyword (write-string [this] (write-str (name this))) 36 | LazySeq (write-string [this] (write-sequential this)) 37 | IMap (write-string [this] (write-map this)) 38 | IVector (write-string [this] (write-sequential this)) 39 | PersistentList (write-string [this] (write-sequential this)) 40 | Ratio (write-string [this] (str (float this))) 41 | String (write-string [this] (write-str this))) 42 | -------------------------------------------------------------------------------- /pixie/io-blocking.pxi: -------------------------------------------------------------------------------- 1 | (ns pixie.io-blocking 2 | (:require [pixie.streams :as st :refer :all] 3 | [pixie.io.common :as common])) 4 | 5 | (def fopen (ffi-fn libc "fopen" [CCharP CCharP] CVoidP)) 6 | (def fseek (ffi-fn libc "fseek" [CVoidP CInt CInt] CInt)) 7 | (def ftell (ffi-fn libc "ftell" [CVoidP] CInt)) 8 | (def -rewind (ffi-fn libc "rewind" [CVoidP] CVoidP)) 9 | (def fread (ffi-fn libc "fread" [CVoidP CInt CInt CVoidP] CInt)) 10 | (def fgetc (ffi-fn libc "fgetc" [CVoidP] CInt)) 11 | (def fputc (ffi-fn libc "fputc" [CInt CVoidP] CInt)) 12 | (def fwrite (ffi-fn libc "fwrite" [CVoidP CInt CInt CVoidP] CInt)) 13 | (def fclose (ffi-fn libc "fclose" [CVoidP] CInt)) 14 | (def popen (ffi-fn libc "popen" [CCharP CCharP] CVoidP)) 15 | (def pclose (ffi-fn libc "pclose" [CVoidP] CInt)) 16 | (def EOF -1) 17 | 18 | (deftype FileStream [fp] 19 | IInputStream 20 | (read [this buffer len] 21 | (assert (>= (buffer-capacity buffer) len) 22 | "Not enough capacity in the buffer") 23 | (let [read-count (fread buffer 1 len fp)] 24 | (set-buffer-count! buffer read-count) 25 | read-count)) 26 | (read-byte [this] 27 | (fgetc buffer)) 28 | ISeekableStream 29 | (seek [this pos] 30 | (fseek fp pos 0)) 31 | (rewind [this] 32 | (-rewind fp)) 33 | IDisposable 34 | (-dispose! [this] 35 | (fclose fp)) 36 | IReduce 37 | (-reduce [this f init] 38 | (common/stream-reducer this f init))) 39 | 40 | (defn open-read 41 | {:doc "Opens a file for reading. Returns an IInputStream." 42 | :added "0.1"} 43 | [filename] 44 | (assert (string? filename) "Filename must be a string") 45 | (->FileStream (fopen filename "r"))) 46 | 47 | (defn read-line 48 | "Reads one line from input-stream for each invocation. 49 | Returns nil when all lines have been read." 50 | [input-stream] 51 | (let [line-feed (into #{} (map int [\newline \return])) 52 | buf (buffer 1)] 53 | (loop [acc []] 54 | (let [len (read input-stream buf 1)] 55 | (cond 56 | (and (pos? len) (not (line-feed (first buf)))) 57 | (recur (conj acc (first buf))) 58 | 59 | (and (zero? len) (empty? acc)) nil 60 | 61 | :else (apply str (map char acc))))))) 62 | 63 | (defn line-seq 64 | "Returns the lines of text from input-stream as a lazy sequence of strings. 65 | input-stream must implement IInputStream." 66 | [input-stream] 67 | (when-let [line (read-line input-stream)] 68 | (cons line (lazy-seq (line-seq input-stream))))) 69 | 70 | (deftype FileOutputStream [fp] 71 | IByteOutputStream 72 | (write-byte [this val] 73 | (assert (integer? val) "Value must be a int") 74 | (fputc val fp)) 75 | IOutputStream 76 | (write [this buffer] 77 | (fwrite buffer 1 (count buffer) fp)) 78 | IDisposable 79 | (-dispose! [this] 80 | (fclose fp))) 81 | 82 | (defn file-output-rf [filename] 83 | (let [fp (->FileOutputStream (fopen filename "w"))] 84 | (fn ([] 0) 85 | ([cnt] (dispose! fp) nil) 86 | ([cnt chr] 87 | (assert (integer? chr)) 88 | (let [written (write-byte fp chr)] 89 | (if (= written EOF) 90 | (reduced cnt) 91 | (+ cnt written))))))) 92 | 93 | (defn spit [filename val] 94 | (transduce (map int) 95 | (file-output-rf filename) 96 | (str val))) 97 | 98 | (defn slurp [filename] 99 | (let [c (->FileStream (fopen filename "r")) 100 | result (transduce 101 | (map char) 102 | string-builder 103 | c)] 104 | (dispose! c) 105 | result)) 106 | 107 | (defn slurp-stream [stream] 108 | (let [c stream 109 | result (transduce 110 | (map char) 111 | string-builder 112 | c)] 113 | (dispose! c) 114 | result)) 115 | 116 | (deftype ProcessInputStream [fp] 117 | IInputStream 118 | (read [this buffer len] 119 | (assert (<= (buffer-capacity buffer) len) 120 | "Not enough capacity in the buffer") 121 | (let [read-count (fread buffer 1 len fp)] 122 | (set-buffer-count! buffer read-count) 123 | read-count)) 124 | (read-byte [this] 125 | (fgetc fp)) 126 | IDisposable 127 | (-dispose! [this] 128 | (pclose fp)) 129 | IReduce 130 | (-reduce [this f init] 131 | (common/stream-reducer this f init))) 132 | 133 | (defn popen-read 134 | {:doc "Opens a file for reading. Returns an IInputStream." 135 | :added "0.1"} 136 | [command] 137 | (assert (string? command) "Command must be a string") 138 | (->ProcessInputStream (popen command "r"))) 139 | 140 | (defn run-command [command] 141 | (let [c (->ProcessInputStream (popen command "r")) 142 | result (transduce 143 | (map char) 144 | string-builder 145 | c)] 146 | (dispose! c) 147 | result)) 148 | -------------------------------------------------------------------------------- /pixie/io/common.pxi: -------------------------------------------------------------------------------- 1 | (ns pixie.io.common 2 | "Common functionality for handling IO" 3 | (:require [pixie.streams :refer :all])) 4 | 5 | (def DEFAULT-BUFFER-SIZE 1024) 6 | 7 | (defn stream-reducer [this f init] 8 | (let [buf (buffer DEFAULT-BUFFER-SIZE) 9 | rrf (preserving-reduced f)] 10 | (loop [acc init] 11 | (let [read-count (read this buf DEFAULT-BUFFER-SIZE)] 12 | (if (> read-count 0) 13 | (let [result (reduce rrf acc buf)] 14 | (if (not (reduced? result)) 15 | (recur result) 16 | @result)) 17 | acc))))) 18 | -------------------------------------------------------------------------------- /pixie/io/tcp.pxi: -------------------------------------------------------------------------------- 1 | (ns pixie.io.tcp 2 | (:require [pixie.stacklets :as st] 3 | [pixie.streams :refer [IInputStream read IOutputStream write]] 4 | [pixie.io.common :as common] 5 | [pixie.uv :as uv] 6 | [pixie.io.uv-common :as uv-common] 7 | [pixie.ffi :as ffi])) 8 | 9 | (defrecord TCPServer [ip port on-connect uv-server bind-addr on-connection-cb] 10 | IDisposable 11 | (-dispose! [this] 12 | (uv/uv_close uv-server st/close_cb) 13 | (dispose! @on-connection-cb) 14 | (dispose! bind-addr))) 15 | 16 | (deftype TCPStream [uv-client uv-write-buf] 17 | IInputStream 18 | (read [this buffer len] 19 | (uv-common/cb-stream-reader uv-client buffer len)) 20 | IOutputStream 21 | (write [this buffer] 22 | (uv-common/cb-stream-writer uv-client uv-write-buf buffer)) 23 | IDisposable 24 | (-dispose! [this] 25 | (dispose! uv-write-buf) 26 | (uv/uv_close uv-client st/close_cb)) 27 | IReduce 28 | (-reduce [this f init] 29 | (common/stream-reducer this f init))) 30 | 31 | (defn launch-tcp-client-from-server [svr] 32 | (assert (instance? TCPServer svr) "Requires a TCPServer as the first argument") 33 | (let [client (uv/uv_tcp_t)] 34 | (uv/uv_tcp_init (uv/uv_default_loop) client) 35 | (if (= 0 (uv/uv_accept (:uv-server svr) client)) 36 | (do (st/spawn-from-non-stacklet #((:on-connect svr) 37 | (->TCPStream client (uv/uv_buf_t)))) 38 | svr) 39 | (do (uv/uv_close client nil) 40 | svr)))) 41 | 42 | (defn tcp-server 43 | "Creates a TCP server on the given ip (as a string) and port (as an integer). Returns a TCPServer that can be 44 | shutdown with dispose!. on-connection is a function that will be passed a TCPStream for each connecting client." 45 | [ip port on-connection] 46 | (assert (string? ip) "Ip should be a string") 47 | (assert (integer? port) "Port should be a int") 48 | 49 | (let [server (uv/uv_tcp_t) 50 | bind-addr (uv/sockaddr_in) 51 | _ (uv/throw-on-error (uv/uv_ip4_addr ip port bind-addr)) 52 | on-new-connection (atom nil) 53 | tcp-server (->TCPServer ip port on-connection server bind-addr on-new-connection)] 54 | (reset! on-new-connection 55 | (ffi/ffi-prep-callback 56 | uv/uv_connection_cb 57 | (fn [server status] 58 | (launch-tcp-client-from-server tcp-server)))) 59 | (uv/uv_tcp_init (uv/uv_default_loop) server) 60 | (uv/uv_tcp_bind server bind-addr 0) 61 | (uv/throw-on-error (uv/uv_listen server 128 @on-new-connection)) 62 | (st/yield-control) 63 | tcp-server)) 64 | 65 | 66 | (defn tcp-client 67 | "Creates a TCP connection to the given ip (as a string) and port (an integer). Returns a TCPStream." 68 | [ip port] 69 | (let [client-addr (uv/sockaddr_in) 70 | uv-connect (uv/uv_connect_t) 71 | client (uv/uv_tcp_t) 72 | cb (atom nil)] 73 | (uv/throw-on-error (uv/uv_ip4_addr ip port client-addr)) 74 | (uv/uv_tcp_init (uv/uv_default_loop) client) 75 | (st/call-cc (fn [k] 76 | (reset! cb (ffi/ffi-prep-callback 77 | uv/uv_connect_cb 78 | (fn [_ status] 79 | (try 80 | (dispose! @cb) 81 | (dispose! uv-connect) 82 | (dispose! client-addr) 83 | (st/run-and-process k (or (st/exception-on-uv-error status) 84 | (->TCPStream client (uv/uv_buf_t)))) 85 | (catch ex 86 | (println ex)))))) 87 | (uv/uv_tcp_connect uv-connect client client-addr @cb))))) 88 | -------------------------------------------------------------------------------- /pixie/io/tty.pxi: -------------------------------------------------------------------------------- 1 | (ns pixie.io.tty 2 | (:require [pixie.stacklets :as st] 3 | [pixie.streams :refer [IInputStream read IOutputStream write]] 4 | [pixie.uv :as uv] 5 | [pixie.io.common :as common] 6 | [pixie.io.uv-common :as uv-common] 7 | [pixie.system :as sys])) 8 | 9 | (deftype TTYInputStream [uv-client] 10 | IInputStream 11 | (read [this buf len] 12 | (uv-common/cb-stream-reader uv-client buf len)) 13 | IDisposable 14 | (-dispose! [this] 15 | (uv/uv_close uv-client st/close_cb)) 16 | IReduce 17 | (-reduce [this f init] 18 | (common/stream-reducer this f init))) 19 | 20 | (deftype TTYOutputStream [uv-client uv-write-buf] 21 | IOutputStream 22 | (write [this buffer] 23 | (uv-common/cb-stream-writer uv-client uv-write-buf buffer)) 24 | IDisposable 25 | (-dispose! [this] 26 | (dispose! uv-write-buf) 27 | (uv/uv_close uv-client st/close_cb))) 28 | 29 | (defn tty-output-stream [fd] 30 | ;(assert (= uv/UV_TTY (uv/uv_guess_handle fd)) "fd is not a TTY") 31 | (let [buf (uv/uv_buf_t) 32 | tty (uv/uv_tty_t)] 33 | (uv/uv_tty_init (uv/uv_default_loop) tty fd 0) 34 | (->TTYOutputStream tty buf))) 35 | 36 | (defn tty-input-stream [fd] 37 | ;(assert (= uv/UV_TTY (uv/uv_guess_handle fd)) "fd is not a TTY") 38 | (let [tty (uv/uv_tty_t)] 39 | (uv/uv_tty_init (uv/uv_default_loop) tty fd 0) 40 | (->TTYInputStream tty))) 41 | 42 | (def stdin (tty-input-stream sys/stdin)) 43 | (def stdout (tty-output-stream sys/stdout)) 44 | (def stderr (tty-output-stream sys/stderr)) 45 | -------------------------------------------------------------------------------- /pixie/io/uv-common.pxi: -------------------------------------------------------------------------------- 1 | (ns pixie.io.uv-common 2 | (:require [pixie.stacklets :as st] 3 | [pixie.uv :as uv] 4 | [pixie.ffi :as ffi])) 5 | 6 | (defn cb-stream-reader [uv-client buffer len] 7 | (assert (<= (buffer-capacity buffer) len) 8 | "Not enough capacity in the buffer") 9 | (let [alloc-cb (uv/-prep-uv-buffer-fn buffer len) 10 | read-cb (atom nil)] 11 | (st/call-cc (fn [k] 12 | (reset! read-cb (ffi/ffi-prep-callback 13 | uv/uv_read_cb 14 | (fn [stream nread uv-buf] 15 | (set-buffer-count! buffer nread) 16 | (try 17 | (dispose! alloc-cb) 18 | (dispose! @read-cb) 19 | ;(dispose! uv-buf) 20 | (uv/uv_read_stop stream) 21 | (st/run-and-process k (or 22 | (st/exception-on-uv-error nread) 23 | nread)) 24 | (catch ex 25 | (println ex)))))) 26 | (uv/uv_read_start uv-client alloc-cb @read-cb))))) 27 | 28 | (defn cb-stream-writer 29 | [uv-client uv-write-buf buffer] 30 | (let [write-cb (atom nil) 31 | uv_write (uv/uv_write_t)] 32 | (ffi/set! uv-write-buf :base buffer) 33 | (ffi/set! uv-write-buf :len (count buffer)) 34 | (st/call-cc 35 | (fn [k] 36 | (reset! write-cb (ffi/ffi-prep-callback 37 | uv/uv_write_cb 38 | (fn [req status] 39 | (try 40 | (dispose! @write-cb) 41 | ;(uv/uv_close uv_write st/close_cb) 42 | (st/run-and-process k status) 43 | (catch ex 44 | (println ex)))))) 45 | (uv/uv_write uv_write uv-client uv-write-buf 1 @write-cb))))) 46 | -------------------------------------------------------------------------------- /pixie/lib_pixie.py: -------------------------------------------------------------------------------- 1 | import ctypes 2 | import os.path 3 | 4 | dll_name = os.path.join(os.path.dirname(__file__), "libpixie-vm.dylib") 5 | print("Loading Pixie from " + dll_name) 6 | dll = ctypes.CDLL(dll_name) 7 | 8 | dll.rpython_startup_code() 9 | dll.pixie_init("/Users/tim/oss/pixie/pixie-vm".encode("ascii")) 10 | 11 | 12 | def repl(): 13 | dll.pixie_execute_source("(ns user (:require [pixie.repl :as repl])) (pixie.repl/repl)".encode('ascii')) 14 | 15 | #repl() -------------------------------------------------------------------------------- /pixie/math.pxi: -------------------------------------------------------------------------------- 1 | (ns pixie.math 2 | (:require [pixie.ffi-infer :as i] 3 | [pixie.string :as s])) 4 | 5 | (i/with-config {:library "m" 6 | :cxx-flags ["-lm"] 7 | :includes ["math.h"]} 8 | (i/defconst M_E) 9 | (i/defconst M_LOG2E) 10 | (i/defconst M_LOG10E) 11 | (i/defconst M_LN2) 12 | (i/defconst M_LN10) 13 | (i/defconst M_PI) 14 | (i/defconst M_PI_2) 15 | (i/defconst M_PI_4) 16 | (i/defconst M_1_PI) 17 | (i/defconst M_2_PI) 18 | (i/defconst M_2_SQRTPI) 19 | (i/defconst M_SQRT2) 20 | (i/defconst M_SQRT1_2) 21 | 22 | (i/defcfn nan) 23 | (i/defcfn ceil) 24 | (i/defcfn floor) 25 | (i/defcfn nearbyint) 26 | (i/defcfn rint) 27 | (i/defcfn lround) 28 | (i/defcfn llrint) 29 | (i/defcfn llround) 30 | (i/defcfn trunc) 31 | 32 | (i/defcfn fmod) 33 | (i/defcfn remainder) 34 | (i/defcfn remquo) 35 | 36 | (i/defcfn fdim) 37 | (i/defcfn fmax) 38 | (i/defcfn fmin) 39 | 40 | (i/defcfn fma) 41 | 42 | (i/defcfn fabs) 43 | (i/defcfn sqrt) 44 | (i/defcfn cbrt) 45 | (i/defcfn hypot) 46 | 47 | (i/defcfn exp) 48 | (i/defcfn exp2) 49 | (if-not (s/starts-with? pixie.platform/name "darwin") 50 | (i/defcfn exp10)) 51 | (i/defcfn expm1) 52 | 53 | (i/defcfn log) 54 | (i/defcfn log2) 55 | (i/defcfn log10) 56 | (i/defcfn log1p) 57 | 58 | (i/defcfn logb) 59 | (i/defcfn ilogb) 60 | 61 | (i/defcfn modf) 62 | (i/defcfn frexp) 63 | 64 | (i/defcfn ldexp) 65 | (i/defcfn scalbn) 66 | (i/defcfn scalbln) 67 | 68 | (i/defcfn pow) 69 | 70 | (i/defcfn cos) 71 | (i/defcfn sin) 72 | (i/defcfn tan) 73 | 74 | (i/defcfn cosh) 75 | (i/defcfn sinh) 76 | (i/defcfn tanh) 77 | 78 | (i/defcfn acos) 79 | (i/defcfn asin) 80 | (i/defcfn atan) 81 | (i/defcfn atan2) 82 | 83 | (i/defcfn acosh) 84 | (i/defcfn asinh) 85 | (i/defcfn atanh) 86 | 87 | (i/defcfn tgamma) 88 | (i/defcfn lgamma) 89 | 90 | (i/defcfn j0) 91 | (i/defcfn j1) 92 | (i/defcfn jn) 93 | (i/defcfn y0) 94 | (i/defcfn y1) 95 | (i/defcfn yn) 96 | 97 | (i/defcfn erf) 98 | (i/defcfn erfc)) 99 | 100 | (if (s/starts-with? pixie.platform/name "darwin") 101 | (defn exp10 [x] (pow 10.0 x))) 102 | -------------------------------------------------------------------------------- /pixie/parser/json.pxi: -------------------------------------------------------------------------------- 1 | (ns pixie.parser.json 2 | (:require [pixie.parser :refer :all] 3 | [pixie.stdlib :as std])) 4 | 5 | 6 | 7 | ;; Basic numeric parser. Supports integers (1, 2, 43), decimals (0.1, 1.1, 1000.11) and exponents (1e42, 1E-2) 8 | (defparser NumberParser [] 9 | NUMBER (and (maybe \-) 10 | -> sign 11 | 12 | (or (and 13 | (parse-if (set "123456789")) -> first 14 | (zero+chars digits) -> rest 15 | <- (str first rest)) 16 | (and \0 <- "0")) 17 | -> integer-digits 18 | 19 | (maybe (and \. 20 | (one+chars digits) -> digits 21 | <- digits)) 22 | -> fraction-digits 23 | 24 | 25 | (maybe (and (parse-if (set "eE")) 26 | (maybe (parse-if (set "-+"))) -> exp-sign 27 | (one+chars digits) -> exp-digits 28 | <- [(std/or exp-sign "") exp-digits])) 29 | -> exp-data 30 | 31 | <- (std/read-string (str (std/or sign "") 32 | integer-digits 33 | (if fraction-digits (str "." fraction-digits) "") 34 | (if exp-data (apply str "E" exp-data) ""))))) 35 | 36 | (def valid-escape-chars 37 | {\\ \\ 38 | \" \" 39 | \/ \/ 40 | \b \backspace 41 | \f \formfeed 42 | \n \newline 43 | \r \return 44 | \t \tab}) 45 | 46 | 47 | ;; Defines a JSON escaped string parser. Supports all the normal \n \f \r stuff as well 48 | ;; as \uXXXX unicode characters 49 | (defparser EscapedStringParser [] 50 | CHAR (or (and \\ 51 | (one-of valid-escape-chars) -> char 52 | <- (valid-escape-chars char)) 53 | 54 | (and \\ 55 | \u 56 | digits -> d1 57 | digits -> d2 58 | digits -> d3 59 | digits -> d4 60 | <- (char (std/read-string (str "0x" d1 d2 d3 d4)))) 61 | 62 | (parse-if #(not= % \"))) 63 | 64 | STRING (and \" 65 | (zero+chars CHAR) -> s 66 | \" 67 | <- s)) 68 | 69 | ;; Basic JSON parser 70 | (defparser JSONParser [NumberParser EscapedStringParser] 71 | 72 | NULL (sequence "null" <- nil) 73 | TRUE (sequence "true" <- true) 74 | FALSE (sequence "false" <- false) 75 | ARRAY (and \[ 76 | (eat whitespace) 77 | (zero+ (and ENTRY -> e 78 | (maybe \,) 79 | <- e)) -> items 80 | (eat whitespace) 81 | (eat whitespace) 82 | \] 83 | <- items) 84 | MAP-ENTRY (and (eat whitespace) 85 | STRING -> key 86 | (eat whitespace) 87 | \: 88 | ENTRY -> value 89 | (maybe \,) 90 | <- [key value]) 91 | MAP (and \{ 92 | (zero+ MAP-ENTRY) -> items 93 | (eat whitespace) 94 | \} 95 | <- (apply hashmap (apply concat items))) 96 | ENTRY (and 97 | (eat whitespace) 98 | (or NUMBER MAP STRING NULL TRUE FALSE ARRAY) -> val 99 | (eat whitespace) 100 | <- val) 101 | ENTRY-AT-END (and ENTRY -> e 102 | (eat whitespace) 103 | end 104 | <- e)) 105 | 106 | (defn read-string [s] 107 | (let [c (string-cursor s) 108 | result ((:ENTRY-AT-END JSONParser) c)] 109 | (if (failure? result) 110 | (println (current c) (snapshot c)) 111 | result))) 112 | -------------------------------------------------------------------------------- /pixie/repl.pxi: -------------------------------------------------------------------------------- 1 | (ns pixie.repl 2 | (:require [pixie.stacklets :as st] 3 | [pixie.io :as io] 4 | [pixie.ffi-infer :as f])) 5 | 6 | (f/with-config {:library "edit" 7 | :includes ["editline/readline.h"]} 8 | (f/defcfn readline) 9 | (f/defcfn add_history)) 10 | 11 | 12 | (defn repl [] 13 | (let [rdr (reader-fn (fn [] 14 | (let [prompt (if (= 0 pixie.stdlib/*reading-form*) 15 | (str (name pixie.stdlib/*ns*) " => ") 16 | "") 17 | line (st/apply-blocking readline prompt)] 18 | (if line 19 | (do 20 | (add_history line) 21 | (str line "\n")) 22 | ""))))] 23 | (loop [] 24 | (try (let [form (read rdr false)] 25 | (if (or (= form eof) (= form :exit)) 26 | (exit 0) 27 | (let [x (eval form)] 28 | (pixie.stdlib/-push-history x) 29 | (prn x)))) 30 | (catch ex 31 | (pixie.stdlib/-set-*e ex) 32 | (println "ERROR: \n" ex))) 33 | (recur)))) 34 | -------------------------------------------------------------------------------- /pixie/set.pxi: -------------------------------------------------------------------------------- 1 | (ns pixie.set 2 | (:require [pixie.stdlib :as std])) 3 | 4 | (defn- -must-be-set [s] 5 | (if (set? s) 6 | s 7 | (throw [:pixie.stdlib/InvalidArgumentException 8 | (str "Not a set: " s)]))) 9 | 10 | (defn- -must-be-sets [& sets] 11 | (doseq [set sets] 12 | (-must-be-set set))) 13 | 14 | (defn- -union [s t] 15 | (-must-be-sets s t) 16 | (if (< (count s) (count t)) 17 | (into t s) 18 | (into s t))) 19 | 20 | (defn union 21 | "Returns a set that is the union of the input sets." 22 | ([] #{}) 23 | ([s] (-must-be-set s)) 24 | ([s t] 25 | (-union s t)) 26 | ([s t & sets] 27 | (reduce -union (-union s t) sets))) 28 | 29 | (defn- -difference [s t] 30 | (-must-be-sets s t) 31 | (into #{} (filter #(not (contains? t %))) s)) 32 | 33 | (defn difference 34 | "Returns a set that is the difference of the input sets." 35 | ([] #{}) 36 | ([s] (-must-be-set s)) 37 | ([s t] 38 | (-difference s t)) 39 | ([s t & sets] 40 | (reduce -difference (-difference s t) sets))) 41 | 42 | (defn- -intersection [s t] 43 | (-must-be-set s) 44 | (-must-be-set t) 45 | (into #{} (filter #(contains? s %)) t)) 46 | 47 | (defn intersection 48 | "Returns a set that is the intersection of the input sets." 49 | ([] #{}) 50 | ([s] (-must-be-set s)) 51 | ([s t] 52 | (-intersection s t)) 53 | ([s t & sets] 54 | (reduce -intersection (-intersection s t) sets))) 55 | 56 | (defn subset? 57 | "Returns true if the first set is a subset of the second." 58 | [s t] 59 | (-must-be-sets s t) 60 | (and (<= (count s) (count t)) 61 | (every? #(contains? t %) (vec s)))) 62 | 63 | (defn strict-subset? 64 | "Returns true if the first set is a subset of the second." 65 | [s t] 66 | (-must-be-sets s t) 67 | (and (< (count s) (count t)) 68 | (subset? s t))) 69 | 70 | (defn superset? 71 | "Returns true if the first set is a superset of the second." 72 | [s t] 73 | (subset? t s)) 74 | 75 | (defn strict-superset? 76 | "Returns true if the first set is a strict superset of the second." 77 | [s t] 78 | (strict-subset? t s)) 79 | -------------------------------------------------------------------------------- /pixie/stacklets.pxi: -------------------------------------------------------------------------------- 1 | (ns pixie.stacklets 2 | (:require [pixie.uv :as uv] 3 | [pixie.ffi :as ffi])) 4 | 5 | ;; If we don't do this, compiling this file doesn't work since the def will clear out 6 | ;; the existing value. 7 | 8 | (when (undefined? (var stacklet-loop-h)) 9 | (def stacklet-loop-h (atom nil)) 10 | (def running-threads (atom 0)) 11 | (def main-loop-running? (atom false)) 12 | (def main-loop-lock (-create-lock)) 13 | (-acquire-lock main-loop-lock true)) 14 | 15 | 16 | 17 | ;; Yield 18 | 19 | (defrecord ThrowException [ex]) 20 | 21 | (defn run-and-process 22 | ([k] 23 | (run-and-process k nil)) 24 | ([k val] 25 | (let [[h f] (k val)] 26 | (f h)))) 27 | 28 | (defn exception-on-uv-error [result] 29 | (when (neg? result) 30 | (->ThrowException (str "UV Error: " (uv/uv_err_name result))))) 31 | 32 | 33 | (defn call-cc [f] 34 | (let [frames (-get-current-var-frames nil) 35 | [h val] (@stacklet-loop-h f)] 36 | (reset! stacklet-loop-h h) 37 | (-set-current-var-frames nil frames) 38 | (if (instance? ThrowException val) 39 | (throw [::Exception (:ex val)]) 40 | val))) 41 | 42 | (defn -run-later [f] 43 | (let [a (uv/uv_async_t) 44 | cb (atom nil)] 45 | (reset! cb (ffi/ffi-prep-callback uv/uv_async_cb 46 | (fn [handle] 47 | (try 48 | (uv/uv_close a close_cb) 49 | (-dispose! @cb) 50 | (f) 51 | (catch ex (println ex)))))) 52 | (uv/uv_async_init (uv/uv_default_loop) a @cb) 53 | (uv/uv_async_send a) 54 | (when (not @main-loop-running?) 55 | (reset! main-loop-running? true) 56 | (-release-lock main-loop-lock)) 57 | nil)) 58 | 59 | 60 | (defn yield-control [] 61 | (call-cc (fn [k] 62 | (-run-later (partial run-and-process k))))) 63 | 64 | (def close_cb (ffi/ffi-prep-callback uv/uv_close_cb 65 | pixie.ffi/dispose!)) 66 | 67 | ;;; Sleep 68 | (defn sleep 69 | ([ms] 70 | (sleep ms false)) 71 | ([ms background?] 72 | (let [f (fn [k] 73 | (let [cb (atom nil) 74 | timer (uv/uv_timer_t)] 75 | (reset! cb (ffi/ffi-prep-callback uv/uv_timer_cb 76 | (fn [handle] 77 | (try 78 | (run-and-process k) 79 | (uv/uv_timer_stop timer) 80 | (-dispose! @cb) 81 | (catch ex 82 | (println ex)))))) 83 | (uv/uv_timer_init (uv/uv_default_loop) timer) 84 | (when background? 85 | (uv/uv_unref timer)) 86 | (uv/uv_timer_start timer @cb ms 0)))] 87 | (call-cc f)))) 88 | 89 | ;; Spawn 90 | (defn -spawn [start-fn] 91 | (call-cc (fn [k] 92 | (-run-later (fn [] 93 | (run-and-process (new-stacklet start-fn)))) 94 | (-run-later (partial run-and-process k))))) 95 | 96 | (defmacro spawn [& body] 97 | `(let [frames (-get-current-var-frames nil)] 98 | (-spawn (fn [h# _] 99 | (-set-current-var-frames nil frames) 100 | (try 101 | (swap! running-threads inc) 102 | (reset! stacklet-loop-h h#) 103 | (let [result# (do ~@body)] 104 | (swap! running-threads dec) 105 | (call-cc (fn [_] nil))) 106 | (catch e 107 | (println e))))))) 108 | 109 | (defmacro spawn-background [& body] 110 | `(let [frames (-get-current-var-frames nil)] 111 | (-spawn (fn [h# _] 112 | (-set-current-var-frames nil frames) 113 | (try 114 | (reset! stacklet-loop-h h#) 115 | (let [result# (do ~@body)] 116 | (call-cc (fn [_] nil))) 117 | (catch e 118 | (println e))))))) 119 | 120 | 121 | (defn spawn-from-non-stacklet [f] 122 | (let [s (new-stacklet (fn [h _] 123 | (try 124 | (reset! stacklet-loop-h h) 125 | (swap! running-threads inc) 126 | (f) 127 | (swap! running-threads dec) 128 | (call-cc (fn [_] nil)) 129 | (catch e 130 | (println e)))))] 131 | (-run-later 132 | (fn [] 133 | (run-and-process s))))) 134 | 135 | 136 | (defn finalizer-loop [] 137 | (spawn-background 138 | (loop [] 139 | (-run-finalizers) 140 | (sleep 1000 true) 141 | (recur)))) 142 | 143 | 144 | (defn -with-stacklets [fn] 145 | (swap! running-threads inc) 146 | (reset! main-loop-running? true) 147 | (let [[h f] ((new-stacklet fn) nil)] 148 | (f h) 149 | (loop [] 150 | (uv/uv_run (uv/uv_default_loop) uv/UV_RUN_DEFAULT) 151 | (when (> @running-threads 0) 152 | (reset! main-loop-running? false) 153 | (-acquire-lock main-loop-lock true) 154 | (recur))))) 155 | 156 | (defmacro with-stacklets [& body] 157 | `(-with-stacklets 158 | (fn [h# _] 159 | (try 160 | (reset! stacklet-loop-h h#) 161 | (finalizer-loop) 162 | (let [result# (do ~@body)] 163 | (swap! running-threads dec) 164 | (call-cc (fn [_] nil))) 165 | (catch e 166 | (println e)))))) 167 | 168 | 169 | 170 | (defn run-with-stacklets [f] 171 | (with-stacklets 172 | (f))) 173 | 174 | (defprotocol IThreadPool 175 | (-execute [this work-fn])) 176 | 177 | ;; Super basic Thread Pool, yes, this should be improved 178 | 179 | (deftype ThreadPool [] 180 | IThreadPool 181 | (-execute [this work-fn] 182 | (-thread (fn [] (work-fn))))) 183 | 184 | (def basic-thread-pool (->ThreadPool)) 185 | 186 | (defn -run-in-other-thread [work-fn] 187 | (-execute basic-thread-pool work-fn)) 188 | 189 | 190 | (defn apply-blocking [f & args] 191 | (call-cc (fn [k] 192 | (-run-in-other-thread 193 | (fn [] 194 | (let [result (apply f args)] 195 | (-run-later (fn [] (run-and-process k result))))))))) 196 | 197 | 198 | -------------------------------------------------------------------------------- /pixie/streams.pxi: -------------------------------------------------------------------------------- 1 | (ns pixie.streams) 2 | 3 | (defprotocol IFlushableStream 4 | (flush [this] "Flushes all buffers in this stream and applies writes to any parent streams")) 5 | 6 | (defprotocol IInputStream 7 | (read [this buffer len] "Reads multiple bytes into a buffer, returns the number of bytes read")) 8 | 9 | (defprotocol IOutputStream 10 | (write [this buffer])) 11 | 12 | (defprotocol IByteInputStream 13 | (read-byte [this])) 14 | 15 | (defprotocol IByteOutputStream 16 | (write-byte [this byte])) 17 | 18 | (defprotocol ISeekableStream 19 | (position [this]) 20 | (seek [this position]) 21 | (rewind [this])) 22 | -------------------------------------------------------------------------------- /pixie/streams/utf8.pxi: -------------------------------------------------------------------------------- 1 | (ns pixie.streams.utf8 2 | (require pixie.streams :refer :all)) 3 | 4 | (defprotocol IUTF8OutputStream 5 | (write-char [this char] "Write a single character to the UTF8 stream")) 6 | 7 | (defprotocol IUTF8InputStream 8 | (read-char [this] "Read a single character from the UTF8 stream")) 9 | 10 | (deftype UTF8OutputStream [out] 11 | IUTF8OutputStream 12 | (write-char [this ch] 13 | (let [ch (int ch)] 14 | (cond 15 | (<= ch 0x7F) (write-byte out ch) 16 | (<= ch 0x7FF) (do (write-byte out (bit-or 0xC0 (bit-shift-right ch 6))) 17 | (write-byte out (bit-or 0x80 (bit-and ch 0x3F)))) 18 | (<= ch 0xFFFF) (do (write-byte out (bit-or 0xE0 (bit-shift-right ch 12))) 19 | (write-byte out (bit-or 0x80 (bit-and (bit-shift-right ch 6) 0x3F))) 20 | (write-byte out (bit-or 0x80 (bit-and ch 0x3F)))) 21 | (<= ch 0x1FFFFF) (do (write-byte out (bit-or 0xE0 (bit-shift-right ch 18))) 22 | (write-byte out (bit-or 0x80 (bit-and (bit-shift-right ch 12) 0x3F))) 23 | (write-byte out (bit-or 0x80 (bit-and (bit-shift-right ch 6) 0x3F))) 24 | (write-byte out (bit-or 0x80 (bit-and ch 0x3F)))) 25 | :else (assert false (str "Cannot encode a UTF8 character of code " ch))))) 26 | IDisposable 27 | (-dispose! [this] 28 | (dispose! out))) 29 | 30 | (deftype UTF8InputStream [in bad-char] 31 | IUTF8InputStream 32 | (read-char [this] 33 | (when-let [byte (read-byte in)] 34 | (let [[n bytes error?] 35 | (cond 36 | (>= 0x7F byte) [byte 1 false] 37 | (= 0xC0 (bit-and byte 0xE0)) [(bit-and byte 31) 2 false] 38 | (= 0xE0 (bit-and byte 0xF0)) [(bit-and byte 15) 3 false] 39 | (= 0xF0 (bit-and byte 0xF8)) [(bit-and byte 7) 4 false] 40 | (= 0xF8 (bit-and byte 0xF8)) [(bit-and byte 3) 5 true] 41 | (= 0xFC (bit-and byte 0xFE)) [(bit-and byte 1) 6 true] 42 | :else [n 1 true])] 43 | (if error? 44 | (or bad-char 45 | (throw [::invalid-character (str "Invalid UTF8 character decoded: " n)])) 46 | (loop [i (dec bytes) n n] 47 | (if (pos? i) 48 | (recur (dec i) 49 | (bit-or (bit-shift-left n 6) 50 | (bit-and (read-byte in) 0x3F))) 51 | (char n))))))) 52 | IDisposable 53 | (-dispose! [this] 54 | (dispose! in)) 55 | IReduce 56 | (-reduce [this f init] 57 | (let [rrf (preserving-reduced f)] 58 | (loop [acc init] 59 | (if-let [char (read-char this)] 60 | (let [result (rrf acc char)] 61 | (if (not (reduced? result)) 62 | (recur result) 63 | @result)) 64 | acc))))) 65 | 66 | (defn utf8-input-stream 67 | "Creates a UTF8 decoder that reads characters from the given IByteInputStream. If a bad character is found 68 | an error will be thrown, unless an optional bad-character marker character is provided." 69 | ([i] 70 | (->UTF8InputStream i nil)) 71 | ([i bad-char] 72 | (->UTF8InputStream i bad-char))) 73 | 74 | (defn utf8-output-stream 75 | "Creates a UTF8 encoder that writes characters to the given IByteOutputStream." 76 | [o] 77 | (->UTF8OutputStream o)) 78 | 79 | (defn utf8-output-stream-rf [output-stream] 80 | (let [fp (utf8-output-stream output-stream)] 81 | (fn ([] 0) 82 | ([_] (dispose! fp)) 83 | ([_ chr] 84 | (assert (char? chr)) 85 | (write-char fp chr) 86 | nil)))) 87 | -------------------------------------------------------------------------------- /pixie/streams/zlib/ffi.pxi: -------------------------------------------------------------------------------- 1 | (ns pixie.streams.zlib.ffi 2 | (:require [pixie.ffi-infer :as f] 3 | [pixie.ffi :as ffi])) 4 | 5 | (f/with-config {:library "z" 6 | :includes ["zlib.h"]} 7 | (f/defcstruct z_stream [:next_in 8 | :avail_in 9 | :total_in 10 | :next_out 11 | :avail_out 12 | :total_out 13 | 14 | :msg 15 | :state 16 | 17 | :zalloc 18 | :zfree 19 | :opaque 20 | 21 | :data_type 22 | :adler 23 | 24 | :reserved]) 25 | 26 | (f/defcfn zError) 27 | (f/defcfn zlibVersion) 28 | 29 | ;; Inflating (decompressing) 30 | (f/defcfn inflate) 31 | (f/defcfn inflateEnd) 32 | (f/defcfn inflateInit2_) 33 | 34 | ;; Defalting (compressing) 35 | (f/defcfn deflate) 36 | (f/defcfn deflateInit_) 37 | (f/defcfn deflateInit2_) 38 | (f/defcfn deflateEnd)) 39 | 40 | (def Z_OK 0) 41 | (def Z_NO_FLUSH 0) 42 | (def Z_PARTIAL_FLUSH 1) 43 | (def Z_SYNC_FLUSH 2) 44 | (def Z_FULL_FLUSH 3) 45 | (def Z_FINISH 4) 46 | (def Z_BLOCK 5) 47 | 48 | (def Z_ERRNO -1) 49 | (def Z_STREAM_ERROR -2) 50 | (def Z_DATA_ERROR -3) 51 | 52 | (def Z_NO_COMPRESSION 0) 53 | (def Z_BEST_SPEED 1) 54 | (def Z_BEST_COMPRESSION 9) 55 | (def Z_DEFAULT_COMPRESSION -1) 56 | 57 | (def Z_DEFLATED 8) 58 | 59 | (def Z_DEFAULT_STRATEGY 0) 60 | -------------------------------------------------------------------------------- /pixie/string.pxi: -------------------------------------------------------------------------------- 1 | (ns pixie.string 2 | (:require [pixie.stdlib :as std] 3 | [pixie.string.internal :as si])) 4 | 5 | ; reexport native string functions 6 | (def substring si/substring) 7 | (def index-of (comp #(if (not= -1 %) %) si/index-of)) 8 | (def split si/split) 9 | 10 | (defn split-lines 11 | "Splits on \\n or \\r\\n, the two typical line breaks." 12 | [s] 13 | (when s (apply concat (map #(split % "\n") (split s "\r\n"))))) 14 | 15 | (def ends-with? si/ends-with) 16 | (def starts-with? si/starts-with) 17 | 18 | (def trim si/trim) 19 | (def triml si/triml) 20 | (def trimr si/trimr) 21 | 22 | (def capitalize si/capitalize) 23 | (def lower-case si/lower-case) 24 | (def upper-case si/upper-case) 25 | 26 | ; TODO: There should be locale-aware variants of these values 27 | (def lower "abcdefghijklmnopqrstuvwxyz") 28 | (def upper "ABCDEFGHIJKLMNOPQRSTUVWXYZ") 29 | (def digits "0123456789") 30 | (def punctuation "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~") 31 | (def whitespace (str \space \newline \tab \backspace \formfeed \return)) 32 | (def letters (str lower upper)) 33 | (def printable (str letters digits punctuation whitespace)) 34 | (def hexdigits "0123456789abcdefABCDEF") 35 | (def octdigits "012345678") 36 | 37 | (defn trim-newline 38 | "Replace all trailing newline characters (\\r and \\n) from the end of a string." 39 | [s] 40 | (loop [index (count s)] 41 | (if (zero? index) 42 | "" 43 | (let [ch (nth s (dec index))] 44 | (if (or (= ch \newline) (= ch \return)) 45 | (recur (dec index)) 46 | (substring s 0 index)))))) 47 | 48 | (defn replace 49 | "Replace all occurrences of x in s with r." 50 | [s x r] 51 | (let [offset (if (zero? (count x)) (+ 1 (count r)) (count r))] 52 | (loop [start 0 53 | s s] 54 | (if-let [i (index-of s x start)] 55 | (recur (+ i offset) (str (substring s 0 i) r (substring s (+ i (count x))))) 56 | s)))) 57 | 58 | (defn replace-first 59 | "Replace the first occurrence of x in s with r." 60 | [s x r] 61 | (if-let [i (index-of s x)] 62 | (str (substring s 0 i) r (substring s (+ i (count x)))) 63 | s)) 64 | 65 | (defn reverse 66 | "Returns s with its characters reversed." 67 | [s] 68 | (when s 69 | (apply str (std/reverse s)))) 70 | 71 | (defn join 72 | {:doc "Join the elements of the collection using an optional separator" 73 | :examples [["(require pixie.string :as s)"] 74 | ["(s/join [1 2 3])" nil "123"] 75 | ["(s/join \", \" [1 2 3])" nil "1, 2, 3"]]} 76 | ([coll] (join "" coll)) 77 | ([separator coll] 78 | (loop [s (seq coll) 79 | res ""] 80 | (cond 81 | (nil? s) res 82 | (nil? (next s)) (str res (first s)) 83 | :else (recur (next s) (str res (first s) separator)))))) 84 | 85 | (defn blank? 86 | "True if s is nil, empty, or contains only whitespace." 87 | [s] 88 | (if s 89 | (let [white (set whitespace)] 90 | (every? white s)) 91 | true)) 92 | 93 | (defn escape 94 | "Return a new string, using cmap to escape each character ch 95 | from s as follows: 96 | 97 | If (cmap ch) is nil, append ch to the new string. 98 | If (cmap ch) is non-nil, append (str (cmap ch)) instead." 99 | [s cmap] 100 | (if (or (nil? s) 101 | (nil? cmap)) 102 | s 103 | (apply str (map #(if-let [c (cmap %)] c %) 104 | (vec s))))) 105 | 106 | (defmacro interp 107 | ; TODO: This might merit special read syntax 108 | {:doc "String interpolation." 109 | :examples [["(require pixie.string :refer [interp])"] 110 | ["(interp \"2 plus 2 is $(+ 2 2)$!\")" nil "2 plus 2 is 4!"] 111 | ["(let [x \"locals\"] (interp \"You can use arbitrary forms; for example $x$\"))" 112 | nil "You can use arbitrary forms; for example locals"] 113 | ["(interp \"$$$$ is the escape for a literal $$\")" 114 | nil "$$ is the escape for a literal $"] 115 | ]} 116 | [txt] 117 | (loop [forms [], txt txt] 118 | (cond 119 | (empty? txt) `(str ~@ forms) 120 | (starts-with? txt "$") 121 | (let [pos (or (index-of txt "$" 1) 122 | (throw "Unmatched $ in interp argument!")) 123 | form-str (subs txt 1 pos) 124 | form (if (empty? form-str) "$" 125 | (read-string form-str)) 126 | rest-str (subs txt (inc pos))] 127 | (recur (conj forms form) rest-str)) 128 | :else 129 | (let [pos (or (index-of txt "$") 130 | (count txt)) 131 | form (subs txt 0 pos) 132 | rest-str (subs txt pos)] 133 | (recur (conj forms form) rest-str))))) 134 | -------------------------------------------------------------------------------- /pixie/system.pxi: -------------------------------------------------------------------------------- 1 | (ns pixie.system 2 | (:require [pixie.ffi-infer :as i])) 3 | 4 | (i/with-config {:library "c" :imports ["stdio.h"]} 5 | (i/defconst STDIN_FILENO) 6 | (i/defconst STDOUT_FILENO) 7 | (i/defconst STDERR_FILENO)) 8 | 9 | (def fdopen (ffi-fn libc "fdopen" [CInt CCharP] CVoidP)) 10 | 11 | (def stdin STDIN_FILENO) 12 | (def stderr STDOUT_FILENO) 13 | (def stdout STDERR_FILENO) 14 | -------------------------------------------------------------------------------- /pixie/test.pxi: -------------------------------------------------------------------------------- 1 | (ns pixie.test 2 | (:require [pixie.string :as s] 3 | [pixie.fs :as fs])) 4 | 5 | (def tests (atom {})) 6 | 7 | (def ^:dynamic *stats*) 8 | 9 | (def ^:dynamic *current-test*) 10 | 11 | 12 | (defmacro deftest [nm & body] 13 | `(do (defn ~nm [] 14 | (println "Running:" (str (namespace (var ~nm)) "/" (name (var ~nm)))) 15 | (try 16 | ~@body 17 | (swap! *stats* update-in [:pass] (fnil inc 0)) 18 | (catch ex 19 | (println "while running" ~(name nm) " " (quote (do ~@body))) 20 | 21 | (swap! *stats* update-in [:fail] (fnil inc 0)) 22 | (println (str ex)) 23 | (swap! *stats* update-in [:errors] (fnil conj []) ex)))) 24 | (swap! tests assoc (symbol (str (namespace (var ~nm)) "/" (name (var ~nm)))) ~nm))) 25 | 26 | 27 | 28 | (defn run-tests [& args] 29 | (push-binding-frame!) 30 | (set! (var *stats*) (atom {:fail 0 :pass 0})) 31 | 32 | (let [match (or (first args) "") 33 | tests (transduce (comp (filter #(>= (s/index-of (str (key %1)) match) 0)) 34 | (map val)) 35 | conj 36 | @tests)] 37 | (println "Running:" (count tests) "tests") 38 | (foreach [test tests] 39 | (test))) 40 | 41 | (let [stats @*stats*] 42 | (println stats) 43 | (pop-binding-frame!) 44 | stats)) 45 | 46 | (defn load-all-tests [] 47 | (println "Looking for tests...") 48 | (let [dirs (distinct (map fs/dir @load-paths)) 49 | pxi-files (->> dirs 50 | (mapcat fs/walk-files) 51 | (filter #(fs/extension? % "pxi")) 52 | (filter #(s/starts-with? (fs/basename %) "test-")) 53 | (distinct))] 54 | (foreach [file pxi-files] 55 | (println "Loading " file) 56 | (load-file (fs/abs file))))) 57 | 58 | 59 | (defmacro assert= [x y] 60 | `(let* [xr# ~x 61 | yr# ~y] 62 | (assert (= xr# yr#) (str (show '~x xr#) " != " (show '~y yr#))))) 63 | 64 | (defmacro assert-throws? 65 | ([body] 66 | `(let [exn# (try (do ~body nil) (catch e# e#))] 67 | (assert (not (nil? exn#)) 68 | (str "Expected " (pr-str (quote ~body)) " to throw an exception")) 69 | exn#)) 70 | ([klass body] 71 | `(let [exn# (assert-throws? ~body)] 72 | (assert (= (type exn#) ~klass) 73 | (str "Expected " (pr-str (quote ~body)) 74 | " to throw exception of class " (pr-str ~klass) 75 | " but got " (pr-str (type exn#)))) 76 | exn#)) 77 | ([klass msg body] 78 | `(let [exn# (assert-throws? ~klass ~body)] 79 | (assert (= (ex-msg exn#) ~msg) 80 | (str "Expected " (pr-str (quote ~body)) 81 | " to throw exception with message " (pr-str ~msg) 82 | " but got " (pr-str (ex-msg exn#))))))) 83 | 84 | (defmacro assert [x] 85 | `(let [x# ~x] 86 | (assert x# (str '~x " is " x#)))) 87 | 88 | 89 | 90 | (defn show 91 | ([orig res] 92 | (if (= orig res) 93 | (pr-str orig) 94 | (str (pr-str orig) " = " (pr-str res))))) 95 | 96 | 97 | (defmacro assert-table [pattern expr & vals] 98 | (let [parted (partition (count pattern) vals)] 99 | `(do ~@(for [fact parted] 100 | `(let [~@(interleave pattern fact)] 101 | ~expr))))) 102 | -------------------------------------------------------------------------------- /pixie/time.pxi: -------------------------------------------------------------------------------- 1 | (ns pixie.time 2 | (:require [pixie.uv :as uv])) 3 | 4 | (defmacro time 5 | [body] 6 | `(let [start# (uv/uv_hrtime) 7 | return# ~body] 8 | (prn (str "Elapsed time: " (/ (- (uv/uv_hrtime) start#) 1000000.0) " ms")) 9 | return#)) 10 | -------------------------------------------------------------------------------- /pixie/vm/__init__.py: -------------------------------------------------------------------------------- 1 | __author__ = 'tim' 2 | -------------------------------------------------------------------------------- /pixie/vm/atom.py: -------------------------------------------------------------------------------- 1 | import pixie.vm.object as object 2 | from pixie.vm.code import extend, as_var 3 | from pixie.vm.primitives import nil 4 | import pixie.vm.stdlib as proto 5 | 6 | 7 | class Atom(object.Object): 8 | _type = object.Type(u"pixie.stdlib.Atom") 9 | 10 | def with_meta(self, meta): 11 | return Atom(self._boxed_value, meta) 12 | 13 | def meta(self): 14 | return self._meta 15 | 16 | def __init__(self, boxed_value, meta=nil): 17 | self._boxed_value = boxed_value 18 | self._meta = meta 19 | 20 | 21 | @extend(proto._reset_BANG_, Atom) 22 | def _reset(self, v): 23 | assert isinstance(self, Atom) 24 | self._boxed_value = v 25 | return v 26 | 27 | 28 | @extend(proto._deref, Atom) 29 | def _deref(self): 30 | assert isinstance(self, Atom) 31 | return self._boxed_value 32 | 33 | @extend(proto._meta, Atom) 34 | def _meta(self): 35 | assert isinstance(self, Atom) 36 | return self.meta() 37 | 38 | @extend(proto._with_meta, Atom) 39 | def _with_meta(self, meta): 40 | assert isinstance(self, Atom) 41 | return self.with_meta(meta) 42 | 43 | @as_var("atom") 44 | def atom(val=nil): 45 | return Atom(val) 46 | -------------------------------------------------------------------------------- /pixie/vm/bits.py: -------------------------------------------------------------------------------- 1 | from pixie.vm.code import as_var 2 | from pixie.vm.object import affirm 3 | 4 | from pixie.vm.numbers import Integer 5 | from rpython.rlib.rarithmetic import intmask 6 | 7 | import pixie.vm.rt as rt 8 | 9 | @as_var("bit-clear") 10 | def bit_clear(x, n): 11 | affirm(isinstance(x, Integer) and isinstance(n, Integer), u"x and n must be Integers") 12 | return rt.wrap(x.int_val() & ~(1 << n.int_val())) 13 | 14 | @as_var("bit-set") 15 | def bit_set(x, n): 16 | affirm(isinstance(x, Integer) and isinstance(n, Integer), u"x and n must be Integers") 17 | return rt.wrap(x.int_val() | (1 << n.int_val())) 18 | 19 | @as_var("bit-flip") 20 | def bit_flip(x, n): 21 | affirm(isinstance(x, Integer) and isinstance(n, Integer), u"x and n must be Integers") 22 | return rt.wrap(x.int_val() ^ (1 << n.int_val())) 23 | 24 | @as_var("bit-not") 25 | def bit_not(x): 26 | affirm(isinstance(x, Integer), u"x must be an Integer") 27 | return rt.wrap(~x.int_val()) 28 | 29 | @as_var("bit-test") 30 | def bit_test(x, n): 31 | affirm(isinstance(x, Integer) and isinstance(n, Integer), u"x and n must be Integers") 32 | return rt.wrap((x.int_val() & (1 << n.int_val())) != 0) 33 | 34 | @as_var("bit-and") 35 | def bit_and(x, y): 36 | affirm(isinstance(x, Integer) and isinstance(y, Integer), u"x and y must be Integers") 37 | return rt.wrap(x.int_val() & y.int_val()) 38 | 39 | @as_var("bit-and-not") 40 | def bit_and_not(x, y): 41 | affirm(isinstance(x, Integer) and isinstance(y, Integer), u"x and y must be Integers") 42 | return rt.wrap(x.int_val() & ~y.int_val()) 43 | 44 | @as_var("bit-or") 45 | def bit_or(x, y): 46 | affirm(isinstance(x, Integer) and isinstance(y, Integer), u"x and y must be Integers") 47 | return rt.wrap(x.int_val() | y.int_val()) 48 | 49 | @as_var("bit-xor") 50 | def bit_xor(x, y): 51 | affirm(isinstance(x, Integer) and isinstance(y, Integer), u"x and y must be Integers") 52 | return rt.wrap(x.int_val() ^ y.int_val()) 53 | 54 | @as_var("bit-shift-left") 55 | def bit_shift_left(x, n): 56 | affirm(isinstance(x, Integer) and isinstance(n, Integer), u"x and n must be Integers") 57 | return rt.wrap(x.int_val() << n.int_val()) 58 | 59 | @as_var("bit-shift-right") 60 | def bit_shift_right(x, n): 61 | affirm(isinstance(x, Integer) and isinstance(n, Integer), u"x and n must be Integers") 62 | return rt.wrap(x.int_val() >> n.int_val()) 63 | 64 | @as_var("unsigned-bit-shift-right") 65 | def unsigned_bit_shift_right(x, n): 66 | affirm(isinstance(x, Integer) and isinstance(n, Integer), u"x and n must be Integers") 67 | return rt.wrap(intmask(x.r_uint_val() >> n.int_val())) 68 | 69 | digits = "0123456789abcdefghijklmnopqrstuvwxyz" 70 | 71 | @as_var("bit-str") 72 | def bit_str(x, shift): 73 | affirm(isinstance(x, Integer) and isinstance(shift, Integer), u"x and shift must be Integers") 74 | x = x.int_val() 75 | shift = shift.int_val() 76 | 77 | buf = ['_'] * 32 78 | char_pos = 32 79 | radix = 1 << shift 80 | mask = radix - 1 81 | while True: 82 | char_pos -= 1 83 | buf[char_pos] = digits[x & mask] 84 | x = x >> shift 85 | if x == 0: 86 | break 87 | 88 | res = "" 89 | for i in range(char_pos, char_pos + 32 - char_pos): 90 | res += buf[i] 91 | return rt.wrap(res) 92 | -------------------------------------------------------------------------------- /pixie/vm/bootstrap.py: -------------------------------------------------------------------------------- 1 | from pixie.vm.code import wrap_fn 2 | 3 | @wrap_fn 4 | def bootstrap(): 5 | import pixie.vm.rt as rt 6 | assert False 7 | rt.load_ns(rt.wrap(u"pixie/stdlib.pxi")) 8 | 9 | 10 | -------------------------------------------------------------------------------- /pixie/vm/c_api.py: -------------------------------------------------------------------------------- 1 | from rpython.rlib.entrypoint import entrypoint_highlevel 2 | from rpython.rtyper.lltypesystem import rffi, lltype 3 | from rpython.rtyper.lltypesystem.lloperation import llop 4 | 5 | @entrypoint_highlevel('main', [rffi.CCHARP], c_name='pixie_init') 6 | def pypy_execute_source(ll_progname): 7 | from target import init_vm 8 | progname = rffi.charp2str(ll_progname) 9 | init_vm(progname) 10 | res = 0 11 | return rffi.cast(rffi.INT, res) 12 | 13 | @entrypoint_highlevel('main', [rffi.CCHARP], c_name='pixie_execute_source') 14 | def pypy_execute_source(ll_source): 15 | from target import EvalFn, run_with_stacklets 16 | source = rffi.charp2str(ll_source) 17 | f = EvalFn(source) 18 | run_with_stacklets.invoke([f]) 19 | res = 0 20 | return rffi.cast(rffi.INT, res) 21 | -------------------------------------------------------------------------------- /pixie/vm/cons.py: -------------------------------------------------------------------------------- 1 | import pixie.vm.object as object 2 | from pixie.vm.primitives import nil 3 | import pixie.vm.stdlib as proto 4 | from pixie.vm.code import extend, as_var 5 | 6 | 7 | class Cons(object.Object): 8 | _type = object.Type(u"pixie.stdlib.Cons") 9 | 10 | def __init__(self, head, tail, meta=nil): 11 | self._first = head 12 | self._next = tail 13 | self._meta = meta 14 | 15 | def first(self): 16 | return self._first 17 | 18 | def next(self): 19 | return self._next 20 | 21 | def meta(self): 22 | return self._meta 23 | 24 | def with_meta(self, meta): 25 | return Cons(self._first, self._next, meta) 26 | 27 | 28 | @extend(proto._first, Cons._type) 29 | def _first(self): 30 | assert isinstance(self, Cons) 31 | return self._first 32 | 33 | @extend(proto._next, Cons._type) 34 | def _next(self): 35 | assert isinstance(self, Cons) 36 | return self._next 37 | 38 | @extend(proto._seq, Cons._type) 39 | def _seq(self): 40 | assert isinstance(self, Cons) 41 | return self 42 | 43 | @extend(proto._meta, Cons) 44 | def _meta(self): 45 | assert isinstance(self, Cons) 46 | return self.meta() 47 | 48 | @extend(proto._with_meta, Cons) 49 | def _with_meta(self, meta): 50 | assert isinstance(self, Cons) 51 | return self.with_meta(meta) 52 | 53 | @as_var("cons") 54 | def cons(head, tail): 55 | return Cons(head, tail) 56 | 57 | def cons(head, tail=nil): 58 | return Cons(head, tail, nil) 59 | -------------------------------------------------------------------------------- /pixie/vm/keyword.py: -------------------------------------------------------------------------------- 1 | from pixie.vm.object import Object, Type 2 | from pixie.vm.primitives import nil 3 | from pixie.vm.string import String 4 | import pixie.vm.stdlib as proto 5 | from pixie.vm.code import extend, as_var 6 | import pixie.vm.rt as rt 7 | import pixie.vm.util as util 8 | from rpython.rlib.rarithmetic import intmask 9 | 10 | 11 | class Keyword(Object): 12 | _type = Type(u"pixie.stdlib.Keyword") 13 | def __init__(self, name): 14 | self._str = name 15 | self._w_name = None 16 | self._w_ns = None 17 | self._hash = 0 18 | 19 | def type(self): 20 | return Keyword._type 21 | 22 | def init_names(self): 23 | if self._w_name is None: 24 | s = self._str.split(u"/") 25 | if len(s) == 2: 26 | self._w_ns = rt.wrap(s[0]) 27 | self._w_name = rt.wrap(s[1]) 28 | elif len(s) == 1: 29 | self._w_name = rt.wrap(s[0]) 30 | self._w_ns = nil 31 | else: 32 | self._w_ns = rt.wrap(s[0]) 33 | self._w_name = rt.wrap(u"/".join(s[1:])) 34 | 35 | 36 | class KeywordCache(object): 37 | def __init__(self): 38 | self._cache = {} 39 | 40 | def intern(self, nm): 41 | kw = self._cache.get(nm, None) 42 | 43 | if kw is None: 44 | kw = Keyword(nm) 45 | self._cache[nm] = kw 46 | 47 | return kw 48 | 49 | _kw_cache = KeywordCache() 50 | 51 | def keyword(nm, ns=None): 52 | if ns: 53 | nm = u"/".join([ns, nm]) 54 | return _kw_cache.intern(nm) 55 | 56 | 57 | @extend(proto._name, Keyword) 58 | def _name(self): 59 | assert isinstance(self, Keyword) 60 | self.init_names() 61 | return self._w_name 62 | 63 | @extend(proto._namespace, Keyword) 64 | def _namespace(self): 65 | assert isinstance(self, Keyword) 66 | self.init_names() 67 | return self._w_ns 68 | 69 | @extend(proto._hash, Keyword) 70 | def _hash(self): 71 | assert isinstance(self, Keyword) 72 | if self._hash == 0: 73 | self._hash = util.hash_unencoded_chars(self._str) 74 | return rt.wrap(intmask(self._hash)) 75 | 76 | @as_var("keyword") 77 | def _keyword(s): 78 | if not isinstance(s, String): 79 | from pixie.vm.object import runtime_error 80 | runtime_error(u"Keyword name must be a string") 81 | return keyword(s._str) 82 | -------------------------------------------------------------------------------- /pixie/vm/lazy_seq.py: -------------------------------------------------------------------------------- 1 | import pixie.vm.object as object 2 | from pixie.vm.primitives import nil 3 | import pixie.vm.stdlib as proto 4 | from pixie.vm.code import extend, as_var 5 | import pixie.vm.rt as rt 6 | import rpython.rlib.jit as jit 7 | 8 | 9 | class LazySeq(object.Object): 10 | _type = object.Type(u"pixie.stdlib.LazySeq") 11 | 12 | def __init__(self, fn, meta=nil): 13 | self._fn = fn 14 | self._meta = meta 15 | self._s = nil 16 | 17 | @jit.jit_callback("lazy_seq_sval") 18 | def sval(self): 19 | if self._fn is None: 20 | return self._s 21 | else: 22 | self._s = self._fn.invoke([]) 23 | self._fn = None 24 | return self._s 25 | 26 | @jit.dont_look_inside 27 | def lazy_seq_seq(self): 28 | self.sval() 29 | if self._s is not nil: 30 | ls = self._s 31 | while True: 32 | if isinstance(ls, LazySeq): 33 | ls = ls.sval() 34 | continue 35 | else: 36 | self._s = ls 37 | return rt.seq(self._s) 38 | else: 39 | return nil 40 | 41 | 42 | 43 | @extend(proto._first, LazySeq) 44 | def _first(self): 45 | assert isinstance(self, LazySeq) 46 | rt.seq(self) 47 | return rt.first(self._s) 48 | 49 | @extend(proto._next, LazySeq) 50 | def _next(self): 51 | assert isinstance(self, LazySeq) 52 | rt.seq(self) 53 | return rt.next(self._s) 54 | 55 | @extend(proto._seq, LazySeq) 56 | def _seq(self): 57 | assert isinstance(self, LazySeq) 58 | return self.lazy_seq_seq() 59 | 60 | 61 | @as_var("lazy-seq*") 62 | def lazy_seq(f): 63 | return LazySeq(f) 64 | -------------------------------------------------------------------------------- /pixie/vm/libs/__init__.py: -------------------------------------------------------------------------------- 1 | __author__ = 'tim' 2 | -------------------------------------------------------------------------------- /pixie/vm/libs/c/uv_ffi.c: -------------------------------------------------------------------------------- 1 | #include "uv_ffi.h" 2 | #include "stdlib.h" 3 | #include "stdio.h" 4 | 5 | #define EXPORT __attribute__((visibility("default"))) 6 | 7 | void do_work(work_baton_t *req) 8 | { 9 | ffi_call(req->cif, req->fn_addr, req->result, req->exb); 10 | } 11 | 12 | EXPORT int uv_ffi_run(work_baton_t *w, uv_loop_t *loop, ffi_cif *cif, void* fn_addr, void *exb, void *result, uv_after_work_cb after_work) 13 | { 14 | w->fn_addr = fn_addr; 15 | w->cif = cif; 16 | w->exb = exb; 17 | w->result = result; 18 | return uv_queue_work(loop, (uv_work_t *)w, (uv_work_cb)do_work, after_work); 19 | } -------------------------------------------------------------------------------- /pixie/vm/libs/c/uv_ffi.h: -------------------------------------------------------------------------------- 1 | #include "uv.h" 2 | #include "ffi.h" 3 | 4 | typedef struct cif_desc_t { 5 | ffi_cif *cif; 6 | int abi; 7 | int nargs; 8 | ffi_type* rtype; 9 | ffi_type** atypes; 10 | int exchange_size; 11 | int exchange_result; 12 | int exchange_result_libffi; 13 | int exchange_args[0]; 14 | } cif_desc_t; 15 | 16 | void (*ffi_cb)(ffi_cif *cif); 17 | 18 | typedef struct work_baton_t 19 | { 20 | uv_work_t work; 21 | ffi_cif *cif; 22 | void *fn_addr; 23 | void *exb; 24 | void *result; 25 | } work_baton_t; -------------------------------------------------------------------------------- /pixie/vm/libs/env.py: -------------------------------------------------------------------------------- 1 | from pixie.vm.code import as_var 2 | from pixie.vm.object import Object, Type, runtime_error 3 | from pixie.vm.primitives import nil 4 | from pixie.vm.string import String 5 | import pixie.vm.stdlib as proto 6 | from pixie.vm.code import extend, as_var 7 | import pixie.vm.rt as rt 8 | import os 9 | 10 | class Environment(Object): 11 | _type = Type(u"pixie.stdlib.Environment") 12 | 13 | def val_at(self, key, not_found): 14 | if not isinstance(key, String): 15 | runtime_error(u"Environment variables are strings ") 16 | key_str = str(rt.name(key)) 17 | try: 18 | var = os.environ[key_str] 19 | return rt.wrap(var) 20 | except KeyError: 21 | return not_found 22 | 23 | # TODO: Implement me. 24 | # def dissoc(self): 25 | # def asssoc(self): 26 | 27 | def reduce_vars(self, f, init): 28 | for k, v in os.environ.items(): 29 | init = f.invoke([init, rt.map_entry(rt.wrap(k), rt.wrap(v))]) 30 | if rt.reduced_QMARK_(init): 31 | return init 32 | return init 33 | 34 | 35 | @extend(proto._val_at, Environment) 36 | def _val_at(self, key, not_found): 37 | assert isinstance(self, Environment) 38 | v = self.val_at(key, not_found) 39 | return v 40 | 41 | @extend(proto._reduce, Environment) 42 | def _reduce(self, f, init): 43 | assert isinstance(self, Environment) 44 | val = self.reduce_vars(f, init) 45 | if rt.reduced_QMARK_(val): 46 | return rt.deref(val) 47 | 48 | return val 49 | 50 | @as_var("pixie.stdlib", "env") 51 | def _env(): 52 | return Environment() 53 | -------------------------------------------------------------------------------- /pixie/vm/libs/libedit.py: -------------------------------------------------------------------------------- 1 | import py 2 | 3 | from pixie.vm.util import unicode_from_utf8 4 | 5 | from rpython.rtyper.lltypesystem import lltype, rffi 6 | from rpython.translator import cdir 7 | from rpython.translator.tool.cbuild import ExternalCompilationInfo 8 | 9 | # ____________________________________________________________ 10 | 11 | srcdir = py.path.local(cdir) / 'src' 12 | compilation_info = ExternalCompilationInfo( 13 | includes=['editline/readline.h'], 14 | libraries=["edit"]) 15 | 16 | def llexternal(*args, **kwargs): 17 | return rffi.llexternal(*args, compilation_info=compilation_info, releasegil=True, **kwargs) 18 | 19 | __readline = llexternal('readline', [rffi.CCHARP], rffi.CCHARP) 20 | 21 | def _readline(prompt): 22 | result = __readline(rffi.str2charp(prompt)) 23 | if result == lltype.nullptr(rffi.CCHARP.TO): 24 | return u"" 25 | else: 26 | return unicode_from_utf8(rffi.charp2str(result)) + u"\n" 27 | -------------------------------------------------------------------------------- /pixie/vm/libs/path.py: -------------------------------------------------------------------------------- 1 | import pixie.vm.rt as rt 2 | from pixie.vm.code import as_var, extend 3 | from pixie.vm.object import Object, Type 4 | import pixie.vm.stdlib as proto 5 | from pixie.vm.primitives import true, false 6 | import os 7 | 8 | class Path(Object): 9 | _type = Type(u"pixie.path.Path") 10 | 11 | def __init__(self, top): 12 | self._path = rt.name(top) 13 | 14 | # keyword args don't seem to work nicely. 15 | #def rel_path(self, other): 16 | # "Returns the path relative to other path" 17 | # return rt.wrap(str(os.path.relpath(self._path, start=other._path))) 18 | 19 | def abs_path(self): 20 | "Returns the absolute path" 21 | return rt.wrap(os.path.abspath(str(self._path))) 22 | 23 | # Basename doesn't play well with pypy... 24 | #def basename(self): 25 | # return rt.wrap(rt.name(os.path.basename("a"))) 26 | 27 | def exists(self): 28 | return true if os.path.exists(str(self._path)) else false 29 | 30 | def is_file(self): 31 | return true if os.path.isfile(str(self._path)) else false 32 | 33 | def is_dir(self): 34 | return true if os.path.isdir(str(self._path)) else false 35 | 36 | @extend(proto._reduce, Path) 37 | def _reduce(self, f, init): 38 | assert isinstance(self, Path) 39 | for dirpath, dirnames, filenames in os.walk(str(self._path)): 40 | for dirname in dirnames: 41 | init = f.invoke([init, Path(rt.wrap(dirpath + "/" + dirname))]) 42 | if rt.reduced_QMARK_(init): 43 | return rt.deref(init) 44 | 45 | for filename in filenames: 46 | init = f.invoke([init, Path(rt.wrap(dirpath + "/" + filename))]) 47 | if rt.reduced_QMARK_(init): 48 | return rt.deref(init) 49 | 50 | return init 51 | 52 | # I have named prefixed all names with '-' to deal with the 53 | # a namespace issue I was having. 54 | # TODO: remove '-' and update calling functions when issue is fixed. 55 | 56 | @as_var("pixie.path", "-path") 57 | def path(path): 58 | return Path(path) 59 | 60 | # TODO: Implement this 61 | @as_var("pixie.path", "-list-dir") 62 | def list_dir(self): 63 | assert isinstance(self, Path) 64 | result = rt.vector() 65 | for item in os.listdir(str(self._path)): 66 | result = rt.conj(result, rt.wrap(str(self._path) + "/" + str(item))) 67 | return result 68 | 69 | @as_var("pixie.path", "-abs") 70 | def _abs(self): 71 | assert isinstance(self, Path) 72 | return self.abs_path() 73 | 74 | @as_var("pixie.path", "-exists?") 75 | def exists_QMARK_(self): 76 | assert isinstance(self, Path) 77 | return self.exists() 78 | 79 | @as_var("pixie.path", "-file?") 80 | def file_QMARK_(self): 81 | assert isinstance(self, Path) 82 | return self.is_file() 83 | 84 | @as_var("pixie.path", "-dir?") 85 | def dir_QMARK_(self): 86 | assert isinstance(self, Path) 87 | return self.is_dir() 88 | -------------------------------------------------------------------------------- /pixie/vm/libs/platform.py: -------------------------------------------------------------------------------- 1 | from rpython.translator.platform import platform 2 | from pixie.vm.persistent_list import create_from_list 3 | from pixie.vm.string import String 4 | from pixie.vm.code import as_var 5 | from rpython.rlib.clibffi import get_libc_name 6 | import os 7 | 8 | 9 | as_var("pixie.platform", "os")(String(unicode(os.name))) 10 | as_var("pixie.platform", "name")(String(unicode(platform.name))) 11 | as_var("pixie.platform", "so-ext")(String(unicode(platform.so_ext))) 12 | as_var("pixie.platform", "lib-c-name")(String(unicode(get_libc_name()))) 13 | 14 | c_flags = [] 15 | for itm in platform.cflags: 16 | c_flags.append(String(unicode(itm))) 17 | 18 | as_var("pixie.platform", "c-flags")(create_from_list(c_flags)) 19 | 20 | link_flags = [] 21 | for itm in platform.link_flags: 22 | c_flags.append(String(unicode(itm))) 23 | as_var("pixie.platform", "link-flags")(create_from_list(link_flags)) 24 | -------------------------------------------------------------------------------- /pixie/vm/libs/pxic/__init__.py: -------------------------------------------------------------------------------- 1 | __author__ = 'tim' 2 | -------------------------------------------------------------------------------- /pixie/vm/libs/pxic/tags.py: -------------------------------------------------------------------------------- 1 | 2 | tag_name = ["INT", 3 | "BIGINT", 4 | "FLOAT", 5 | "INT_STRING", 6 | "BIGINT_STRING", 7 | "STRING", 8 | "CODE", 9 | "TRUE", 10 | "FALSE", 11 | "NIL", 12 | "VAR", 13 | "MAP", 14 | "VECTOR", 15 | "SEQ", 16 | "KEYWORD", 17 | "SYMBOL", 18 | "CACHED_OBJ", 19 | "NEW_CACHED_OBJ", 20 | "LINE_PROMISE", 21 | "NAMESPACE", 22 | "TAGGED", 23 | "CODE_INFO", 24 | "EOF"] 25 | 26 | tags = {} 27 | 28 | 29 | SMALL_INT_START = 128 30 | SMALL_INT_END = 255 31 | SMALL_INT_MAX = SMALL_INT_END - SMALL_INT_END 32 | 33 | MAX_STRING_SIZE = 1 << 30 34 | 35 | for idx, nm in enumerate(tag_name): 36 | globals()[nm] = idx 37 | tags[nm] = idx 38 | 39 | -------------------------------------------------------------------------------- /pixie/vm/libs/pxic/util.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | read_handlers = {} 4 | write_handlers = {} 5 | 6 | def add_marshall_handlers(tp, write, read): 7 | read_handlers[tp] = read 8 | write_handlers[tp] = write 9 | 10 | -------------------------------------------------------------------------------- /pixie/vm/libs/ring_buffer.py: -------------------------------------------------------------------------------- 1 | from rpython.rlib.rarithmetic import r_uint 2 | from pixie.vm.primitives import nil 3 | 4 | empty_slot = (nil, nil) 5 | 6 | class RingBuffer(object): 7 | def __init__(self, size): 8 | assert isinstance(size, r_uint) 9 | self._array = [empty_slot] * size 10 | self._array_len = size 11 | self._length = size 12 | self._head = r_uint(0) 13 | self._tail = r_uint(0) 14 | 15 | def pending(self): 16 | return self._array_len - self._length 17 | 18 | def pop(self): 19 | if not self._length == 0: 20 | x = self._array[self._tail] 21 | self._array[self._tail] = empty_slot 22 | self._tail = (self._tail + 1) % self._array_len 23 | self._length -= 1 24 | return x 25 | return empty_slot 26 | 27 | def push(self, x): 28 | self._array[self._head] = x 29 | self._head = (self._head + 1) % self._array_len 30 | self._length += 1 31 | 32 | def unbounded_push(self, x): 33 | if self._length == 0: 34 | self.resize() 35 | self.push(x) 36 | 37 | def resize(self): 38 | new_arr_size = self._array_len * 2 39 | new_arr = [None] * new_arr_size 40 | 41 | if self._tail < self._head: 42 | array_copy(self._array, self._tail, new_arr, 0, self._length) 43 | self._tail = 0 44 | self._head = self._length 45 | self._array = new_arr 46 | self._array_len = new_arr_size 47 | 48 | elif self._tail > self._head: 49 | array_copy(self._array, self._tail, new_arr, 0, self._array_len - self._tail) 50 | array_copy(self._array, 0, new_arr, self._array_len - self._tail, self._head) 51 | self._tail = 0 52 | self._head = self._length 53 | self._array = new_arr 54 | self._array_len = new_arr_size 55 | 56 | else: 57 | self._tail = 0 58 | self._head = 0 59 | self._array = new_arr 60 | self._array_len = new_arr_size 61 | 62 | 63 | def array_copy(src, src_pos, dest, dest_pos, count): 64 | x = r_uint(0) 65 | while x < count: 66 | dest[dest_pos + x] = src[src_pos + x] 67 | x += 1 68 | -------------------------------------------------------------------------------- /pixie/vm/libs/string.py: -------------------------------------------------------------------------------- 1 | import pixie.vm.rt as rt 2 | from pixie.vm.string import String 3 | from pixie.vm.code import as_var, intern_var, wrap_fn, MultiArityFn 4 | from pixie.vm.object import affirm, runtime_error 5 | from pixie.vm.numbers import Integer 6 | from rpython.rlib.unicodedata import unicodedb_6_2_0 as unicodedb 7 | import rpython.rlib.rstring as rstring 8 | import pixie.vm.rt as rt 9 | 10 | 11 | 12 | @as_var("pixie.string.internal", "starts-with") 13 | def startswith(a, b): 14 | return rt.wrap(rt.name(a).startswith(rt.name(b))) 15 | 16 | 17 | @as_var("pixie.string.internal", "ends-with") 18 | def endswith(a, b): 19 | return rt.wrap(rt.name(a).endswith(rt.name(b))) 20 | 21 | @as_var("pixie.string.internal", "split") 22 | def split(a, b): 23 | affirm(rt.count(b) > 0, u"separator can't be empty") 24 | v = rt.vector() 25 | for s in rstring.split(rt.name(a), rt.name(b)): 26 | v = rt.conj(v, rt.wrap(s)) 27 | return v 28 | 29 | def index_of2(a, sep): 30 | return rt.wrap(rt.name(a).find(rt.name(sep))) 31 | 32 | def index_of3(a, sep, start): 33 | affirm(isinstance(start, Integer), u"Third argument must be an integer") 34 | start = start.int_val() 35 | if start >= 0: 36 | return rt.wrap(rt.name(a).find(rt.name(sep), start)) 37 | else: 38 | runtime_error(u"Third argument must be a non-negative integer") 39 | 40 | def index_of4(a, sep, start, end): 41 | affirm(isinstance(start, Integer) and isinstance(end, Integer), u"Third and fourth argument must be integers") 42 | start = start.int_val() 43 | end = end.int_val() 44 | if start >= 0 and end >= 0: 45 | return rt.wrap(rt.name(a).find(rt.name(sep), start, end)) 46 | else: 47 | runtime_error(u"Third and fourth argument must be non-negative integers") 48 | 49 | index_of = intern_var(u"pixie.string.internal", u"index-of") 50 | index_of.set_root(MultiArityFn(u"index-of", {2: wrap_fn(index_of2), 3: wrap_fn(index_of3), 4: wrap_fn(index_of4)}, 51 | required_arity = 2)) 52 | 53 | def substring2(a, start): 54 | return substring3(a, start, rt._count(a)) 55 | 56 | def substring3(a, start, end): 57 | affirm(isinstance(a, String), u"First argument must be a string") 58 | affirm(isinstance(start, Integer) and isinstance(end, Integer), u"Second and third argument must be integers") 59 | start = start.int_val() 60 | end = end.int_val() 61 | if start >= 0 and end >= 0: 62 | return rt.wrap(rt.name(a)[start:end]) 63 | else: 64 | runtime_error(u"Second and third argument must be non-negative integers") 65 | 66 | substring = intern_var(u"pixie.string.internal", u"substring") 67 | substring.set_root(MultiArityFn(u"substring", {2: wrap_fn(substring2), 3: wrap_fn(substring3)}, 68 | required_arity = 2)) 69 | 70 | @as_var("pixie.string.internal", "upper-case") 71 | def upper_case(a): 72 | a = rt.name(a) 73 | res = "" 74 | for ch in a: 75 | res += chr(unicodedb.toupper(ord(ch))) 76 | return rt.wrap(res) 77 | 78 | @as_var("pixie.string.internal", "lower-case") 79 | def lower_case(a): 80 | a = rt.name(a) 81 | res = "" 82 | for ch in a: 83 | res += chr(unicodedb.tolower(ord(ch))) 84 | return rt.wrap(res) 85 | 86 | @as_var("pixie.string.internal", "capitalize") 87 | def capitalize(a): 88 | a = rt.name(a) 89 | res = u"" 90 | res += unichr(unicodedb.toupper(ord(a[0]))) 91 | res += a[1:] 92 | return rt.wrap(res) 93 | 94 | @as_var("pixie.string.internal", "trim") 95 | def trim(a): 96 | a = rt.name(a) 97 | i = 0 98 | while i < len(a) and unicodedb.isspace(ord(a[i])): 99 | i += 1 100 | j = len(a) 101 | while j > 0 and unicodedb.isspace(ord(a[j - 1])): 102 | j -= 1 103 | if j <= i: 104 | return rt.wrap(u"") 105 | return rt.wrap(a[i:j]) 106 | 107 | @as_var("pixie.string.internal", "triml") 108 | def triml(a): 109 | a = rt.name(a) 110 | i = 0 111 | while i < len(a) and unicodedb.isspace(ord(a[i])): 112 | i += 1 113 | return rt.wrap(a[i:len(a)]) 114 | 115 | @as_var("pixie.string.internal", "trimr") 116 | def trimr(a): 117 | a = rt.name(a) 118 | j = len(a) 119 | while j > 0 and unicodedb.isspace(ord(a[j - 1])): 120 | j -= 1 121 | if j <= 0: 122 | return rt.wrap(u"") 123 | return rt.wrap(a[0:j]) 124 | -------------------------------------------------------------------------------- /pixie/vm/map_entry.py: -------------------------------------------------------------------------------- 1 | py_object = object 2 | import pixie.vm.object as object 3 | import pixie.vm.stdlib as proto 4 | from pixie.vm.code import extend, as_var 5 | 6 | class MapEntry(object.Object): 7 | _type = object.Type(u"pixie.stdlib.MapEntry") 8 | 9 | def __init__(self, key, val): 10 | self._key = key 11 | self._val = val 12 | 13 | 14 | 15 | @as_var("map-entry") 16 | def map_entry(k, v): 17 | return MapEntry(k, v) 18 | 19 | 20 | @extend(proto._key, MapEntry) 21 | def _key(self): 22 | assert isinstance(self, MapEntry) 23 | return self._key 24 | 25 | @extend(proto._val, MapEntry) 26 | def _val(self): 27 | assert isinstance(self, MapEntry) 28 | return self._val 29 | 30 | -------------------------------------------------------------------------------- /pixie/vm/persistent_hash_set.py: -------------------------------------------------------------------------------- 1 | py_object = object 2 | import pixie.vm.object as object 3 | from pixie.vm.primitives import nil, true, false 4 | import pixie.vm.persistent_hash_map as persistent_hash_map 5 | import pixie.vm.stdlib as proto 6 | from pixie.vm.code import extend, as_var, intern_var 7 | import pixie.vm.rt as rt 8 | 9 | 10 | VAR_KEY = intern_var(u"pixie.stdlib", u"key") 11 | 12 | class PersistentHashSet(object.Object): 13 | _type = object.Type(u"pixie.stdlib.PersistentHashSet") 14 | 15 | def __init__(self, meta, m): 16 | self._meta = meta 17 | self._map = m 18 | 19 | def conj(self, v): 20 | return PersistentHashSet(self._meta, self._map.assoc(v, v)) 21 | 22 | def disj(self, k): 23 | return PersistentHashSet(self._meta, self._map.without(k)) 24 | 25 | def meta(self): 26 | return self._meta 27 | 28 | def with_meta(self, meta): 29 | return PersistentHashSet(meta, self._map) 30 | 31 | EMPTY = PersistentHashSet(nil, persistent_hash_map.EMPTY) 32 | 33 | @as_var("set") 34 | def _create(coll): 35 | ret = EMPTY 36 | coll = rt._seq(coll) 37 | while coll is not nil: 38 | ret = ret.conj(rt._first(coll)) 39 | coll = rt._seq(rt._next(coll)) 40 | return ret 41 | 42 | @extend(proto._count, PersistentHashSet) 43 | def _count(self): 44 | assert isinstance(self, PersistentHashSet) 45 | return rt._count(self._map) 46 | 47 | @extend(proto._val_at, PersistentHashSet) 48 | def _val_at(self, key, not_found): 49 | assert isinstance(self, PersistentHashSet) 50 | return rt._val_at(self._map, key, not_found) 51 | 52 | @extend(proto._contains_key, PersistentHashSet) 53 | def _contains_key(self, key): 54 | assert isinstance(self, PersistentHashSet) 55 | return rt._contains_key(self._map, key) 56 | 57 | @extend(proto._eq, PersistentHashSet) 58 | def _eq(self, obj): 59 | assert isinstance(self, PersistentHashSet) 60 | if self is obj: 61 | return true 62 | if not isinstance(obj, PersistentHashSet): 63 | return false 64 | if self._map._cnt != obj._map._cnt: 65 | return false 66 | 67 | seq = rt.seq(obj) 68 | while seq is not nil: 69 | if rt._contains_key(self, rt.first(seq)) is false: 70 | return false 71 | seq = rt.next(seq) 72 | return true 73 | 74 | @extend(proto._conj, PersistentHashSet) 75 | def _conj(self, v): 76 | assert isinstance(self, PersistentHashSet) 77 | return self.conj(v) 78 | 79 | @extend(proto._disj, PersistentHashSet) 80 | def _disj(self, v): 81 | assert isinstance(self, PersistentHashSet) 82 | return self.disj(v) 83 | 84 | @extend(proto._reduce, PersistentHashSet) 85 | def _reduce(self, f, init): 86 | assert isinstance(self, PersistentHashSet) 87 | return rt._reduce(rt.keys(self._map), f, init) 88 | 89 | @extend(proto._meta, PersistentHashSet) 90 | def _meta(self): 91 | assert isinstance(self, PersistentHashSet) 92 | return self.meta() 93 | 94 | @extend(proto._with_meta, PersistentHashSet) 95 | def _with_meta(self, meta): 96 | assert isinstance(self, PersistentHashSet) 97 | return self.with_meta(meta) 98 | -------------------------------------------------------------------------------- /pixie/vm/persistent_list.py: -------------------------------------------------------------------------------- 1 | import pixie.vm.object as object 2 | from pixie.vm.primitives import nil 3 | import pixie.vm.stdlib as proto 4 | from pixie.vm.code import extend, as_var 5 | from rpython.rlib.rarithmetic import r_uint, intmask 6 | import pixie.vm.rt as rt 7 | 8 | class PersistentList(object.Object): 9 | _type = object.Type(u"pixie.stdlib.PersistentList") 10 | 11 | def __init__(self, head, tail, cnt, meta=nil): 12 | self._first = head 13 | self._next = tail 14 | self._cnt = cnt 15 | self._meta = meta 16 | 17 | def first(self): 18 | return self._first 19 | 20 | def next(self): 21 | return self._next 22 | 23 | def meta(self): 24 | return self._meta 25 | 26 | def with_meta(self, meta): 27 | return PersistentList(self._first, self._next, self._cnt, meta) 28 | 29 | 30 | 31 | @extend(proto._first, PersistentList) 32 | def _first(self): 33 | assert isinstance(self, PersistentList) 34 | return self._first 35 | 36 | @extend(proto._next, PersistentList) 37 | def _next(self): 38 | assert isinstance(self, PersistentList) 39 | return self._next 40 | 41 | @extend(proto._seq, PersistentList) 42 | def _seq(self): 43 | assert isinstance(self, PersistentList) 44 | return self 45 | 46 | @extend(proto._count, PersistentList) 47 | def _count(self): 48 | assert isinstance(self, PersistentList) 49 | return rt.wrap(intmask(self._cnt)) 50 | 51 | @extend(proto._conj, PersistentList) 52 | def _conj(self, itm): 53 | assert isinstance(self, PersistentList) 54 | return PersistentList(itm, self, self._cnt + 1, nil) 55 | 56 | @extend(_conj, nil._type) 57 | def _conj(_, itm): 58 | return PersistentList(itm, nil, 1, nil) 59 | 60 | def count(self): 61 | cnt = 0 62 | while self is not nil: 63 | self = self.next() 64 | cnt += 1 65 | return cnt 66 | 67 | @as_var("list") 68 | def list__args(args): 69 | return create_from_list(args) 70 | 71 | def create_from_list(lst): 72 | if len(lst) == 0: 73 | return EmptyList() 74 | 75 | i = r_uint(len(lst)) 76 | acc = nil 77 | while i > 0: 78 | acc = PersistentList(lst[i - 1], acc, len(lst) - i + 1, nil) 79 | i -= 1 80 | return acc 81 | 82 | def create(*args): 83 | return create_from_list(args) 84 | 85 | @extend(proto._meta, PersistentList) 86 | def _meta(self): 87 | assert isinstance(self, PersistentList) 88 | return self.meta() 89 | 90 | @extend(proto._with_meta, PersistentList) 91 | def _with_meta(self, meta): 92 | assert isinstance(self, PersistentList) 93 | return self.with_meta(meta) 94 | 95 | 96 | 97 | 98 | class EmptyList(object.Object): 99 | _type = object.Type(u"pixie.stdlib.EmptyList") 100 | 101 | def __init__(self, meta=nil): 102 | self._meta = meta 103 | 104 | def meta(self): 105 | return self._meta 106 | 107 | def with_meta(self, meta): 108 | return EmptyList(meta) 109 | 110 | 111 | 112 | 113 | 114 | @extend(proto._first, EmptyList) 115 | def _first(self): 116 | assert isinstance(self, EmptyList) 117 | return nil 118 | 119 | @extend(proto._next, EmptyList) 120 | def _next(self): 121 | assert isinstance(self, EmptyList) 122 | return nil 123 | 124 | @extend(proto._seq, EmptyList) 125 | def _seq(self): 126 | assert isinstance(self, EmptyList) 127 | return nil 128 | 129 | @extend(proto._count, EmptyList) 130 | def _count(self): 131 | assert isinstance(self, EmptyList) 132 | return rt.wrap(0) 133 | 134 | @extend(proto._conj, EmptyList) 135 | def _conj(self, itm): 136 | assert isinstance(self, EmptyList) 137 | return PersistentList(itm, nil, 1) 138 | 139 | @extend(proto._meta, EmptyList) 140 | def _meta(self): 141 | assert isinstance(self, EmptyList) 142 | return self.meta() 143 | 144 | @extend(proto._with_meta, EmptyList) 145 | def _with_meta(self, meta): 146 | assert isinstance(self, EmptyList) 147 | return self.with_meta(meta) 148 | 149 | @extend(proto._str, EmptyList) 150 | def _str(self): 151 | return rt.wrap(u"()") 152 | 153 | @extend(proto._repr, EmptyList) 154 | def _str(self): 155 | return rt.wrap(u"()") 156 | 157 | @extend(proto._reduce, EmptyList) 158 | def _str(self, f, init): 159 | return init 160 | 161 | 162 | -------------------------------------------------------------------------------- /pixie/vm/primitives.py: -------------------------------------------------------------------------------- 1 | import pixie.vm.object as object 2 | 3 | class Nil(object.Object): 4 | _type = object.Type(u"pixie.stdlib.Nil") 5 | 6 | def __repr__(self): 7 | return u"nil" 8 | 9 | nil = Nil() 10 | 11 | class Bool(object.Object): 12 | _type = object.Type(u"pixie.stdlib.Bool") 13 | 14 | true = Bool() 15 | false = Bool() 16 | -------------------------------------------------------------------------------- /pixie/vm/reduced.py: -------------------------------------------------------------------------------- 1 | import pixie.vm.object as object 2 | from pixie.vm.code import extend, as_var, returns 3 | from pixie.vm.primitives import true, false 4 | import pixie.vm.stdlib as proto 5 | 6 | 7 | class Reduced(object.Object): 8 | _type = object.Type(u"pixie.stdlib.Reduced") 9 | 10 | def __init__(self, boxed_value): 11 | self._boxed_value = boxed_value 12 | 13 | @extend(proto._deref, Reduced._type) 14 | def _deref(self): 15 | assert isinstance(self, Reduced) 16 | return self._boxed_value 17 | 18 | @as_var("reduced") 19 | def reduced(val): 20 | return Reduced(val) 21 | 22 | @returns(bool) 23 | @as_var("reduced?") 24 | def reduced_QMARK_(val): 25 | return true if isinstance(val, Reduced) else false 26 | -------------------------------------------------------------------------------- /pixie/vm/rt.py: -------------------------------------------------------------------------------- 1 | __config__ = None 2 | py_list = list 3 | py_str = str 4 | from rpython.rlib.objectmodel import specialize, we_are_translated 5 | 6 | 7 | def init(): 8 | import pixie.vm.code as code 9 | from pixie.vm.object import affirm, _type_registry 10 | from rpython.rlib.rarithmetic import r_uint 11 | from rpython.rlib.rbigint import rbigint 12 | from pixie.vm.primitives import nil, true, false 13 | from pixie.vm.string import String 14 | from pixie.vm.object import Object 15 | from pixie.vm.compiler import NS_VAR 16 | 17 | _type_registry.set_registry(code._ns_registry) 18 | 19 | def unwrap(fn): 20 | if isinstance(fn, code.Var) and fn.is_defined() and hasattr(fn.deref(), "_returns"): 21 | tp = fn.deref()._returns 22 | if tp is bool: 23 | def wrapper(*args): 24 | ret = fn.invoke(py_list(args)) 25 | if ret is nil or ret is false: 26 | return False 27 | return True 28 | return wrapper 29 | elif tp is r_uint: 30 | return lambda *args: fn.invoke(py_list(args)).r_uint_val() 31 | elif tp is unicode: 32 | def wrapper(*args): 33 | ret = fn.invoke(py_list(args)) 34 | if ret is nil: 35 | return None 36 | 37 | if not isinstance(ret, String): 38 | from pixie.vm.object import runtime_error 39 | runtime_error(u"Invalid return value, expected String") 40 | return ret._str 41 | return wrapper 42 | else: 43 | assert False, "Don't know how to convert" + str(tp) 44 | return lambda *args: fn.invoke(py_list(args)) 45 | 46 | if "__inited__" in globals(): 47 | return 48 | 49 | import sys 50 | #sys.setrecursionlimit(10000) # Yeah we blow the stack sometimes, we promise it's not a bug 51 | 52 | import pixie.vm.code as code 53 | import pixie.vm.numbers as numbers 54 | import pixie.vm.bits 55 | import pixie.vm.interpreter 56 | import pixie.vm.atom 57 | import pixie.vm.reduced 58 | import pixie.vm.util 59 | import pixie.vm.array 60 | import pixie.vm.lazy_seq 61 | import pixie.vm.persistent_list 62 | import pixie.vm.persistent_hash_map 63 | import pixie.vm.persistent_hash_set 64 | import pixie.vm.custom_types 65 | import pixie.vm.map_entry 66 | import pixie.vm.libs.platform 67 | import pixie.vm.libs.ffi 68 | import pixie.vm.libs.env 69 | import pixie.vm.symbol 70 | import pixie.vm.libs.path 71 | import pixie.vm.libs.string 72 | import pixie.vm.threads 73 | import pixie.vm.string_builder 74 | import pixie.vm.stacklet 75 | 76 | import pixie.vm.c_api 77 | 78 | @specialize.argtype(0) 79 | def wrap(x): 80 | if isinstance(x, bool): 81 | return true if x else false 82 | if isinstance(x, int): 83 | return numbers.Integer(x) 84 | if isinstance(x, rbigint): 85 | return numbers.BigInteger(x) 86 | if isinstance(x, float): 87 | return numbers.Float(x) 88 | if isinstance(x, unicode): 89 | return String(x) 90 | if isinstance(x, py_str): 91 | return String(unicode(x)) 92 | if isinstance(x, Object): 93 | return x 94 | if x is None: 95 | return nil 96 | 97 | if not we_are_translated(): 98 | print x, type(x) 99 | affirm(False, u"Bad wrap") 100 | 101 | globals()["wrap"] = wrap 102 | 103 | def int_val(x): 104 | affirm(isinstance(x, numbers.Number), u"Expected number") 105 | return x.int_val() 106 | 107 | globals()["int_val"] = int_val 108 | 109 | from pixie.vm.code import _ns_registry, BaseCode, munge 110 | 111 | for name, var in _ns_registry._registry[u"pixie.stdlib"]._registry.iteritems(): 112 | name = munge(name) 113 | if var.is_defined() and isinstance(var.deref(), BaseCode): 114 | globals()[name] = unwrap(var) 115 | else: 116 | globals()[name] = var 117 | 118 | import pixie.vm.bootstrap 119 | 120 | def reinit(): 121 | for name, var in _ns_registry._registry[u"pixie.stdlib"]._registry.iteritems(): 122 | name = munge(name) 123 | if name in globals(): 124 | continue 125 | 126 | if var.is_defined() and isinstance(var.deref(), BaseCode): 127 | globals()[name] = unwrap(var) 128 | else: 129 | globals()[name] = var 130 | 131 | #f = open("pixie/stdlib.pxi") 132 | #data = f.read() 133 | #f.close() 134 | #rdr = reader.MetaDataReader(reader.StringReader(unicode(data)), u"pixie/stdlib.pixie") 135 | #result = nil 136 | # 137 | # @wrap_fn 138 | # def run_load_stdlib(): 139 | # with compiler.with_ns(u"pixie.stdlib"): 140 | # while True: 141 | # form = reader.read(rdr, False) 142 | # if form is reader.eof: 143 | # return result 144 | # result = compiler.compile(form).invoke([]) 145 | # reinit() 146 | # 147 | # stacklet.with_stacklets(run_load_stdlib) 148 | 149 | init_fns = [u"reduce", u"get", u"reset!", u"assoc", u"key", u"val", u"keys", u"vals", u"vec", u"load-file", u"compile-file", 150 | u"load-ns", u"hashmap", u"cons", u"-assoc", u"-val-at"] 151 | for x in init_fns: 152 | globals()[py_str(code.munge(x))] = unwrap(code.intern_var(u"pixie.stdlib", x)) 153 | 154 | init_vars = [u"load-paths"] 155 | for x in init_vars: 156 | globals()[py_str(code.munge(x))] = code.intern_var(u"pixie.stdlib", x) 157 | 158 | globals()[py_str(code.munge(u"ns"))] = NS_VAR 159 | 160 | globals()["__inited__"] = True 161 | 162 | globals()["is_true"] = lambda x: False if x is false or x is nil or x is None else True 163 | 164 | numbers.init() 165 | code.init() 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | -------------------------------------------------------------------------------- /pixie/vm/stacklet.py: -------------------------------------------------------------------------------- 1 | import rpython.rlib.rstacklet as rstacklet 2 | from pixie.vm.object import Object, Type, affirm 3 | from pixie.vm.code import as_var 4 | import pixie.vm.rt as rt 5 | 6 | 7 | class GlobalState(object): 8 | def __init__(self): 9 | self._is_inited = False 10 | self._val = None 11 | 12 | 13 | global_state = GlobalState() 14 | 15 | 16 | def init(): 17 | if not global_state._is_inited: 18 | global_state._th = rstacklet.StackletThread(rt.__config__) 19 | global_state._is_inited = True 20 | 21 | 22 | class StackletHandle(Object): 23 | _type = Type(u"StackletHandle") 24 | def __init__(self, h): 25 | self._stacklet_handle = h 26 | self._used = False 27 | 28 | def invoke(self, args): 29 | affirm(not self._used, u"Can only call a given stacklet handle once.") 30 | affirm(len(args) == 1, u"Only one arg should be handed to a stacklet handle") 31 | self._used = True 32 | global_state._val = args[0] 33 | new_h = StackletHandle(global_state._th.switch(self._stacklet_handle)) 34 | val = global_state._val 35 | global_state._val = None 36 | return rt.vector(new_h, val) 37 | 38 | def new_handler(h, _): 39 | fn = global_state._val 40 | global_state._val = None 41 | h = global_state._th.switch(h) 42 | val = global_state._val 43 | fn.invoke([StackletHandle(h), val]) 44 | affirm(False, u"TODO: What do we do now?") 45 | return h 46 | 47 | 48 | 49 | @as_var("new-stacklet") 50 | def new_stacklet(fn): 51 | global_state._val = fn 52 | h = global_state._th.new(new_handler) 53 | return StackletHandle(h) 54 | -------------------------------------------------------------------------------- /pixie/vm/string.py: -------------------------------------------------------------------------------- 1 | import pixie.vm.rt as rt 2 | from pixie.vm.object import Object, Type, affirm 3 | from pixie.vm.code import extend, as_var, wrap_fn 4 | from pixie.vm.primitives import nil, true, false 5 | from pixie.vm.numbers import Integer, _add 6 | import pixie.vm.stdlib as proto 7 | import pixie.vm.util as util 8 | from rpython.rlib.rarithmetic import intmask, r_uint 9 | from pixie.vm.libs.pxic.util import add_marshall_handlers 10 | 11 | class String(Object): 12 | _type = Type(u"pixie.stdlib.String") 13 | 14 | def __init__(self, s): 15 | #assert isinstance(s, unicode) 16 | self._str = s 17 | 18 | 19 | @extend(proto._str, String) 20 | def _str(x): 21 | return x 22 | 23 | @extend(proto._repr, String) 24 | def _repr(self): 25 | res = u"" 26 | assert isinstance(self, String) 27 | for c in self._str: 28 | if c == "\"": 29 | res += u"\\\"" 30 | elif c == "\n": 31 | res += u"\\n" 32 | elif c == "\t": 33 | res += u"\\t" 34 | elif c == "\b": 35 | res += u"\\b" 36 | elif c == "\f": 37 | res += u"\\f" 38 | elif c == "\r": 39 | res += u"\\r" 40 | else: 41 | res += c 42 | return rt.wrap(u"\"" + res + u"\"") 43 | 44 | @extend(proto._count, String) 45 | def _count(self): 46 | assert isinstance(self, String) 47 | return rt.wrap(len(self._str)) 48 | 49 | @extend(proto._nth, String) 50 | def _nth(self, idx): 51 | assert isinstance(self, String) 52 | i = idx.int_val() 53 | if 0 <= i < len(self._str): 54 | return Character(ord(self._str[i])) 55 | affirm(False, u"Index out of Range") 56 | 57 | @extend(proto._nth_not_found, String) 58 | def _nth_not_found(self, idx, not_found): 59 | assert isinstance(self, String) 60 | i = idx.int_val() 61 | if 0 <= i < len(self._str): 62 | return Character(ord(self._str[i])) 63 | return not_found 64 | 65 | @extend(proto._eq, String) 66 | def _eq(self, v): 67 | assert isinstance(self, String) 68 | if not isinstance(v, String): 69 | return false 70 | return true if self._str == v._str else false 71 | 72 | class Character(Object): 73 | _type = Type(u"pixie.stdlib.Character") 74 | _immutable_fields_ = ["_char_val"] 75 | 76 | def __init__(self, i): 77 | assert isinstance(i, int) 78 | self._char_val = i 79 | 80 | def char_val(self): 81 | return self._char_val 82 | 83 | @wrap_fn 84 | def write_char(obj): 85 | assert isinstance(obj, Character) 86 | return rt.wrap(obj._char_val) 87 | 88 | @wrap_fn 89 | def read_char(obj): 90 | return Character(obj.int_val()) 91 | 92 | add_marshall_handlers(Character._type, write_char, read_char) 93 | 94 | @extend(proto._str, Character) 95 | def _str(self): 96 | assert isinstance(self, Character) 97 | return rt.wrap(u"" + unichr(self.char_val())) 98 | 99 | @extend(proto._repr, Character) 100 | def _repr(self): 101 | assert isinstance(self, Character) 102 | cv = self.char_val() 103 | if cv < 128: 104 | return rt.wrap(u"\\"+unicode(chr(cv))) 105 | hexv = rt.name(rt.bit_str(rt.wrap(self.char_val()), rt.wrap(4))) 106 | return rt.wrap(u"\\u" + u"0" * (4 - len(hexv)) + hexv) 107 | 108 | @extend(proto._eq, Character) 109 | def _eq(self, obj): 110 | assert isinstance(self, Character) 111 | if self is obj: 112 | return true 113 | if not isinstance(obj, Character): 114 | return false 115 | return true if self.char_val() == obj.char_val() else false 116 | 117 | @extend(proto._hash, Character) 118 | def _hash(self): 119 | return rt.wrap(intmask(util.hash_int(r_uint(self.char_val())))) 120 | 121 | @as_var("char") 122 | def char(val): 123 | affirm(isinstance(val, Integer), u"First argument must be an Integer") 124 | return Character(val.int_val()) 125 | 126 | @extend(_add, Character._type, Integer._type) 127 | def _add(a, b): 128 | assert isinstance(a, Character) and isinstance(b, Integer) 129 | return rt._add(rt.wrap(a.char_val()), b) 130 | 131 | @extend(_add, Character._type, Character._type) 132 | def _add(a, b): 133 | assert isinstance(a, Character) and isinstance(b, Character) 134 | return Character(a.char_val() + b.char_val()) 135 | 136 | 137 | @extend(proto._name, String) 138 | def _name(self): 139 | return self 140 | 141 | @extend(proto._namespace, String) 142 | def _namespace(self): 143 | return nil 144 | 145 | @extend(proto._hash, String) 146 | def _hash(self): 147 | assert isinstance(self, String) 148 | return rt.wrap(intmask(util.hash_unencoded_chars(self._str))) 149 | -------------------------------------------------------------------------------- /pixie/vm/string_builder.py: -------------------------------------------------------------------------------- 1 | import pixie.vm.rt as rt 2 | from pixie.vm.object import Object, Type 3 | from pixie.vm.code import as_var, extend 4 | import pixie.vm.stdlib as proto 5 | 6 | class StringBuilder(Object): 7 | _type = Type(u"pixie.stdlib.StringBuilder") 8 | 9 | def __init__(self): 10 | self._strs = [] 11 | 12 | def add_str(self, s): 13 | self._strs.append(s) 14 | return self 15 | 16 | def to_string(self): 17 | return u"".join(self._strs) 18 | 19 | 20 | @extend(proto._conj_BANG_, StringBuilder) 21 | def _conj(self, val): 22 | return self.add_str(rt.name(rt._str(val))) 23 | 24 | @extend(proto._persistent_BANG_, StringBuilder) 25 | def _persistent(self): 26 | return rt._str(self) 27 | 28 | @extend(proto._str, StringBuilder) 29 | def _str(self): 30 | return rt.wrap(self.to_string()) 31 | 32 | @as_var("-string-builder") 33 | def _string_builder(): 34 | return StringBuilder() 35 | -------------------------------------------------------------------------------- /pixie/vm/symbol.py: -------------------------------------------------------------------------------- 1 | import pixie.vm.object as object 2 | from pixie.vm.primitives import nil, true, false 3 | import pixie.vm.stdlib as proto 4 | from pixie.vm.code import extend, as_var 5 | from pixie.vm.string import String 6 | import pixie.vm.rt as rt 7 | import pixie.vm.util as util 8 | from rpython.rlib.rarithmetic import intmask 9 | 10 | 11 | class Symbol(object.Object): 12 | _type = object.Type(u"pixie.stdlib.Symbol") 13 | def __init__(self, s, meta=nil): 14 | #assert isinstance(s, unicode) 15 | self._str = s 16 | self._w_name = None 17 | self._w_ns = None 18 | self._hash = 0 19 | self._meta = meta 20 | 21 | def type(self): 22 | return Symbol._type 23 | 24 | def init_names(self): 25 | if self._w_name is None: 26 | s = self._str.split(u"/") 27 | if len(s) == 2: 28 | self._w_ns = rt.wrap(s[0]) 29 | self._w_name = rt.wrap(s[1]) 30 | elif len(s) == 1: 31 | self._w_name = rt.wrap(s[0]) 32 | self._w_ns = nil 33 | else: 34 | self._w_ns = rt.wrap(s[0]) 35 | self._w_name = rt.wrap(u"/".join(s[1:])) 36 | 37 | def with_meta(self, meta): 38 | return Symbol(self._str, meta) 39 | 40 | def meta(self): 41 | return self._meta 42 | 43 | 44 | 45 | def symbol(s): 46 | return Symbol(s) 47 | 48 | @extend(proto._eq, Symbol) 49 | def _eq(self, other): 50 | assert isinstance(self, Symbol) 51 | if not isinstance(other, Symbol): 52 | return false 53 | return true if self._str == other._str else false 54 | 55 | @extend(proto._str, Symbol) 56 | def _str(self): 57 | assert isinstance(self, Symbol) 58 | return rt.wrap(self._str) 59 | 60 | @extend(proto._name, Symbol) 61 | def _name(self): 62 | assert isinstance(self, Symbol) 63 | self.init_names() 64 | return self._w_name 65 | 66 | @extend(proto._namespace, Symbol) 67 | def _namespace(self): 68 | assert isinstance(self, Symbol) 69 | self.init_names() 70 | return self._w_ns 71 | 72 | @extend(proto._hash, Symbol) 73 | def _hash(self): 74 | assert isinstance(self, Symbol) 75 | if self._hash == 0: 76 | self._hash = util.hash_unencoded_chars(self._str) 77 | return rt.wrap(intmask(self._hash)) 78 | 79 | @as_var("symbol") 80 | def _symbol(s): 81 | if not isinstance(s, String): 82 | from pixie.vm.object import runtime_error 83 | runtime_error(u"Symbol name must be a string") 84 | return symbol(s._str) 85 | 86 | 87 | 88 | @extend(proto._meta, Symbol) 89 | def _meta(self): 90 | assert isinstance(self, Symbol) 91 | return self.meta() 92 | 93 | @extend(proto._with_meta, Symbol) 94 | def _with_meta(self, meta): 95 | assert isinstance(self, Symbol) 96 | return self.with_meta(meta) 97 | -------------------------------------------------------------------------------- /pixie/vm/test/__init__.py: -------------------------------------------------------------------------------- 1 | __author__ = 'tim' 2 | -------------------------------------------------------------------------------- /pixie/vm/test/test_hashmaps.py: -------------------------------------------------------------------------------- 1 | import pixie.vm.rt as rt 2 | from pixie.vm.primitives import nil 3 | 4 | rt.init() 5 | 6 | def test_hashmap_create(): 7 | 8 | acc = rt.hashmap(rt.wrap(1), rt.wrap(2)) 9 | 10 | val = rt._val_at(acc, rt.wrap(1), nil) 11 | 12 | assert val.int_val() == 2 13 | -------------------------------------------------------------------------------- /pixie/vm/test/test_reader.py: -------------------------------------------------------------------------------- 1 | from pixie.vm.reader import read_inner, StringReader 2 | from pixie.vm.object import Object 3 | from pixie.vm.cons import Cons 4 | from pixie.vm.numbers import Integer 5 | from pixie.vm.symbol import symbol, Symbol 6 | from pixie.vm.string import Character 7 | from pixie.vm.persistent_vector import PersistentVector 8 | from pixie.vm.persistent_hash_map import PersistentHashMap 9 | from pixie.vm.primitives import nil 10 | import pixie.vm.rt as rt 11 | import unittest 12 | 13 | 14 | data = {u"(1 2)": (1, 2,), 15 | u"(foo)": (symbol(u"foo"),), 16 | u"foo": symbol(u"foo"), 17 | u"1": 1, 18 | u"2": 2, 19 | u"((42))": ((42,),), 20 | u"(platform+ 1 2)": (symbol(u"platform+"), 1, 2), 21 | u"[42 43 44]": [42, 43, 44], 22 | u"(1 2 ; 7 8 9\n3)": (1, 2, 3,), 23 | u"(1 2 ; 7 8 9\r\n3)": (1, 2, 3,), 24 | u"(1 2 ; 7 8 9\r\n)": (1, 2,), 25 | u"(1 2 ; 7 8 9\n)": (1, 2,), 26 | u"[1 2 ; 7 8 9\n]": [1, 2,], 27 | u"(1 2; 7 8 9\n)": (1, 2,), 28 | u";foo\n(1 2; 7 8 9\n)": (1, 2,), 29 | u"{\"foo\" 1 ;\"bar\" 2\n\"baz\" 3}": {"foo": 1, "baz": 3}, 30 | u"{\"foo\" ; \"bar\" 2\n1 \"baz\" 3}": {"foo": 1, "baz": 3}, 31 | u"(\\a)": (Character(ord("a")),), 32 | u"(\\))": (Character(ord(")")),), 33 | u"(\\()": (Character(ord("(")),), 34 | u"(\\;)": (Character(ord(";")),)} 35 | 36 | class TestReader(unittest.TestCase): 37 | def _compare(self, frm, to): 38 | if isinstance(to, tuple): 39 | assert isinstance(frm, Cons) 40 | 41 | for x in to: 42 | self._compare(frm.first(), x) 43 | frm = frm.next() 44 | assert frm == nil 45 | elif isinstance(to, int): 46 | assert isinstance(frm, Integer) 47 | assert frm._int_val == to 48 | 49 | elif isinstance(to, Symbol): 50 | assert isinstance(frm, Symbol) 51 | assert frm._str == to._str 52 | 53 | elif isinstance(to, list): 54 | assert isinstance(frm, PersistentVector) 55 | 56 | for x in range(max(len(to), rt._count(frm)._int_val)): 57 | self._compare(rt.nth(frm, rt.wrap(x)), to[x]) 58 | 59 | elif isinstance(to, dict): 60 | assert isinstance(frm, PersistentHashMap) 61 | for key in dict.keys(to): 62 | self._compare(frm.val_at(rt.wrap(key), ""), to[key]) 63 | 64 | assert rt._count(frm)._int_val == len(dict.keys(to)) 65 | 66 | elif isinstance(to, Character): 67 | assert isinstance(frm, Character) 68 | assert to._char_val == frm._char_val 69 | 70 | else: 71 | raise Exception("Don't know how to handle " + str(type(to))) 72 | 73 | def test_forms(self): 74 | for s in data: 75 | tst = data[s] 76 | result = read(StringReader(s), True) 77 | assert isinstance(result, Object) 78 | 79 | self._compare(result, tst) 80 | 81 | 82 | -------------------------------------------------------------------------------- /pixie/vm/test/test_vars.py: -------------------------------------------------------------------------------- 1 | from pixie.vm.code import intern_var, get_var_if_defined 2 | 3 | 4 | def test_intern(): 5 | assert intern_var(u"foo", u"bar") is intern_var(u"foo", u"bar") 6 | assert intern_var(u"foo2", u"bar") is not intern_var(u"foo", u"bar") 7 | 8 | assert get_var_if_defined(u"foo", u"bar") 9 | assert get_var_if_defined(u"foo2", u"bar") 10 | -------------------------------------------------------------------------------- /pixie/vm/test/test_vectors.py: -------------------------------------------------------------------------------- 1 | from pixie.vm.persistent_vector import EMPTY 2 | 3 | 4 | def test_vector_conj(): 5 | 6 | acc = EMPTY 7 | for x in range(1200): 8 | acc = acc.conj(x) 9 | for y in range(x): 10 | assert acc.nth(y) == y, "Error at: " + str(x) + " and " + str(y) 11 | -------------------------------------------------------------------------------- /pixie/vm/threads.py: -------------------------------------------------------------------------------- 1 | from pixie.vm.object import Object, Type, safe_invoke 2 | from pixie.vm.primitives import true 3 | import rpython.rlib.rthread as rthread 4 | from pixie.vm.primitives import nil 5 | import rpython.rlib.rgil as rgil 6 | from pixie.vm.code import as_var 7 | import pixie.vm.rt as rt 8 | 9 | class Bootstrapper(object): 10 | def __init__(self): 11 | self._is_inited = False 12 | #_self.init() 13 | 14 | def init(self): 15 | if not self._is_inited: 16 | self._lock = rthread.allocate_lock() 17 | self._is_inited = True 18 | rgil.allocate() 19 | 20 | def aquire(self, fn): 21 | self.init() 22 | self._lock.acquire(True) 23 | self._fn = fn 24 | 25 | def fn(self): 26 | return self._fn 27 | 28 | def release(self): 29 | self._fn = None 30 | self._lock.release() 31 | 32 | 33 | def _cleanup_(self): 34 | self._lock = None 35 | self._is_inited = False 36 | 37 | 38 | def bootstrap(): 39 | rthread.gc_thread_start() 40 | fn = bootstrapper.fn() 41 | bootstrapper.release() 42 | safe_invoke(fn, []) 43 | rthread.gc_thread_die() 44 | 45 | bootstrapper = Bootstrapper() 46 | 47 | @as_var("-thread") 48 | def new_thread(fn): 49 | bootstrapper.aquire(fn) 50 | ident = rthread.start_new_thread(bootstrap, ()) 51 | return nil 52 | 53 | @as_var("-yield-thread") 54 | def yield_thread(): 55 | rgil.yield_thread() 56 | return nil 57 | 58 | # Locks 59 | 60 | class Lock(Object): 61 | _type = Type(u"pixie.stdlib.Lock") 62 | def __init__(self, ll_lock): 63 | self._ll_lock = ll_lock 64 | 65 | 66 | @as_var("-create-lock") 67 | def _create_lock(): 68 | return Lock(rthread.allocate_lock()) 69 | 70 | @as_var("-acquire-lock") 71 | def _acquire_lock(self, no_wait): 72 | assert isinstance(self, Lock) 73 | return rt.wrap(self._ll_lock.acquire(no_wait == true)) 74 | 75 | @as_var("-acquire-lock-timed") 76 | def _acquire_lock(self, ms): 77 | assert isinstance(self, Lock) 78 | return rt.wrap(self._ll_lock.acquire(ms.int_val())) 79 | 80 | @as_var("-release-lock") 81 | def _release_lock(self): 82 | assert isinstance(self, Lock) 83 | return rt.wrap(self._ll_lock.release()) 84 | 85 | 86 | # The *_external_call() functions are themselves called only from the rffi 87 | # module from a helper function that also has this hint. 88 | -------------------------------------------------------------------------------- /pixie/vm/util.py: -------------------------------------------------------------------------------- 1 | from rpython.rlib.rarithmetic import r_uint, LONG_BIT, intmask 2 | from rpython.rlib.runicode import str_decode_utf_8, unicode_encode_utf_8 3 | from pixie.vm.object import affirm 4 | 5 | seed = 0 6 | C1 = r_uint(0xcc9e2d51) 7 | C2 = r_uint(0x1b873593) 8 | 9 | 10 | 11 | def rotr(value, shift): 12 | return (value >> shift) | (value << (LONG_BIT - shift)) 13 | 14 | def rotl(value, shift): 15 | return (value << shift) | (value >> (LONG_BIT - shift)) 16 | 17 | def hash_int(input): 18 | if input == 0: 19 | return r_uint(0) 20 | k1 = mix_k1(input) 21 | h1 = mix_h1(seed, k1) 22 | 23 | return fmix(h1, 4) 24 | 25 | def mix_k1(k1): 26 | k1 *= C1 27 | k1 = rotl(k1, 15) 28 | k1 *= C2 29 | return k1 30 | 31 | def mix_h1(h1, k1): 32 | h1 ^= k1 33 | h1 = rotl(h1, 13) 34 | h1 = h1 * 5 + r_uint(0xe6546b64) 35 | return h1 36 | 37 | def hash_unencoded_chars(u): 38 | #assert isinstance(u, unicode) 39 | h1 = seed 40 | 41 | # step through the CharSequence 2 chars at a time 42 | for i in range(1, len(u), 2): 43 | k1 = r_uint(ord(u[i-1]) | ord(u[i])) 44 | k1 = mix_k1(k1) 45 | h1 = mix_h1(h1, k1) 46 | 47 | # deal with any remaining characters 48 | if (len(u) & 1) == 1: 49 | k1 = r_uint(ord(u[(len(u) - 1)])) 50 | k1 = mix_k1(k1) 51 | h1 ^= k1 52 | 53 | return fmix(h1, 2 * len(u)) 54 | 55 | 56 | def fmix(h1, length): 57 | h1 ^= length 58 | h1 ^= h1 >> 16 59 | h1 *= r_uint(0x85ebca6b) 60 | h1 ^= h1 >> 13 61 | h1 *= r_uint(0xc2b2ae35) 62 | h1 ^= h1 >> 16 63 | return h1 64 | 65 | 66 | def mix_coll_hash(hash, count): 67 | h1 = seed 68 | k1 = mix_k1(hash) 69 | h1 = mix_h1(h1, k1) 70 | return fmix(h1, count) 71 | 72 | 73 | 74 | from pixie.vm.object import Object, Type 75 | from pixie.vm.code import as_var 76 | import pixie.vm.rt as rt 77 | 78 | class HashingState(Object): 79 | _type = Type(u"pixie.stdlib.HashingState") 80 | 81 | def __init__(self): 82 | self._n = r_uint(0) 83 | self._hash = r_uint(1) 84 | 85 | def update_hash_ordered(self, itm): 86 | self._n += 1 87 | self._hash = 31 * self._hash + rt.hash(itm) 88 | return self 89 | 90 | def update_hash_unordered(self, itm): 91 | self._n += 1 92 | self._hash += rt.hash(itm) 93 | return self 94 | 95 | def finish(self): 96 | return rt.wrap(intmask(mix_coll_hash(self._hash, self._n))) 97 | 98 | 99 | @as_var("new-hash-state") 100 | def new_hash_state(): 101 | return HashingState() 102 | 103 | @as_var("update-hash-ordered!") 104 | def update_hash_ordered(acc, val): 105 | affirm(isinstance(acc, HashingState), u"Expected HashingState as first argument") 106 | return acc.update_hash_ordered(val) 107 | 108 | @as_var("update-hash-unordered!") 109 | def update_hash_ordered(acc, val): 110 | affirm(isinstance(acc, HashingState), u"Expected HashingState as first argument") 111 | return acc.update_hash_unordered(val) 112 | 113 | @as_var("finish-hash-state") 114 | def finish_hash_state(acc): 115 | affirm(isinstance(acc, HashingState), u"Expected HashingState as first argument") 116 | return acc.finish() 117 | 118 | @as_var("hash-int") 119 | def _hash_int(acc): 120 | return rt.wrap(intmask(hash_int(acc.r_uint_val()))) 121 | 122 | 123 | # unicode utils 124 | 125 | def unicode_from_utf8(s): 126 | """Converts a `str` value to a `unicode` value assuming it's encoded in UTF8.""" 127 | res, _ = str_decode_utf_8(s, len(s), 'strict') 128 | return res 129 | 130 | def unicode_to_utf8(s): 131 | """Converts a `unicode` value to a UTF8 encoded `str` value.""" 132 | return unicode_encode_utf_8(s, len(s), 'strict') 133 | -------------------------------------------------------------------------------- /pixie/walk.pxi: -------------------------------------------------------------------------------- 1 | (ns pixie.walk) 2 | 3 | (defprotocol IWalk 4 | (-walk [x f])) 5 | 6 | (extend-protocol IWalk 7 | PersistentList 8 | (-walk [x f] 9 | (apply list (map f x))) 10 | 11 | Cons 12 | (-walk [x f] 13 | (cons (f (first x)) (map f (next x)))) 14 | 15 | IMapEntry 16 | (-walk [x f] 17 | (map-entry (f (key x)) (f (val x)))) 18 | 19 | PersistentVector 20 | (-walk [x f] 21 | (into [] (map f) x)) 22 | 23 | PersistentHashSet 24 | (-walk [x f] 25 | (into #{} (map f) x)) 26 | 27 | PersistentHashMap 28 | (-walk [x f] 29 | (into {} (map f) x)) 30 | 31 | IRecord 32 | (-walk [x f] 33 | (into x (map f) x)) 34 | 35 | ISeqable 36 | (-walk [x f] 37 | (map f x)) 38 | 39 | IObject 40 | (-walk [x f] x) 41 | 42 | Nil 43 | (-walk [x f] nil)) 44 | 45 | (defn walk 46 | {:doc "Traverses form, an arbitrary data structure. f is a 47 | function. Applies f to each element of form, building up a data 48 | structure of the same type. Recognizes all Pixie data 49 | structures. Consumes seqs." 50 | :signatures [[f x]] 51 | :added "0.1"} 52 | [f x] 53 | (-walk x f)) 54 | 55 | (defn postwalk 56 | {:doc "Performs a depth-first, post-order traversal of form. Calls f on 57 | each sub-form, uses f's return value in place of the original. 58 | Recognizes all Pixie data structures." 59 | :signatures [[f x]] 60 | :added "0.1"} 61 | [f x] 62 | (f (walk (partial postwalk f) x))) 63 | 64 | (defn prewalk 65 | {:doc "Like postwalk, but does pre-order traversal." 66 | :added "0.1"} 67 | [f x] 68 | (walk (partial prewalk f) (f x))) 69 | 70 | (defn prewalk-replace 71 | {:doc "Recursively transforms form by replacing 72 | keys in smap with their values. Like `replace` but works on 73 | any data structure. Does replacement at the root of the tree 74 | first." 75 | :signatures [[f x]] 76 | :added "0.1"} 77 | [smap x] 78 | (prewalk (fn [x] (if (contains? smap x) (smap x) x)) x)) 79 | 80 | (defn postwalk-replace 81 | {:doc "Recursively transforms form by replacing keys in smap with 82 | their values. Like `replace` but works on any data structure. 83 | Does replacement at the leaves of the tree first." 84 | :signatures [[smap x]] 85 | :added "0.1"} 86 | [smap x] 87 | (postwalk (fn [x] (if (contains? smap x) (smap x) x)) x)) 88 | 89 | (defn keywordize-keys 90 | {:doc "Recursively transforms all map keys from strings to keywords." 91 | :signatures [[m]] 92 | :added "0.1"} 93 | [m] 94 | (let [f (fn [[k v]] (if (string? k) [(keyword k) v] [k v]))] 95 | ;; only apply to maps 96 | (postwalk (fn [x] (if (map? x) (into {} (map f x)) x)) m))) 97 | 98 | (defn stringify-keys 99 | {:doc "Recursively transforms all map keys from keywords to strings." 100 | :signatures [[m]] 101 | :added "0.1"} 102 | [m] 103 | (let [f (fn [[k v]] (if (keyword? k) [(name k) v] [k v]))] 104 | ;; only apply to maps 105 | (postwalk (fn [x] (if (map? x) (into {} (map f x)) x)) m))) 106 | 107 | (defn macroexpand-all 108 | {:doc "Recursively performs all possible macroexpansions in 109 | form. For development use only." 110 | :added "0.1" 111 | :signatures [[x]]} 112 | [x] 113 | (prewalk (fn [x] (if (seq? x) (macroexpand-1 x) x)) x)) 114 | -------------------------------------------------------------------------------- /run-tests.pxi: -------------------------------------------------------------------------------- 1 | (require pixie.test :as t) 2 | 3 | 4 | (swap! load-paths conj "./tests/") 5 | 6 | (println @load-paths) 7 | 8 | (if (= 0 (count program-arguments)) 9 | (t/load-all-tests) 10 | (doseq [nm program-arguments] 11 | (println "Loading: " nm) 12 | (load-ns (symbol nm)))) 13 | 14 | (let [result (apply t/run-tests program-arguments)] 15 | (exit (get result :fail))) 16 | -------------------------------------------------------------------------------- /tests/pixie/tests/collections/test-maps.pxi: -------------------------------------------------------------------------------- 1 | (ns collections.test-maps 2 | (require pixie.test :as t)) 3 | 4 | (t/deftest maps-contains 5 | (let [m {:a 1, :b 2, :c 3} 6 | c [:a :b :c] 7 | n [:d 'a 1]] 8 | (foreach [c c] 9 | (t/assert= (contains? m c) true)) 10 | (foreach [n n] 11 | (t/assert= (contains? m c) false)))) 12 | 13 | (t/deftest map-equals 14 | (let [m {:a 1, :b 2, :c 3}] 15 | (t/assert= (-eq {} {}) true) 16 | (t/assert= (-eq m m) true) 17 | (t/assert= (-eq m {:a 1, :b 2, :c 3}) true) 18 | 19 | (t/assert= (-eq m {}) false) 20 | (t/assert= (-eq m nil) false) 21 | 22 | (t/assert= (-eq m {:a 1, :b 2}) false) 23 | (t/assert= (-eq m [[:a 1] [:b 2] [:c 3]]) false) 24 | 25 | (t/assert= (-eq m {:a 1, :b 2, :c 4}) false) 26 | (t/assert= (-eq m {:a 3, :b 2, :c 1}) false))) 27 | 28 | (t/deftest map-val-at-and-invoke 29 | (let [m {:a 1, :b 2, :c 3}] 30 | (foreach [e m] 31 | (t/assert= (get m (key e)) (val e)) 32 | (t/assert= (m (key e)) (val e))) 33 | (t/assert= (get m :d) nil) 34 | (t/assert= (m :d) nil) 35 | (t/assert= (m :d :foo) :foo) 36 | (t/assert= (:d m :foo) :foo))) 37 | 38 | (t/deftest map-without 39 | (let [m {:a 1 :b 2}] 40 | (t/assert= m m) 41 | (t/assert= (dissoc m :a) {:b 2}) 42 | (t/assert= (dissoc m :a :b) {}))) 43 | 44 | (t/deftest map-conj 45 | (let [m {:a 1 :b 2}] 46 | (t/assert= m m) 47 | ;; Should conj vector of length 2 48 | (t/assert= (conj m [:c 3]) {:a 1 :b 2 :c 3}) 49 | (t/assert= (conj m [:b 4]) {:a 1 :b 4}) 50 | (t/assert= (conj m [:b 4] [:c 5]) {:a 1 :b 4 :c 5}) 51 | 52 | ;; Should conj sequences of pairs 53 | (t/assert= (conj {} '([:a 1] [:b 2] [:c 3])) {:a 1 :b 2 :c 3}) 54 | 55 | ;; Should conj sequences of MapEntries 56 | (t/assert= (conj {} (seq {:a 1 :b 2 :c 3})) {:a 1 :b 2 :c 3}))) 57 | -------------------------------------------------------------------------------- /tests/pixie/tests/collections/test-seqables.pxi: -------------------------------------------------------------------------------- 1 | (ns collections.test-seqables 2 | (require pixie.test :as t)) 3 | 4 | (t/deftest test-seq 5 | (let [l '(1 2 3) 6 | v [1 2 3]] 7 | (t/assert= (seq l) '(1 2 3)) 8 | (t/assert= (seq v) [1 2 3]) 9 | 10 | (t/assert= (seq nil) nil) 11 | (t/assert= (seq []) nil))) 12 | 13 | (t/deftest test-first 14 | (let [l '(1 2 3) 15 | v [1 2 3]] 16 | (t/assert= (first l) 1) 17 | (t/assert= (first v) 1) 18 | (t/assert= (first (seq l)) 1) 19 | (t/assert= (first (seq v)) 1))) 20 | 21 | (t/deftest test-next 22 | (let [l '(1 2 3) 23 | v [1 2 3]] 24 | (t/assert= (next l) '(2 3)) 25 | (t/assert= (next v) '(2 3)) 26 | (t/assert= (next (seq l)) '(2 3)) 27 | (t/assert= (next (seq v)) '(2 3)) 28 | 29 | (t/assert= (next (next (next l))) nil) 30 | (t/assert= (next (next (next v))) nil))) 31 | 32 | (t/deftest test-equals 33 | (let [l '(1 2 3)] 34 | (t/assert= l l) 35 | (t/assert= l '(1 2 3)) 36 | (t/assert= l [1 2 3]) 37 | 38 | (t/assert= (= nil '()) false) 39 | (t/assert= (= '() nil) false) 40 | (t/assert= (= l '(1 2 3 4)) false) 41 | (t/assert= (= l [1 2 3 4]) false))) 42 | 43 | (t/deftest test-conj 44 | (t/assert= '(3 1 2) (conj '(1 2) 3)) 45 | (t/assert= '(5 4 3 1 2) (conj '(1 2) 3 4 5))) 46 | -------------------------------------------------------------------------------- /tests/pixie/tests/collections/test-sets.pxi: -------------------------------------------------------------------------------- 1 | (ns collections.test-sets 2 | (require pixie.test :as t) 3 | (require pixie.tests.utils :as u)) 4 | 5 | (def worst-hashers (vec (map u/->WorstHasher) 6 | (range 100))) 7 | 8 | (t/deftest test-count 9 | (t/assert= (count (set [])) 0) 10 | (t/assert= (count (set [1 2 3])) 3) 11 | (t/assert= (count (set [1 1 2 1])) 2) 12 | (t/assert= (count (set worst-hashers)) 100)) 13 | 14 | (t/deftest test-contains 15 | (let [s #{1 2 3} 16 | c [1 2 3] 17 | n [-1 0 4] 18 | g (set worst-hashers)] 19 | (foreach [c c] 20 | (t/assert= (contains? s c) true)) 21 | (foreach [n n] 22 | (t/assert= (contains? s n) false)) 23 | (foreach [n worst-hashers] 24 | (t/assert= (contains? g n) true)))) 25 | 26 | (t/deftest test-conj 27 | (t/assert= (conj #{}) #{}) 28 | (t/assert= (conj #{1 2} 3) #{1 2 3}) 29 | (t/assert= (reduce conj #{} (range 10)) (set (vec (range 10)))) 30 | (t/assert= (reduce conj #{} worst-hashers) (set worst-hashers))) 31 | 32 | (t/deftest test-disj 33 | (t/assert= (disj #{}) #{}) 34 | (t/assert= (disj #{1 2} 3) #{1 2}) 35 | (t/assert= (disj #{1 2} 2) #{1}) 36 | (t/assert= (reduce disj (set (vec (range 10))) (range 10)) #{}) 37 | (t/assert= (reduce disj (set worst-hashers) worst-hashers) #{})) 38 | 39 | (t/deftest test-eq 40 | (let [s #{1 2 3}] 41 | (t/assert= s s) 42 | (t/assert= s #{1 2 3}) 43 | (t/assert= #{1 2 3} s) 44 | 45 | (t/assert= (= #{} nil) false) 46 | (t/assert= (= #{} []) false) 47 | (t/assert= (= #{} '()) false) 48 | 49 | (t/assert= (= s [1 2 3]) false) 50 | (t/assert= (= s '(1 2 3)) false) 51 | (t/assert= (= s #{1 2}) false) 52 | (t/assert= (= s #{1 2 3 4}) false))) 53 | 54 | (t/deftest test-invoke 55 | (let [s #{1 2 3}] 56 | (t/assert= (s 1) 1) 57 | (t/assert= (s 2) 2) 58 | (t/assert= (s 3) 3) 59 | 60 | (t/assert= (s -1) nil) 61 | (t/assert= (s 4) nil) 62 | (t/assert= (s :d :foo) :foo) 63 | (t/assert= (:d s :foo) :foo))) 64 | 65 | (t/deftest test-has-meta 66 | (let [m {:has-meta true} 67 | s (with-meta #{} m)] 68 | (t/assert= (meta #{}) nil) 69 | (t/assert= (meta s) m))) 70 | 71 | (t/deftest test-conj 72 | (t/assert= #{1 2} (conj #{1} 2)) 73 | (t/assert= #{1 2 3 4} (conj #{1} 2 3 4))) 74 | -------------------------------------------------------------------------------- /tests/pixie/tests/collections/test-vectors.pxi: -------------------------------------------------------------------------------- 1 | (ns pixie.tests.collections.test-vectors 2 | (require pixie.test :as t)) 3 | 4 | (def MAX-SIZE 1064) 5 | 6 | (comment 7 | ;; Takes forever in interpreted mode but useful for debugging 8 | (t/deftest vector-creation 9 | (loop [acc []] 10 | (if (= (count acc) MAX-SIZE) 11 | acc 12 | (do (dotimes [j (count acc)] 13 | (t/assert= j (nth acc j))) 14 | (recur (conj acc (count acc))))))) 15 | ) 16 | (t/deftest vector-contains 17 | (let [v [1 2 3] 18 | c [0 1 2] 19 | n [-1 3]] 20 | (foreach [c c] 21 | (t/assert= (contains? v c) true)) 22 | (foreach [n n] 23 | (t/assert= (contains? v n) false)))) 24 | 25 | (t/deftest vector-equals 26 | (let [v [1 2 3]] 27 | (t/assert= [] '()) 28 | (t/assert= v v) 29 | (t/assert= v [1 2 3]) 30 | (t/assert= v '(1 2 3)) 31 | 32 | (t/assert= (= [] nil) false) 33 | (t/assert= (= v []) false) 34 | (t/assert= (= v [1 2]) false) 35 | (t/assert= (= v [1 2 3 4]) false) 36 | (t/assert= (= v '(1 2)) false) 37 | (t/assert= (= v '(1 2 3 4)) false))) 38 | 39 | (t/deftest vector-conj 40 | (t/assert= [1 2] (conj [1] 2)) 41 | (t/assert= [1 2 3 4] (conj [1] 2 3 4))) 42 | 43 | (t/deftest vector-conj! 44 | (t/assert= [1 2] (persistent! (conj! (transient [1]) 2))) 45 | (t/assert= [1 2 3] (persistent! (conj! (transient [1]) 2 3)))) 46 | 47 | (t/deftest vector-push 48 | (t/assert= [1] (push [] 1)) 49 | (t/assert= [1 2] (push [1] 2)) 50 | (t/assert= [0 1 2 3 4] (reduce push [] (range 5)))) 51 | 52 | (t/deftest vector-pop 53 | (t/assert= [] (pop [1])) 54 | (t/assert= [1] (pop [1 2])) 55 | (t/assert= [1 2] (pop [1 2 3])) 56 | (try (pop []) 57 | (catch e 58 | (t/assert= "Can't pop an empty vector" (ex-msg e))))) 59 | 60 | (t/deftest vector-push! 61 | (t/assert= [1] (persistent! (push! (transient []) 1))) 62 | (t/assert= [1 2] (persistent! (push! (transient [1]) 2)))) 63 | 64 | (t/deftest vector-pop! 65 | (t/assert= [] (persistent! (pop! (transient [1])))) 66 | (t/assert= [1] (persistent! (pop! (transient [1 2])))) 67 | (t/assert= [1 2] (persistent! (pop! (transient [1 2 3])))) 68 | (try (persistent! (pop! (transient []))) 69 | (catch e 70 | (t/assert= "Can't pop an empty vector" (ex-msg e))))) 71 | 72 | (t/deftest transient-vector-count 73 | (t/assert= 0 (count (transient []))) 74 | (t/assert= 1 (count (transient [1]))) 75 | (t/assert= 2 (count (transient [1 2]))) 76 | (t/assert= 1 (count (pop! (transient [1 2])))) 77 | (t/assert= 100 (count (reduce conj! (transient []) (range 0 100))))) 78 | -------------------------------------------------------------------------------- /tests/pixie/tests/data/test-json.pxi: -------------------------------------------------------------------------------- 1 | (ns pixie.tests.data.test-json 2 | (:require [pixie.test :refer :all] 3 | [pixie.data.json :as json])) 4 | 5 | 6 | 7 | ;; This test just ensures that we can use read-string; more thorough testing is 8 | ;; done in pixie.tests.parser.test-json 9 | (deftest test-read-string 10 | (assert= (json/read-string "{\"foo\": 42, \"bar\": [\"baz\"]}") 11 | {"foo" 42, "bar" ["baz"]})) 12 | 13 | (deftest test-strings 14 | (assert-table [x y] (assert= (json/write-string x) y) 15 | \a "\"a\"" 16 | "foo" "\"foo\"" 17 | :bar "\"bar\"" 18 | "" "\"\"")) 19 | 20 | (deftest test-numbers 21 | (assert-table [x y] (assert= (json/write-string x) y) 22 | 1 "1" 23 | 1.0 "1.000000" 24 | 0.1 "0.100000" 25 | 1.1 "1.100000" 26 | 1234.5678 "1234.567800" 27 | -1 "-1" 28 | -0.1 "-0.100000" 29 | -1.1 "-1.100000" 30 | -1234.5678 "-1234.567800" 31 | 1e1 "10.000000" 32 | 3/1 "3")) 33 | 34 | (deftest test-vectors 35 | (assert-table [x y] (assert= (json/write-string x) y) 36 | [] "[]" 37 | [nil] "[null]" 38 | [1 2] "[1, 2]" 39 | [1 1.0 nil] "[1, 1.000000, null]" 40 | ["foo" 42] "[\"foo\", 42]")) 41 | 42 | (deftest test-seqs 43 | (assert-table [x y] (assert= (json/write-string x) y) 44 | (take 0 (range)) "[]" 45 | (take 1 (repeat nil)) "[null]" 46 | (map identity [1 2]) "[1, 2]" 47 | (reduce + [1 2 3]) "6")) 48 | 49 | (deftest test-lists 50 | (assert-table [x y] (assert= (json/write-string x) y) 51 | '() "[]" 52 | '(nil) "[null]" 53 | '(1 2) "[1, 2]" 54 | '(1 1.0 nil) "[1, 1.000000, null]" 55 | '("foo" 42) "[\"foo\", 42]" 56 | (list) "[]" 57 | (list nil) "[null]" 58 | (list 1 2) "[1, 2]" 59 | (list 1 1.0 nil) "[1, 1.000000, null]" 60 | (list "foo" 42) "[\"foo\", 42]")) 61 | 62 | (deftest test-maps 63 | (assert-table [x y] (assert= (json/write-string x) y) 64 | {} "{}" 65 | {:foo 42} "{\"foo\": 42}" 66 | {"foo" 42} "{\"foo\": 42}" 67 | {"foo" 42, "bar" nil} "{\"foo\": 42, \"bar\": null}")) 68 | 69 | (deftest test-round-trip 70 | (let [data {"foo" 1, "bar" [2 3]}] ; won't work with keywords because the parser doesn't keywordise them (yet) 71 | (assert= (-> data json/write-string json/read-string) 72 | data)) 73 | 74 | (let [string "{\"foo\": [1, 2, 3]}"] 75 | (assert= (-> string json/read-string json/write-string) 76 | string))) 77 | -------------------------------------------------------------------------------- /tests/pixie/tests/fs/parent/bar.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pixie-lang/pixie/d76adb041a4968906bf22575fee7a572596e5796/tests/pixie/tests/fs/parent/bar.txt -------------------------------------------------------------------------------- /tests/pixie/tests/fs/parent/child/bar.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pixie-lang/pixie/d76adb041a4968906bf22575fee7a572596e5796/tests/pixie/tests/fs/parent/child/bar.txt -------------------------------------------------------------------------------- /tests/pixie/tests/fs/parent/child/foo.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pixie-lang/pixie/d76adb041a4968906bf22575fee7a572596e5796/tests/pixie/tests/fs/parent/child/foo.txt -------------------------------------------------------------------------------- /tests/pixie/tests/fs/parent/foo.txt: -------------------------------------------------------------------------------- 1 | foo 2 | -------------------------------------------------------------------------------- /tests/pixie/tests/io/test-tcp.pxi: -------------------------------------------------------------------------------- 1 | (ns pixie.test.io.test-tcp 2 | (:require [pixie.io.tcp :refer :all] 3 | [pixie.io :refer [buffered-input-stream buffered-output-stream read-byte write-byte]] 4 | [pixie.streams :refer :all] 5 | [pixie.stacklets :as st] 6 | [pixie.async :as async] 7 | [pixie.uv :as uv] 8 | [pixie.test :refer :all])) 9 | 10 | (deftest test-echo-server 11 | (let [client-done (async/promise) 12 | on-client (fn on-client [conn] 13 | (let [in (buffered-input-stream conn) 14 | out (buffered-output-stream conn)] 15 | (try 16 | (loop [] 17 | (let [val (read-byte in)] 18 | (write-byte out val) 19 | (flush out) 20 | (recur))) 21 | (catch ex 22 | (dispose! in) 23 | (dispose! out) 24 | 25 | (dispose! conn) 26 | (client-done true))))) 27 | 28 | server (tcp-server "0.0.0.0" 4242 on-client)] 29 | 30 | (let [client-stream (tcp-client "127.0.0.1" 4242) 31 | in (buffered-input-stream client-stream) 32 | out (buffered-output-stream client-stream)] 33 | 34 | (dotimes [x 255] 35 | (write-byte out x) 36 | (flush out) 37 | (assert= x (read-byte in))) 38 | (dispose! client-stream) 39 | (assert @client-done) 40 | (dispose! server)))) 41 | -------------------------------------------------------------------------------- /tests/pixie/tests/parser/test-json.pxi: -------------------------------------------------------------------------------- 1 | (ns pixie.tests.parser.test-json 2 | (:require [pixie.test :refer :all] 3 | [pixie.parser.json :as json])) 4 | 5 | 6 | 7 | (deftest test-json-numbers 8 | (assert-table [x y] (assert= (json/read-string x) y) 9 | "1" 1 10 | "1.0" 1.0 11 | "0.1" 0.1 12 | "1.1" 1.1 13 | "1234.5678" 1234.5678 14 | 15 | "-1" -1 16 | "-0.1" -0.1 17 | "-1.1" -1.1 18 | "-1234.5678" -1234.5678 19 | "1e1" 1e1)) 20 | 21 | (deftest test-vectors 22 | (assert-table [x y] (assert= (json/read-string x) y) 23 | "[]" [] 24 | "[null]" [nil] 25 | "[1, 2]" [1 2] 26 | "[1, 1.0, null]" [1 1.0 nil] 27 | "[\"foo\", 42]" ["foo" 42])) 28 | 29 | (deftest test-maps 30 | (assert-table [x y] (assert= (json/read-string x) y) 31 | "{\"foo\": 42}" {"foo", 42} 32 | "{\"foo\": 42, \"bar\":null}" {"foo" 42 33 | "bar" nil})) 34 | -------------------------------------------------------------------------------- /tests/pixie/tests/streams/test-utf8.pxi: -------------------------------------------------------------------------------- 1 | (ns pixie.streams.test-utf8 2 | (require pixie.streams.utf8 :refer :all) 3 | (require pixie.io :as io) 4 | (require pixie.test :refer :all)) 5 | 6 | 7 | (deftest test-writing-ints 8 | (using [os (-> (io/open-write "/tmp/pixie-utf-test.txt") 9 | (io/buffered-output-stream 1024) 10 | utf8-output-stream)] 11 | (dotimes [x 32000] 12 | (write-char os (char x)))) 13 | (using [is (-> (io/open-read "/tmp/pixie-utf-test.txt") 14 | (io/buffered-input-stream 1024) 15 | utf8-input-stream)] 16 | (dotimes [x 32000] 17 | (assert= x (int (read-char is)))))) 18 | -------------------------------------------------------------------------------- /tests/pixie/tests/test-arrays.pxi: -------------------------------------------------------------------------------- 1 | (ns pixie.test.test-arrays 2 | (require pixie.test :as t)) 3 | 4 | (t/deftest test-array-creation 5 | (let [a (make-array 10)] 6 | (t/assert= (count a) 10) 7 | (t/assert= (alength a) 10) 8 | (foreach [x a] 9 | (t/assert= x nil)))) 10 | 11 | (t/deftest test-alength 12 | (let [a (make-array 10)] 13 | (t/assert= (alength a) 10) 14 | (t/assert-throws? RuntimeException 15 | (alength [])))) 16 | 17 | (t/deftest test-aget-and-aset 18 | (let [a (make-array 10)] 19 | (dotimes [i 10] 20 | (t/assert= (aget a i) nil)) 21 | 22 | (dotimes [i 10] 23 | (aset a i i)) 24 | 25 | (dotimes [i 10] 26 | (t/assert= (aget a i) i)) 27 | 28 | (t/assert-throws? RuntimeException 29 | (aget a 1.0)) 30 | 31 | (t/assert-throws? RuntimeException 32 | (aset a 1.0 :foo)))) 33 | 34 | (t/deftest test-aconcat 35 | (let [a1 (make-array 10) 36 | a2 (make-array 10)] 37 | (t/assert= (alength (aconcat a1 a2)) (+ (alength a1) (alength a2))) 38 | 39 | (dotimes [i 10] 40 | (aset a1 i i) 41 | (aset a2 i (+ 10 i))) 42 | 43 | (let [a3 (aconcat a1 a2)] 44 | (dotimes [i 20] 45 | (t/assert= (aget a3 i) i))) 46 | 47 | (t/assert-throws? RuntimeException 48 | (t/aconcat a1 [])) 49 | 50 | (t/assert-throws? RuntimeException 51 | (t/aconcat a1 '())))) 52 | 53 | (t/deftest test-aslice 54 | (let [a (make-array 10)] 55 | (dotimes [i 10] 56 | (aset a i i)) 57 | 58 | (let [a1 (aslice a 3) 59 | a2 (aslice a 7)] 60 | (foreach [i (range 0 7)] 61 | (t/assert= (aget a1 i) (+ i 3))) 62 | (foreach [i (range 0 3)] 63 | (t/assert= (aget a2 i) (+ i 7)))) 64 | 65 | (t/assert-throws? RuntimeException 66 | (aslice [1 2 3 4] 0 2)) 67 | 68 | (t/assert-throws? RuntimeException 69 | (aslice '() 0 2)) 70 | 71 | (t/assert-throws? RuntimeException 72 | (aslice a 1.0 2)))) 73 | 74 | 75 | (t/deftest test-byte-array-creation 76 | (let [ba (byte-array 10)] 77 | (t/assert= (vec ba) [0 0 0 0 0 0 0 0 0 0]) 78 | (t/assert= (count ba) 10))) 79 | -------------------------------------------------------------------------------- /tests/pixie/tests/test-async.pxi: -------------------------------------------------------------------------------- 1 | (ns pixie.tests.test-async 2 | (:require [pixie.stacklets :as st] 3 | [pixie.async :as async :refer :all] 4 | [pixie.test :as t :refer :all])) 5 | 6 | 7 | (deftest test-future-deref 8 | (let [f (future 42)] 9 | (assert= @f 42))) 10 | 11 | (deftest test-future-deref-chain 12 | (let [f1 (future 42) 13 | f2 (future @f1) 14 | f3 (future @f2)] 15 | (assert= @f3 42))) 16 | 17 | (def *some-var* 0) 18 | (set-dynamic! (var *some-var*)) 19 | 20 | (deftest test-dynamic-var-propagation 21 | (set! (var *some-var*) 0) 22 | (assert= *some-var* 0) 23 | (let [fr @(future (do (println "running") 24 | (let [old-val *some-var*] 25 | (set! (var *some-var*) 42) 26 | [old-val *some-var*])))] 27 | 28 | (assert= fr [0 42]) 29 | (assert= *some-var* 0))) 30 | -------------------------------------------------------------------------------- /tests/pixie/tests/test-bits.pxi: -------------------------------------------------------------------------------- 1 | (ns pixie.test.test-bits 2 | (require pixie.test :as t)) 3 | 4 | (t/deftest test-bit-clear 5 | (t/assert= (bit-clear 0 0) 0) 6 | (t/assert= (bit-clear 2r11111 7) 2r11111) 7 | 8 | (t/assert= (bit-clear 2r111 0) 2r110) 9 | (t/assert= (bit-clear 2r111 1) 2r101) 10 | (t/assert= (bit-clear 2r111 2) 2r011)) 11 | 12 | (t/deftest test-bit-set 13 | (t/assert= (bit-set 2r111 0) 2r111) 14 | (t/assert= (bit-set 2r000 1) 2r010)) 15 | 16 | (t/deftest test-bit-flip 17 | (t/assert= (bit-flip 2r101 0) 2r100) 18 | (t/assert= (bit-flip 2r101 1) 2r111)) 19 | 20 | (t/deftest test-bit-test 21 | (t/assert= (bit-test 2r101 0) true) 22 | (t/assert= (bit-test 2r101 1) false)) 23 | 24 | (t/deftest test-bit-and 25 | (t/assert= (bit-and 0 0) 0) 26 | (t/assert= (bit-and 2r101 2r101) 2r101) 27 | (t/assert= (bit-and 2r101 2r101) 2r101) 28 | (t/assert= (bit-and 2r101 0) 0)) 29 | 30 | (t/deftest test-bit-or 31 | (t/assert= (bit-or 0 0) 0) 32 | (t/assert= (bit-or 2r101 2r010) 2r111) 33 | (t/assert= (bit-or 2r111 2r010) 2r111) 34 | (t/assert= (bit-or 2r111 2r111) 2r111) 35 | (t/assert= (bit-or 2r101 0) 2r101)) 36 | 37 | (t/deftest test-bit-xor 38 | (t/assert= (bit-xor 0 0) 0) 39 | (t/assert= (bit-xor 2r101 2r010) 2r111) 40 | (t/assert= (bit-xor 2r111 2r010) 2r101) 41 | (t/assert= (bit-xor 2r111 2r111) 2r000) 42 | (t/assert= (bit-xor 2r101 0) 2r101)) 43 | 44 | (t/deftest test-bit-shift-left 45 | (t/assert= (bit-shift-left 0 0) 0) 46 | (t/assert= (bit-shift-left 2r101 0) 2r101) 47 | 48 | (t/assert= (bit-shift-left 0 7) 0) 49 | (t/assert= (bit-shift-left 2r001 2) 2r100) 50 | (t/assert= (bit-shift-left 2r111 2) 2r11100)) 51 | 52 | (t/deftest test-bit-shift-right 53 | (t/assert= (bit-shift-right 0 0) 0) 54 | (t/assert= (bit-shift-right 2r101 0) 2r101) 55 | 56 | (t/assert= (bit-shift-right 0 7) 0) 57 | (t/assert= (bit-shift-right 2r001 2) 0) 58 | (t/assert= (bit-shift-right 2r111 2) 2r001) 59 | (t/assert= (bit-shift-right 2r1011010 2) 2r10110)) 60 | -------------------------------------------------------------------------------- /tests/pixie/tests/test-buffers.pxi: -------------------------------------------------------------------------------- 1 | (ns pixie.tests.test-buffers 2 | (:require [pixie.test :refer :all] 3 | [pixie.buffers :refer :all])) 4 | 5 | (deftest test-adding-and-removing-from-buffer 6 | (let [buffer (ring-buffer 10)] 7 | (dotimes [x 100] 8 | (add! buffer x) 9 | (assert= x (remove! buffer))))) 10 | 11 | (deftest test-adding-multiple-items 12 | (let [buffer (ring-buffer 10)] 13 | (dotimes [x 10] 14 | (add! buffer x)) 15 | (dotimes [x 10] 16 | (assert= x (remove! buffer))))) 17 | 18 | (deftest test-adding-multiple-items-with-resize 19 | (dotimes [y 100] 20 | (let [buffer (ring-buffer 2)] 21 | (dotimes [x y] 22 | (add-unbounded! buffer x)) 23 | (dotimes [x y] 24 | (assert= x (remove! buffer)))))) 25 | 26 | 27 | (def drain-buffer (partial into [])) 28 | 29 | (deftest test-dropping-buffer 30 | (let [buf (dropping-buffer 4)] 31 | (dotimes [x 5] 32 | (add! buf x)) 33 | (assert= [0 1 2 3] (drain-buffer buf)) 34 | (assert (not (full? buf))))) 35 | 36 | (deftest test-sliding-buffer 37 | (let [buf (sliding-buffer 4)] 38 | (dotimes [x 5] 39 | (add! buf x)) 40 | (assert= [1 2 3 4] (drain-buffer buf)) 41 | (assert (not (full? buf))))) 42 | -------------------------------------------------------------------------------- /tests/pixie/tests/test-channels.pxi: -------------------------------------------------------------------------------- 1 | (ns pixie.tests.test-channels 2 | (:require [pixie.test :refer :all] 3 | [pixie.channels :refer :all] 4 | [pixie.async :refer :all] 5 | [pixie.stacklets :as st])) 6 | 7 | 8 | (deftest simple-read-and-write 9 | (let [takep (promise) 10 | putp (promise) 11 | c (chan)] 12 | (assert (-put! c 42 putp)) 13 | (assert (-take! c takep)) 14 | 15 | (assert= @takep 42) 16 | (assert= @putp true))) 17 | 18 | (deftest simple-take-before-put 19 | (let [takep (promise) 20 | putp (promise) 21 | c (chan)] 22 | (assert (-take! c takep)) 23 | (assert (-put! c 42 putp)) 24 | 25 | (assert= @takep 42) 26 | (assert= @putp true))) 27 | 28 | (deftest simple-take-before-put-with-buffers 29 | (let [c (chan 2) 30 | takesp (mapv (fn [_] (promise)) 31 | (range 10)) 32 | putsp (mapv (fn [_] (promise)) 33 | (range 10))] 34 | (doseq [p takesp] 35 | (assert (-take! c p))) 36 | (dotimes [x 10] 37 | (assert (-put! c x (nth putsp x)))) 38 | 39 | (doseq [p putsp] 40 | (assert= @p true)) 41 | 42 | (dotimes [x 10] 43 | (assert= @(nth takesp x) x)))) 44 | 45 | (deftest closing-dispatches-future-takes-with-nil 46 | (let [c (chan 1) 47 | tp (promise)] 48 | (-close! c) 49 | (-take! c tp) 50 | (assert= @tp nil))) 51 | 52 | (deftest closing-dispatches-past-takes-with-nil 53 | (let [c (chan 1) 54 | tp (promise)] 55 | (-take! c tp) 56 | (-close! c) 57 | (assert= @tp nil))) 58 | 59 | (deftest closed-channels-return-false-on-future-puts 60 | (let [c (chan 1) 61 | pp (promise)] 62 | (-close! c) 63 | (assert= (-put! c 42 pp) false) 64 | (assert= @pp false))) 65 | 66 | (deftest closing-allows-puts-to-flush 67 | (let [c (chan) 68 | tps (mapv (fn [_] (promise)) (range 3))] 69 | (dotimes [x 2] 70 | (-put! c x (fn [_] nil))) 71 | (-close! c) 72 | (assert= (mapv (partial -take! c) tps) [true true false]) 73 | (assert= (mapv deref tps) [0 1 nil]))) 74 | 75 | (deftest alt-handlers-only-invoke-one-callback 76 | (let [completed (atom 0) 77 | c (chan 5) 78 | [f1 f2] (alt-handlers 79 | [(fn [_] (swap! completed inc)) 80 | (fn [_] (swap! completed inc))])] 81 | (-take! c f1) 82 | (-take! c f2) 83 | (-put! c 1 (fn [_])) 84 | (-put! c 2 (fn [_])) 85 | (st/yield-control) 86 | 87 | (assert= @completed 1))) 88 | -------------------------------------------------------------------------------- /tests/pixie/tests/test-compare.pxi: -------------------------------------------------------------------------------- 1 | (ns pixie.tests.test-compare 2 | (require pixie.test :as t)) 3 | 4 | (t/deftest test-compare-numbers 5 | (t/assert= (compare 1 1) 0) 6 | (t/assert= (compare 1 2) -1) 7 | (t/assert= (compare 1 -1) 1) 8 | 9 | (t/assert= (compare 1 1.0) 0) 10 | (t/assert= (compare 1.0 1) 0) 11 | (t/assert= (compare 1.0 2.0) -1) 12 | (t/assert= (compare 1.0 -1.0) 1) 13 | 14 | (t/assert= (compare 1/2 1/2) 0) 15 | (t/assert= (compare 1/3 1/2) -1) 16 | (t/assert= (compare 1/2 1/3) 1)) 17 | 18 | (t/deftest test-compare-strings 19 | (t/assert= (compare "a" "a") 0) 20 | (t/assert= (compare "a" "b") -1) 21 | (t/assert= (compare "b" "a") 1) 22 | 23 | (t/assert= (compare "aa" "a") 1) 24 | (t/assert= (compare "a" "aa") -1) 25 | 26 | (t/assert= (compare "aa" "b") -1) 27 | (t/assert= (compare "b" "aa") 1) 28 | 29 | (t/assert= (compare "aaaaaaaa" "azaaaaaa") -1) 30 | (t/assert= (compare "azaaaaaa" "aaaaaaaa") 1)) 31 | 32 | (t/deftest test-compare-keywords 33 | (t/assert= (compare :a :a) 0) 34 | (t/assert= (compare :a :b) -1) 35 | (t/assert= (compare :b :a) 1) 36 | (t/assert= (compare :ns/a :ns/a) 0) 37 | (t/assert= (compare :ns/a :ns/b) -1) 38 | (t/assert= (compare :a :aa) -1) 39 | (t/assert= (compare :aa :a) 1)) 40 | 41 | (t/deftest test-compare-symbols 42 | (t/assert= (compare 'a 'a) 0) 43 | (t/assert= (compare 'a 'b) -1) 44 | (t/assert= (compare 'b 'a) 1) 45 | (t/assert= (compare 'ns/a 'ns/a) 0) 46 | (t/assert= (compare 'ns/a 'ns/b) -1) 47 | (t/assert= (compare 'a 'aa) -1) 48 | (t/assert= (compare 'aa 'a) 1)) 49 | 50 | (t/deftest test-compare-vectors 51 | (t/assert= (compare [] []) 0) 52 | (t/assert= (compare [1] []) 1) 53 | (t/assert= (compare [1] [2]) -1) 54 | (t/assert= (compare [1 2] [1 3]) -1) 55 | (t/assert= (compare [:a] [:a]) 0) 56 | (t/assert= (compare [:a] [:b]) -1) 57 | (t/assert= (compare [:a] [:a :a]) -1) 58 | (t/assert= (compare ["a"] ["a"]) 0) 59 | (t/assert= (compare ["a"] ["a" "b"]) -1)) 60 | -------------------------------------------------------------------------------- /tests/pixie/tests/test-compiler.pxi: -------------------------------------------------------------------------------- 1 | (ns pixie.test.test-compiler 2 | (require pixie.test :as t)) 3 | 4 | (t/deftest test-do 5 | (t/assert= (do 1) 1) 6 | (t/assert= (do 1 2) 2) 7 | (t/assert= (do) nil) 8 | (t/assert= (do 1 2 3 4 5 6) 6)) 9 | 10 | (t/deftest test-if 11 | (t/assert= (if true 42 nil) 42) 12 | (t/assert= (if false 42 nil) nil) 13 | (t/assert= (if false 42) nil)) 14 | 15 | (t/deftest test-let 16 | (t/assert= (let [] 1) 1) 17 | (t/assert= (let [x 1]) nil) 18 | (t/assert= (let []) nil)) 19 | 20 | (t/deftest test-lists 21 | (t/assert= (vec '()) []) 22 | (t/assert= (vec '()) ())) 23 | 24 | 25 | (defprotocol IMutable 26 | (mutate! [this])) 27 | 28 | (deftype Foo [x] 29 | IMutable 30 | (mutate! [this] 31 | (let [xold x] 32 | (set-field! this :x 42) 33 | (t/assert (not (= xold x))) 34 | (t/assert (= x 42))))) 35 | 36 | (t/deftest test-deftype-mutables 37 | (mutate! (->Foo 0))) 38 | 39 | (t/deftest test-recur-must-tail 40 | (t/assert-throws? 41 | (eval 42 | '(loop [n 0] 43 | (inc (recur n))))) 44 | 45 | (t/assert-throws? 46 | (eval 47 | '(fn [n] 48 | (if (zero? n) 49 | 1 50 | (* n (recur (dec n))))))) 51 | 52 | (t/assert-throws? 53 | (eval 54 | '(fn [n] 55 | (if (zero? n) 56 | (* n (recur (dec n))) 57 | 1)))) 58 | 59 | (t/assert= 60 | (eval 61 | '(loop [n 0] 62 | (if (= 10 n) 63 | n 64 | (recur (inc n))))) 65 | 10) 66 | 67 | (t/assert= 68 | (eval 69 | '(loop [n 0] 70 | (if (not= 10 n) 71 | (recur (inc n)) 72 | n))) 73 | 10)) 74 | -------------------------------------------------------------------------------- /tests/pixie/tests/test-csp.pxi: -------------------------------------------------------------------------------- 1 | (ns pixie.tests.test-buffers 2 | (require pixie.test :refer :all) 3 | (require pixie.csp :refer :all)) 4 | 5 | 6 | (deftest test-go-blocks-return-values 7 | (assert= (! c) (range 10)) 14 | (close! c))] 15 | (assert= (vec c) (range 10)) 16 | (assert= (! c1 1) 25 | (>! c2 2) 26 | (assert (not (= c1 c2))) 27 | (assert= (! c 1) 33 | (assert= (alts! [c] :default 42) [c 1]))) 34 | 35 | (deftest test-timeout-channel 36 | (let [ts [(timeout 300) 37 | (timeout 200) 38 | (timeout 100)]] 39 | (-> (go 40 | (loop [ts (set ts) 41 | res []] 42 | (if (empty? ts) 43 | res 44 | (let [[p _] (alts! ts)] 45 | (recur (set (remove #{p} ts)) 46 | (conj res p)))))) 47 | Three 1 2 3)) 7 | (def t2 (->Three 1 2 3)) 8 | (def t3 (map->Three {:one 1, :two 2, :three 3})) 9 | (def t4 (->Three 1 2 4)) 10 | (def t5 (->Three 3 4 5)) 11 | 12 | (t/deftest test-satisfies 13 | (foreach [t [t1 t2 t3 t4 t5]] 14 | (t/assert= t t) 15 | (t/assert (satisfies? IRecord t)) 16 | (t/assert (satisfies? IAssociative t)) 17 | (t/assert (satisfies? ILookup t)) 18 | (t/assert (satisfies? IPersistentCollection t)))) 19 | 20 | (t/deftest test-record-pred 21 | (t/assert (record? t1))) 22 | 23 | (t/deftest test-eq 24 | (t/assert= t1 t2) 25 | (t/assert= t2 t3) 26 | (t/assert= t3 t1) 27 | (foreach [t [t1 t2 t3]] 28 | (t/assert (not (= t (assoc t :one 42)))) 29 | (t/assert (not (= t t4))) 30 | (t/assert (not (= t t5))))) 31 | 32 | (t/deftest test-ilookup 33 | (foreach [t [t1 t2 t3]] 34 | (t/assert (satisfies? ILookup t)) 35 | (t/assert= (get t :one) 1) 36 | (t/assert= (get t :two) 2) 37 | (t/assert= (get t :three) 3) 38 | (t/assert= (get t :oops) nil) 39 | (t/assert= (get t :oops 'not-found) 'not-found))) 40 | 41 | (t/deftest test-iassociative 42 | (foreach [t [t1 t2 t3]] 43 | (t/assert (satisfies? IAssociative t)) 44 | (t/assert= t (assoc t4 :three 3)) 45 | (let [t' (assoc t :one 42) 46 | t-oops (try (assoc t :oops 'never-found) 47 | (catch ex t))] 48 | (t/assert (not (= t t'))) 49 | (t/assert= (get t' :one) 42) 50 | (t/assert= t t-oops) 51 | (t/assert (not (contains? t-oops :oops))) 52 | (t/assert= (get t-oops :oops) nil)) 53 | 54 | (t/assert (contains? t :one)) 55 | (t/assert (contains? t :two)) 56 | (t/assert (contains? t :three)))) 57 | 58 | (t/deftest test-ipersistentcoll 59 | (t/assert= 11 (-> t1 (conj [:one 11]) :one)) 60 | (t/assert= 11 (-> t1 (conj (map-entry :one 11)) :one))) 61 | 62 | (t/deftest test-record-metadata 63 | (t/assert= nil (meta t1)) 64 | (t/assert= :foo (-> t1 (with-meta :foo) meta))) 65 | 66 | 67 | (t/deftest ireduce [] 68 | (t/assert= [[:one 1] [:two 2] [:three 3]] (reduce conj [] t1)) 69 | (t/assert= [1 2 3] (vals t1)) 70 | (t/assert= [:one :two :three] (keys t1))) 71 | 72 | (t/deftest icounted [] 73 | (t/assert= (count t1) 3)) 74 | 75 | (t/deftest seqable [] 76 | (t/assert= (key (first (seq t1))) :one) 77 | (t/assert= (val (first (seq t1))) 1) 78 | (t/assert (instance? LazySeq (seq t1)))) 79 | -------------------------------------------------------------------------------- /tests/pixie/tests/test-deftype.pxi: -------------------------------------------------------------------------------- 1 | (ns pixie.test.test-deftype 2 | (require pixie.test :as t)) 3 | 4 | (deftype Simple [:val]) 5 | (deftype Simple2 [val]) 6 | 7 | (t/deftest test-simple 8 | (let [o1 (->Simple 1) 9 | o2 (->Simple 2)] 10 | (foreach [obj-and-val [[o1 1] [o2 2]]] 11 | (let [o (first obj-and-val) 12 | v (second obj-and-val)] 13 | (t/assert= (get-field o :val) v))))) 14 | 15 | (deftype MagicalVectorMap [] IMap IVector) 16 | 17 | (t/deftest test-satisfies 18 | (let [mvm (->MagicalVectorMap)] 19 | (t/assert (satisfies? IVector mvm)) 20 | (t/assert (satisfies? IMap mvm)))) 21 | 22 | (deftype Count [:val] 23 | ICounted 24 | (-count [self] val)) 25 | 26 | (deftype Count2 [val] 27 | ICounted 28 | (-count [self] val)) 29 | 30 | (t/deftest test-extend 31 | (let [o1 (->Count 1) 32 | o2 (->Count 2)] 33 | (foreach [obj-and-val [[o1 1] [o2 2]]] 34 | (let [o (first obj-and-val) 35 | v (second obj-and-val)] 36 | (t/assert= (get-field o :val) v) 37 | (t/assert (satisfies? ICounted o)) 38 | (t/assert= (-count o) v) 39 | (t/assert= (count o) v))))) 40 | 41 | (defprotocol TestObject 42 | (add [self x & args]) 43 | (one-plus [self x & xs])) 44 | 45 | (deftype Three [:one :two :three] 46 | TestObject 47 | (add [self x & args] 48 | (apply + x args)) 49 | (one-plus [self x & xs] 50 | (apply + one x xs)) 51 | ICounted 52 | (-count [self] (+ one two three))) 53 | 54 | (deftype Three2 [one two three] 55 | TestObject 56 | (add [self x & args] 57 | (apply + x args)) 58 | (one-plus [self x & xs] 59 | (apply + one x xs)) 60 | ICounted 61 | (-count [self] (+ one two three))) 62 | 63 | (t/deftest test-complex 64 | (let [o1 (->Three 1 2 3) 65 | o2 (->Three2 3 4 5)] 66 | (foreach [obj-and-vals [[o1 1 2 3] [o2 3 4 5]]] 67 | (let [o (first obj-and-vals) 68 | one (second obj-and-vals) 69 | two (third obj-and-vals) 70 | three (fourth obj-and-vals)] 71 | (t/assert= (get-field o :one) one) 72 | (t/assert= (get-field o :two) two) 73 | (t/assert= (get-field o :three) three) 74 | 75 | (t/assert (satisfies? ICounted o)) 76 | (t/assert= (-count o) (+ one two three)) 77 | (t/assert= (count o) (+ one two three)) 78 | 79 | (t/assert= (add o 21 21) 42) 80 | (t/assert= (one-plus o 9) (+ one 9)))))) 81 | -------------------------------------------------------------------------------- /tests/pixie/tests/test-destructuring.pxi: -------------------------------------------------------------------------------- 1 | (ns pixie.test.test-destructuring 2 | (require pixie.test :as t)) 3 | 4 | (t/deftest test-let-simple 5 | (t/assert= (let [x 1 y 2 z 3] [x y z]) [1 2 3]) 6 | (t/assert= (let [x 1 y 2 z 3] [x y z]) (let* [x 1 y 2 z 3] [1 2 3])) 7 | 8 | (t/assert= (let [x 1 x 2] x) 2) 9 | (t/assert= (let [x 1 y 2 x 3] x) 3) 10 | 11 | (t/assert= (let [x 1] (let [x 2] x)) 2)) 12 | 13 | (t/deftest test-let-vector-simple 14 | (t/assert= (let [[x y z] [1 2 3]] [x y z]) [1 2 3]) 15 | (t/assert= (let [[x y z] [1 2 3 4]] [x y z]) [1 2 3]) 16 | 17 | (t/assert= (let [[x y z & rest] [1 2 3 4]] 18 | [x y z rest]) 19 | [1 2 3 '(4)]) 20 | (t/assert= (let [[x y z & rest] [1 2]] 21 | [x y z rest]) 22 | [1 2 nil nil])) 23 | 24 | (t/deftest test-let-vector-nested 25 | (t/assert= (let [[[x y] z & rest] [[1 2] 3 4]] 26 | [x y z rest]) 27 | [1 2 3 '(4)]) 28 | 29 | (t/assert= (let [[[x [y]] z & rest] [[1 [2 3]] 4 5]] 30 | [x y z rest]) 31 | [1 2 4 '(5)])) 32 | 33 | (t/deftest test-let-vector-rest 34 | (t/assert= (let [[x y & [z & rest]] [1 2 3 4 5]] 35 | [x y z rest]) 36 | [1 2 3 '(4 5)])) 37 | 38 | (t/deftest test-let-map 39 | (t/assert= (let [{a :a, b :b, {c :c :as s} :d :as m} {:a 1, :b 2, :d {:c 3}}] 40 | [a b c s m]) 41 | [1 2 3 {:c 3} {:a 1, :b 2, :d {:c 3}}]) 42 | 43 | (t/assert= (let [{:keys [a b c] :as m} {:a 1, :b 2, :c 3, :d 4}] 44 | [a b c (:d m)]) 45 | [1 2 3 4])) 46 | 47 | (t/deftest test-let-map-defaults 48 | (t/assert= (let [{a :a :or {a 42}} {:a 1}] a) 1) 49 | (t/assert= (let [{a :a :or {a 42}} {}] a) 42) 50 | 51 | (t/assert= (let [{a :a :or {a 42}} {:a nil}] a) nil) 52 | (t/assert= (let [{a :a :or {a 42}} {:a false}] a) false) 53 | 54 | (t/assert= (let [{:keys [a], :or {a 42}} {:a 1}] a) 1) 55 | (t/assert= (let [{:keys [a], :or {a 42}} {}] a) 42)) 56 | 57 | (t/deftest test-fn-simple 58 | (t/assert= ((fn [[x y & rest]] [x y rest]) [1 2 3 4 5]) [1 2 '(3 4 5)]) 59 | (t/assert= ((fn [{a :a, b :b :as m}] [a b m]) {:a 1, :b 2, :answer 42}) [1 2 {:a 1, :b 2, :answer 42}]) 60 | 61 | (t/assert= ((fn [[[x y] z & rest]] [x y z rest]) [[1 2] 3 4 5]) [1 2 3 '(4 5)])) 62 | 63 | (t/deftest test-fn-multiple-args 64 | (t/assert= ((fn [[x y z] {:keys [a b c]}] [x y z a b c]) [1 2 3] {:a 4, :b 5, :c 6}) 65 | [1 2 3 4 5 6])) 66 | 67 | (t/deftest test-fn-rest-args 68 | (let [f1 (fn [& [status]] (or status :yay))] 69 | (t/assert= (f1) :yay) 70 | (t/assert= (f1 :nay) :nay) 71 | (t/assert= (f1 :nay :something-else :whatever) :nay)) 72 | (let [f2 (fn [x & [y]] 73 | (+ x (or y 1)))] 74 | (t/assert= (f2 41) 42) 75 | (t/assert= (f2 21 21) 42) 76 | (t/assert= (f2 21 21 :something-else :whatever) 42))) 77 | -------------------------------------------------------------------------------- /tests/pixie/tests/test-docs.pxi: -------------------------------------------------------------------------------- 1 | (ns pixie.tests.test-docs 2 | (require pixie.test :as t)) 3 | 4 | ; validate the examples in the docs by checking whether the included 5 | ; results match the actual results you get by evaluating the examples. 6 | 7 | (defn check-examples [ns] 8 | (let [ns (the-ns ns) 9 | syms (keys (ns-map ns))] 10 | (doseq [sym syms] 11 | (let [meta (meta @(resolve-in ns sym)) 12 | examples (get meta :examples)] 13 | (doseq [example examples] 14 | (if (contains? example 2) 15 | (t/assert= (eval (read-string (first example))) 16 | (third example)) 17 | (eval (read-string (first example))))))))) 18 | 19 | (t/deftest test-stdlib-docs 20 | (check-examples 'pixie.stdlib)) 21 | 22 | (t/deftest test-string-docs 23 | (load-ns 'pixie.string) 24 | (check-examples 'pixie.string)) 25 | -------------------------------------------------------------------------------- /tests/pixie/tests/test-errors.pxi: -------------------------------------------------------------------------------- 1 | (ns pixie.test.test-errors 2 | (:require [pixie.test :refer :all])) 3 | 4 | (deftest test-add-exception-info 5 | (try 6 | (try 7 | (+ 1 "foo") 8 | (catch ex 9 | (throw (add-exception-info ex "My Msg" :my-data)))) 10 | (catch ex 11 | (let [filter-fn (fn [mp] 12 | (and (= (:type mp) :extra) 13 | (= (:msg mp) "My Msg") 14 | (= (:data mp) :my-data)))] 15 | (assert= 1 (count (filter filter-fn ex))))))) 16 | -------------------------------------------------------------------------------- /tests/pixie/tests/test-ffi.pxi: -------------------------------------------------------------------------------- 1 | (ns pixie.tests.test-ffi 2 | (require pixie.test :as t) 3 | (require pixie.math :as m) 4 | (require pixie.ffi-infer :as i)) 5 | 6 | 7 | 8 | (t/deftest test-buffer-ffi 9 | (let [fp (fopen "README.md" "r") 10 | b (buffer 1024)] 11 | (t/assert= 10 (fread b 1 10 fp)) 12 | (t/assert= 91 (nth b 0)))) 13 | 14 | (t/deftest test-arity-check 15 | (let [sscanf-2 (ffi-fn libc "sscanf" [CCharP CCharP] CInt) 16 | sscanf-* (ffi-fn libc "sscanf" [CCharP CCharP] CInt :variadic? true)] 17 | (try 18 | (sscanf-2 "too few arguments") 19 | (t/assert false) 20 | (catch ex (t/assert= (type ex) RuntimeException))) 21 | 22 | (try 23 | (sscanf-2 "too" "many" "arguments") 24 | (t/assert false) 25 | (catch ex (t/assert= (type ex) RuntimeException))) 26 | 27 | (try 28 | (sscanf-* "too few arguments") 29 | (t/assert false) 30 | (catch ex (t/assert= (type ex) RuntimeException))) 31 | 32 | (sscanf-2 "string" "fmt") 33 | (sscanf-* "string" "fmt") 34 | (sscanf-* "string" "fmt" "optional arg1" "optional arg2") 35 | (t/assert true))) 36 | 37 | (t/deftest test-ffi-infer 38 | (t/assert= 0.5 (m/asin (m/sin 0.5)))) 39 | 40 | (t/deftest test-cdouble 41 | (i/with-config {:library "m" 42 | :cxx-flags ["-lm"] 43 | :includes ["math.h"]} 44 | (i/defcfn sinf) 45 | (i/defcfn asinf) 46 | (i/defcfn cosf) 47 | (i/defcfn powf)) 48 | (t/assert= 0.5 (asinf (sinf 0.5))) 49 | (t/assert= 1.0 (+ (powf (sinf 0.5) 2.0) (powf (cosf 0.5) 2.0)))) 50 | 51 | (t/deftest test-invalid-float-argument 52 | (t/assert-throws? (m/sin "nil")) 53 | (t/assert-throws? (m/sin nil)) 54 | ) 55 | 56 | (t/deftest test-ffi-callbacks 57 | (let [MAX 255 58 | qsort-cb (pixie.ffi/ffi-callback [CVoidP CVoidP] CInt) 59 | qsort (ffi-fn libc "qsort" [CVoidP CInt CInt qsort-cb] CInt) 60 | 61 | buf (buffer MAX)] 62 | (using [cb (pixie.ffi/ffi-prep-callback qsort-cb (fn [x y] 63 | (if (> (pixie.ffi/unpack x 0 CUInt8) 64 | (pixie.ffi/unpack y 0 CUInt8)) 65 | -1 66 | 1)))] 67 | (dotimes [x MAX] 68 | (pixie.ffi/pack! buf x CUInt8 x)) 69 | (qsort buf MAX 1 cb) 70 | 71 | (dotimes [x (dec MAX)] 72 | (t/assert (> (pixie.ffi/unpack buf x CUInt8) 73 | (pixie.ffi/unpack buf (inc x) CUInt8))))))) 74 | 75 | (t/deftest test-size 76 | (t/assert= (pixie.ffi/struct-size (pixie.ffi/c-struct "struct" 1234 [])) 1234)) 77 | 78 | 79 | (t/deftest test-double-coercion 80 | (t/assert= (m/sin 1) (m/sin 1.0)) 81 | (let [big (reduce * 1 (range 1 100))] 82 | (t/assert= (m/sin big) (m/sin (float big)))) 83 | (t/assert= (m/sin (/ 1 2)) (m/sin (float (/ 1 2))))) 84 | -------------------------------------------------------------------------------- /tests/pixie/tests/test-fns.pxi: -------------------------------------------------------------------------------- 1 | (ns pixie.test.test-fns 2 | (require pixie.test :as t)) 3 | 4 | (t/deftest test-fn-literals 5 | (t/assert= (#(+ 3 4)) 7) 6 | (t/assert= (#(+ 3 %) 4) 7) 7 | (t/assert= (#(+ 3 %1) 4) 7) 8 | (t/assert= (#(+ %1 3) 4) 7) 9 | (t/assert= (#(+ %1 %1) 3.5) 7.0) 10 | (t/assert= (#(+ %1 %2) 3 4) 7) 11 | (t/assert= (#(- %2 %1) 3 4) 1) 12 | (t/assert= (#(+ %1 %1 %2 %2) 1.5 2) 7.0) 13 | (t/assert= (#(+ %1 %3) 3 'ignored 4) 7) 14 | (t/assert= (#(- %3 %1) 3 'ignored 4) 1) 15 | (t/assert= (#(apply + %1 %2 %&) 1 2 3 4 5) (+ 1 2 3 4 5))) 16 | 17 | ;; Note these tests are for functions of type 'Code'. 18 | (t/deftest test-code-arity-errors 19 | (let [arity-0 (fn arity-0 []) 20 | arity-1 (fn arity-1 [a]) 21 | arity-2 (fn arity-2 [a b]) 22 | arity-0-or-1 (fn arity-0-or-1 ([]) ([a])) 23 | arity-1-or-3 (fn arity-1-or-3 ([a]) ([a b c])) 24 | arity-0-or-1-or-3-or-more 25 | (fn arity-0-or-1-or-3-or-more ([]) ([a]) ([a b c & more]))] 26 | (t/assert-throws? RuntimeException 27 | "Invalid number of arguments 1 for function 'arity-0'. Expected 0" 28 | (arity-0 :foo)) 29 | (t/assert-throws? RuntimeException 30 | "Invalid number of arguments 2 for function 'arity-0'. Expected 0" 31 | (arity-0 :foo :bar)) 32 | (t/assert-throws? RuntimeException 33 | "Invalid number of arguments 0 for function 'arity-1'. Expected 1" 34 | (arity-1)) 35 | (t/assert-throws? RuntimeException 36 | "Invalid number of arguments 2 for function 'arity-1'. Expected 1" 37 | (arity-1 :foo :bar)) 38 | (t/assert-throws? RuntimeException 39 | "Invalid number of arguments 0 for function 'arity-2'. Expected 2" 40 | (arity-2)) 41 | (t/assert-throws? RuntimeException 42 | "Invalid number of arguments 1 for function 'arity-2'. Expected 2" 43 | (arity-2 :foo)) 44 | (t/assert-throws? RuntimeException 45 | "Wrong number of arguments 2 for function 'arity-0-or-1'. Expected 0 or 1" 46 | (arity-0-or-1 :foo :bar)) 47 | (t/assert-throws? RuntimeException 48 | "Wrong number of arguments 3 for function 'arity-0-or-1'. Expected 0 or 1" 49 | (arity-0-or-1 :foo :bar :baz)) 50 | (t/assert-throws? RuntimeException 51 | "Wrong number of arguments 2 for function 'arity-1-or-3'. Expected 1 or 3" 52 | (arity-1-or-3 :foo :bar)) 53 | (t/assert-throws? RuntimeException 54 | "Wrong number of arguments 0 for function 'arity-1-or-3'. Expected 1 or 3" 55 | (arity-1-or-3)) 56 | (t/assert-throws? RuntimeException 57 | "Wrong number of arguments 2 for function 'arity-0-or-1-or-3-or-more'. Expected 0, 1 or 3+" 58 | (arity-0-or-1-or-3-or-more :foo :bar)))) 59 | 60 | (t/deftest test-code-arities) 61 | -------------------------------------------------------------------------------- /tests/pixie/tests/test-forms.pxi: -------------------------------------------------------------------------------- 1 | (ns pixie.tests.test-forms 2 | (require pixie.test :as t)) 3 | 4 | (t/deftest test-when 5 | (t/assert= (when false :never) nil) 6 | (t/assert= (when nil :never) nil) 7 | (t/assert= (when (= 3 4) :never) nil) 8 | 9 | (t/assert= (when true :always) :always) 10 | (t/assert= (when (+ 3 4) :always) :always) 11 | (t/assert= (when {} :always) :always) 12 | 13 | (let [c (atom 0)] 14 | (when (= 3 3) 15 | (swap! c inc) 16 | (swap! c inc) 17 | (swap! c inc)) 18 | (t/assert= @c 3))) 19 | 20 | (t/deftest test-when-not 21 | (t/assert= (when-not false :always) :always) 22 | (t/assert= (when-not nil :always) :always) 23 | (t/assert= (when-not (= 3 4) :always) :always) 24 | 25 | (t/assert= (when-not true :never) nil) 26 | (t/assert= (when-not (+ 3 4) :never) nil) 27 | (t/assert= (when-not {} :never) nil) 28 | 29 | (let [c (atom 0)] 30 | (when-not (= 3 4) 31 | (swap! c inc) 32 | (swap! c inc) 33 | (swap! c inc)) 34 | (t/assert= @c 3))) 35 | 36 | (t/deftest test-when-let 37 | (t/assert= (when-let [v false] :never) nil) 38 | (t/assert= (when-let [v nil] :never) nil) 39 | (t/assert= (when-let [v (= 3 4)] :never) nil) 40 | 41 | (t/assert= (when-let [v true] :always) :always) 42 | (t/assert= (when-let [v (+ 3 4)] :always) :always) 43 | (t/assert= (when-let [v {}] :always) :always) 44 | 45 | (let [c (atom 0)] 46 | (when-let [v @c] 47 | (swap! c inc) 48 | (swap! c inc) 49 | (swap! c inc)) 50 | (t/assert= @c 3))) 51 | 52 | (t/deftest test-when-let-destructuring 53 | (t/assert= (when-let [[x y & z] false] :yay) nil) 54 | (t/assert= (when-let [[x y & z] nil] :yay) nil) 55 | (t/assert= (when-let [{:keys [a b]} nil] :yay) nil) 56 | 57 | (t/assert= (when-let [[x y & z] [1 2 3]] :yay) :yay) 58 | (t/assert= (when-let [[x y & z] [1 2 3]] [x y z]) [1 2 '(3)]) 59 | (t/assert= (when-let [{:keys [a b]} {}] :yay) :yay) 60 | (t/assert= (when-let [{:keys [a b]} {}] [a b]) [nil nil]) 61 | (t/assert= (when-let [{:keys [a b]} {:a 1, :b 41}] [a b]) [1 41])) 62 | 63 | (t/deftest test-if-let 64 | (t/assert= (if-let [v false] :yay :nay) :nay) 65 | (t/assert= (if-let [v false] :yay) nil) 66 | (t/assert= (if-let [v nil] :yay :nay) :nay) 67 | (t/assert= (if-let [v nil] :yay) nil) 68 | (t/assert= (if-let [v (= 3 4)] :yay :nay) :nay) 69 | (t/assert= (if-let [v (= 3 4)] :yay) nil) 70 | 71 | (t/assert= (if-let [v true] :yay :nay) :yay) 72 | (t/assert= (if-let [v true] :yay) :yay) 73 | (t/assert= (if-let [v (+ 3 4)] v :nay) 7) 74 | (t/assert= (if-let [v (+ 3 4)] v) 7) 75 | (t/assert= (if-let [v {}] v :nay) {}) 76 | (t/assert= (if-let [v {}] v) {})) 77 | 78 | (t/deftest test-if-let-destructuring 79 | (t/assert= (if-let [[x y & z] false] :yay :nay) :nay) 80 | (t/assert= (if-let [[x y & z] false] :yay) nil) 81 | (t/assert= (if-let [[x y & z] nil] :yay :nay) :nay) 82 | (t/assert= (if-let [[x y & z] nil] :yay) nil) 83 | (t/assert= (if-let [{:keys [a b]} nil] :yay :nay) :nay) 84 | (t/assert= (if-let [{:keys [a b]} nil] :yay) nil) 85 | 86 | (t/assert= (if-let [[x y & z] [1 2 3]] :yay :nay) :yay) 87 | (t/assert= (if-let [[x y & z] [1 2 3]] [x y z] :nay) [1 2 '(3)]) 88 | (t/assert= (if-let [{:keys [a b]} {}] :yay :nay) :yay) 89 | (t/assert= (if-let [{:keys [a b]} {}] [a b] :nay) [nil nil]) 90 | (t/assert= (if-let [{:keys [a b]} {:a 1, :b 41}] [a b] :nay) [1 41])) 91 | -------------------------------------------------------------------------------- /tests/pixie/tests/test-fs.pxi: -------------------------------------------------------------------------------- 1 | (ns pixie.tests.test-fs 2 | (require pixie.test :as t) 3 | (require pixie.fs :as fs)) 4 | 5 | (t/deftest test-file-comparisons 6 | "All these paths are the same" 7 | (let [dir-a "tests/pixie/tests/fs/parent" 8 | dir-b "tests/pixie/tests/fs/../fs/parent" 9 | dir-c "tests/pixie/tests/fs/../fs/parent/../../fs/parent" 10 | file-a (str dir-a "/foo.txt") 11 | file-b (str dir-b "/foo.txt") 12 | file-c (str dir-c "/foo.txt")] 13 | (t/assert= (= (fs/dir dir-a) 14 | (fs/dir dir-b) 15 | (fs/dir dir-c)) 16 | true) 17 | 18 | (t/assert= (= (fs/file file-a) 19 | (fs/file file-b) 20 | (fs/file file-c)) 21 | true) 22 | 23 | (t/assert= (count (distinct [(fs/dir dir-a) 24 | (fs/dir dir-b) 25 | (fs/dir dir-c)])) 26 | 1) 27 | (t/assert= (count (distinct [(fs/file file-a) 28 | (fs/file file-b) 29 | (fs/file file-c)])) 30 | 1))) 31 | 32 | (comment 33 | ; Although these pass locally, they don't appear to work on travis-ci (segfault) 34 | (t/deftest test-walking 35 | (let [dir-a "tests/pixie/tests/fs"] 36 | (t/assert= (set (fs/walk (fs/dir dir-a))) 37 | #{(fs/dir (str dir-a "/parent")) 38 | (fs/file (str dir-a "/parent/foo.txt")) 39 | (fs/file (str dir-a "/parent/bar.txt")) 40 | (fs/dir (str dir-a "/parent/child")) 41 | (fs/file (str dir-a "/parent/child/foo.txt")) 42 | (fs/file (str dir-a "/parent/child/bar.txt"))}) 43 | 44 | (t/assert= (set (fs/walk-files (fs/dir dir-a))) 45 | #{(fs/file (str dir-a "/parent/foo.txt")) 46 | (fs/file (str dir-a "/parent/bar.txt")) 47 | (fs/file (str dir-a "/parent/child/foo.txt")) 48 | (fs/file (str dir-a "/parent/child/bar.txt"))}) 49 | 50 | (t/assert= (set (fs/walk-dirs (fs/dir dir-a))) 51 | #{(fs/dir (str dir-a "/parent")) 52 | (fs/dir (str dir-a "/parent/child"))}))) 53 | 54 | (t/deftest test-list 55 | (let [dir-a "tests/pixie/tests/fs/parent"] 56 | (t/assert= (set (fs/list (fs/dir dir-a))) 57 | #{(fs/file (str dir-a "/foo.txt")) 58 | (fs/file (str dir-a "/bar.txt")) 59 | (fs/dir (str dir-a "/child"))}))) 60 | ) 61 | 62 | (t/deftest test-rel? 63 | (let [dir-a (fs/dir "tests/pixie/tests/fs") 64 | dir-b (fs/dir "tests/pixie/tests/fs/parent") 65 | dir-c (fs/dir "tests/pixie/tests/fs/parent/child") 66 | file-a (fs/file "tests/pixie/tests/fs/parent/foo.txt") 67 | file-b (fs/file "tests/pixie/tests/fs/parent/bar.txt") 68 | file-c (fs/file "tests/pixie/tests/fs/parent/child/foo.txt")] 69 | ;; Test directory-directory comparisons 70 | (t/assert= (fs/rel dir-a dir-a) ".") 71 | (t/assert= (fs/rel dir-a dir-b) "..") 72 | (t/assert= (fs/rel dir-a dir-c) "../..") 73 | (t/assert= (fs/rel dir-c dir-b) "child") 74 | (t/assert= (fs/rel dir-c dir-a) "parent/child") 75 | ;; Test file-file comparisons 76 | (t/assert= (fs/rel file-a file-a) ".") 77 | (t/assert= (fs/rel file-a file-b) "../foo.txt") 78 | (t/assert= (fs/rel file-a file-c) "../../foo.txt") 79 | (t/assert= (fs/rel file-c file-a) "../child/foo.txt") 80 | (t/assert= (fs/rel file-c file-b) "../child/foo.txt") 81 | ;; Test file-directory comparisons 82 | (t/assert= (fs/rel file-a dir-a) "parent/foo.txt") 83 | (t/assert= (fs/rel file-a dir-b) "foo.txt") 84 | (t/assert= (fs/rel file-a dir-c) "../foo.txt"))) 85 | 86 | (t/deftest test-basename? 87 | (let [dir-a (fs/dir "tests/pixie/tests/fs") 88 | dir-b (fs/dir "tests/pixie/tests/fs/parent") 89 | dir-c (fs/dir "tests/pixie/tests/fs/parent/child") 90 | file-a (fs/file "tests/pixie/tests/fs/parent/foo.txt") 91 | file-b (fs/file "tests/pixie/tests/fs/parent/bar.txt") 92 | file-c (fs/file "tests/pixie/tests/fs/parent/child/foo.txt")] 93 | ;; Test directory-directory comparisons 94 | (t/assert= (fs/basename dir-a) "fs") 95 | (t/assert= (fs/basename dir-b) "parent") 96 | (t/assert= (fs/basename dir-c) "child") 97 | (t/assert= (fs/basename file-a) "foo.txt") 98 | (t/assert= (fs/basename file-b) "bar.txt") 99 | (t/assert= (fs/basename file-c) "foo.txt"))) 100 | 101 | (t/deftest test-exists? 102 | (let [real-dir (fs/dir "tests/pixie/tests/fs/parent") 103 | fake-dir (fs/dir "tests/pixie/tests/fs/parent/fake-dir") 104 | fake-file (fs/dir "tests/pixie/tests/fs/parent/fake-file")] 105 | (t/assert= (fs/exists? real-dir) true) 106 | (t/assert= (fs/exists? fake-dir) false) 107 | (t/assert= (fs/exists? fake-file) false))) 108 | 109 | (t/deftest test-size 110 | (let [file-with-content (fs/file "tests/pixie/tests/fs/parent/foo.txt") 111 | file-without-content (fs/file "tests/pixie/tests/fs/parent/bar.txt") 112 | fake-file (fs/file "tests/pixie/tests/fs/parent/fake-file")] 113 | (t/assert= (fs/size file-with-content) 4) 114 | (t/assert= (fs/size file-without-content) 0) 115 | (t/assert-throws? (fs/size fake-file)))) 116 | 117 | (t/deftest test-permissions 118 | (let [file (fs/file "tests/pixie/tests/fs/parent/foo.txt") 119 | fake-file (fs/file "tests/pixie/tests/fs/parent/fake-file")] 120 | ; because Travis seems to change the permissions of some files... 121 | (let [perms (fs/permissions file)] 122 | (t/assert= (count perms) 3) 123 | (t/assert (instance? String perms)) 124 | (t/assert= (set (seq perms)) #{\6 \4})) 125 | (t/assert-throws? (fs/permissions fake-file)))) 126 | -------------------------------------------------------------------------------- /tests/pixie/tests/test-io-utf8.txt: -------------------------------------------------------------------------------- 1 | I love 🍺 . This is a thumbs up 👍 2 | -------------------------------------------------------------------------------- /tests/pixie/tests/test-io.txt: -------------------------------------------------------------------------------- 1 | This is a test file used in testing the io routines. Please do not remove it. 2 | Second line. 3 | -------------------------------------------------------------------------------- /tests/pixie/tests/test-ith.pxi: -------------------------------------------------------------------------------- 1 | (ns pixie.tests.test-ith 2 | (require pixie.test :as t)) 3 | 4 | (t/deftest test-ith 5 | (let [v [1 2 3 4 5] 6 | l '(1 2 3 4 5) 7 | r (range 1 6)] 8 | (t/assert= (ith [1 2 3 4 5] 0) 1) 9 | (t/assert= (ith v 0) 1) 10 | (t/assert= (ith v 0) (nth v 0)))) 11 | 12 | (t/deftest test-ith-nil 13 | (t/assert= (ith nil 0) nil) 14 | (t/assert= (ith nil 1) nil) 15 | (t/assert= (ith nil -1) nil)) 16 | 17 | (t/deftest test-ith-empty-always-oob 18 | (t/assert= "Index out of Range" (try (ith [] 0) (catch e (ex-msg e)))) 19 | (t/assert= "Index out of Range" (try (ith [] 1) (catch e (ex-msg e)))) 20 | (t/assert= "Index out of Range" (try (ith [] -1) (catch e (ex-msg e)))) 21 | (t/assert= "Index out of Range" (try (ith '() 0) (catch e (ex-msg e)))) 22 | (t/assert= "Index out of Range" (try (ith '() 1) (catch e (ex-msg e)))) 23 | (t/assert= "Index out of Range" (try (ith '() -1) (catch e (ex-msg e)))) 24 | (t/assert= "Index out of Range" (try (ith (range 0 0) 0) (catch e (ex-msg e)))) 25 | (t/assert= "Index out of Range" (try (ith (range 0 0) 1) (catch e (ex-msg e)))) 26 | (t/assert= "Index out of Range" (try (ith (range 0 0) -1) (catch e (ex-msg e))))) 27 | 28 | (t/deftest test-ith-out-of-bounds 29 | (let [v [1 2 3 4 5] 30 | l '(1 2 3 4 5) 31 | r (range 1 6)] 32 | (t/assert= "Index out of Range" (try (ith v 5) (catch e (ex-msg e)))) 33 | (t/assert= "Index out of Range" (try (ith l 5) (catch e (ex-msg e)))) 34 | (t/assert= "Index out of Range" (try (ith r 5) (catch e (ex-msg e)))) 35 | (t/assert= "Index out of Range" (try (ith v -6) (catch e (ex-msg e)))) 36 | (t/assert= "Index out of Range" (try (ith l -6) (catch e (ex-msg e)))) 37 | (t/assert= "Index out of Range" (try (ith r -6) (catch e (ex-msg e)))))) 38 | 39 | (t/deftest test-ith-doseq 40 | (let [v [1 2 3 4 5] 41 | l '(1 2 3 4 5) 42 | r (range 1 6)] 43 | (doseq [i (range 0 5)] 44 | (t/assert= (ith v i) (nth v i)) 45 | (t/assert= (ith l i) (nth l i)) 46 | (t/assert= (ith r i) (nth r i))) 47 | (doseq [i (range -5 0)] 48 | (t/assert= (ith v i) (nth v (+ 5 i))) 49 | (t/assert= (ith l i) (nth l (+ 5 i))) 50 | (t/assert= (ith r i) (nth r (+ 5 i)))))) 51 | 52 | -------------------------------------------------------------------------------- /tests/pixie/tests/test-keywords.pxi: -------------------------------------------------------------------------------- 1 | (ns pixie.tests.test-keywords 2 | (require pixie.test :as t)) 3 | 4 | (t/deftest keyword-invoke 5 | (let [m {:a 1, :b 2, :c 3}] 6 | (t/assert= (:a m) 1) 7 | (t/assert= (:b m) 2) 8 | (t/assert= (:c m) 3) 9 | (t/assert= (:d m) nil) 10 | (t/assert= (:d m :foo) :foo))) 11 | 12 | (t/deftest keyword-namespace 13 | (t/assert= (namespace :foo/bar) "foo") 14 | (t/assert= (namespace :cat/dog) "cat") 15 | (t/assert= (namespace ::foo) "pixie.tests.test-keywords")) 16 | 17 | (t/deftest fqd-keywords 18 | (t/assert-throws? (read-string "::x/bar")) 19 | (t/assert-throws? (read-string "::a.b/foo")) 20 | (refer-ns 'my.other.ns 'my.fake.core 'fake) 21 | (binding [*ns* (the-ns 'my.other.ns)] 22 | (t/assert= :my.fake.core/foo (read-string "::fake/foo")) 23 | (t/assert= :my.fake.core/foo (read-string "::my.fake.core/foo")) 24 | (t/assert-throws? (read-string "::f/foo")))) 25 | 26 | (t/deftest keyword-equality 27 | (t/assert= :foo/bar :foo/bar) 28 | (t/assert= (not= :foo/bar :cat/bar) true) 29 | (t/assert= (not= :foo/cat :foo/dog) true)) 30 | 31 | (t/deftest keyword-reader 32 | (t/assert= (read-string ":1") :1) 33 | (t/assert= (read-string ":1") :1) 34 | (t/assert= (read-string ":1.0") :1.0) 35 | (t/assert= (read-string ":foo") :foo) 36 | (t/assert= (read-string ":1foo") :1foo) 37 | (t/assert= (read-string ":foo/bar") :foo/bar) 38 | (t/assert= (read-string ":1foo/1bar") :1foo/1bar) 39 | (t/assert= (read-string ":nil") :nil) 40 | (t/assert= (read-string ":true") :true) 41 | (t/assert= (read-string ":false") :false) 42 | 43 | ;; We are reading at runtime so the namespace isn't 44 | ;; going to be pixie.test.test-keywords. Its probably 45 | ;; 'user but lets explicitly set it. 46 | ;; The refer-ns is to initialize a new space 47 | (refer-ns 'my.other.ns 'my.fake.core 'fake) 48 | (binding [*ns* (the-ns 'my.other.ns)] 49 | (t/assert= (read-string "::1") :my.other.ns/1) 50 | (t/assert= (read-string "::1.0") :my.other.ns/1.0) 51 | (t/assert= (read-string "::foo") :my.other.ns/foo) 52 | (t/assert= (read-string "::true") :my.other.ns/true))) 53 | 54 | (t/deftest string-to-keyword 55 | (t/assert= (keyword "1") :1) 56 | (t/assert= (keyword "1") :1) 57 | (t/assert= (keyword "1.0") :1.0) 58 | (t/assert= (keyword "foo") :foo) 59 | (t/assert= (keyword "1foo") :1foo) 60 | (t/assert= (keyword "foo/bar") :foo/bar) 61 | (t/assert= (keyword "1foo/1bar") :1foo/1bar) 62 | (t/assert= (keyword "nil") :nil) 63 | (t/assert= (keyword "true") :true) 64 | (t/assert= (keyword "false") :false) 65 | (t/assert-throws? (keyword 1)) 66 | (t/assert-throws? (keyword :a)) 67 | (t/assert-throws? (keyword 'a)) 68 | (t/assert-throws? (keyword nil)) 69 | (t/assert-throws? (keyword true))) 70 | -------------------------------------------------------------------------------- /tests/pixie/tests/test-macros.pxi: -------------------------------------------------------------------------------- 1 | (ns collections.test-macros 2 | (require pixie.test :as t)) 3 | 4 | (t/deftest hashmap-unquote 5 | (let [x 10 k :boop] 6 | (t/assert= (-eq `{:x ~x} {:x 10}) true) 7 | (t/assert= (-eq `{~k ~x} {:boop 10}) true) 8 | (t/assert= (-eq `{:x {:y ~x}} {:x {:y 10}}) true))) 9 | 10 | (def constantly-nil (constantly nil)) 11 | 12 | (t/deftest some->test 13 | (t/assert (nil? (some-> nil))) 14 | (t/assert (= 0 (some-> 0))) 15 | (t/assert (= -1 (some-> 1 (- 2)))) 16 | (t/assert (nil? (some-> 1 constantly-nil (- 2))))) 17 | 18 | (t/deftest some->>test 19 | (t/assert (nil? (some->> nil))) 20 | (t/assert (= 0 (some->> 0))) 21 | (t/assert (= 1 (some->> 1 (- 2)))) 22 | (t/assert (nil? (some->> 1 constantly-nil (- 2))))) 23 | 24 | (t/deftest cond->test 25 | (t/assert (= 0 (cond-> 0))) 26 | (t/assert (= -1 (cond-> 0 true inc true (- 2)))) 27 | (t/assert (= 0 (cond-> 0 false inc))) 28 | (t/assert (= -1 (cond-> 1 true (- 2) false inc)))) 29 | 30 | (t/deftest cond->>test 31 | (t/assert (= 0 (cond->> 0))) 32 | (t/assert (= 1 (cond->> 0 true inc true (- 2)))) 33 | (t/assert (= 0 (cond->> 0 false inc))) 34 | (t/assert (= 1 (cond->> 1 true (- 2) false inc)))) 35 | 36 | (t/deftest as->test 37 | (t/assert (= 0 (as-> 0 x))) 38 | (t/assert (= 1 (as-> 0 x (inc x)))) 39 | (t/assert (= 2 (as-> [0 1] x 40 | (map inc x) 41 | (reverse x) 42 | (first x))))) 43 | 44 | (t/deftest threading-loop-recur 45 | (t/assert (nil? (loop [] 46 | (as-> 0 x 47 | (when-not (zero? x) 48 | (recur)))))) 49 | (t/assert (nil? (loop [x nil] (some-> x recur)))) 50 | (t/assert (nil? (loop [x nil] (some->> x recur)))) 51 | (t/assert (= 0 (loop [x 0] (cond-> x false recur)))) 52 | (t/assert (= 0 (loop [x 0] (cond->> x false recur))))) 53 | -------------------------------------------------------------------------------- /tests/pixie/tests/test-numbers.pxi: -------------------------------------------------------------------------------- 1 | (ns pixie.tests.test-numbers 2 | (require pixie.test :as t)) 3 | 4 | (t/deftest integer-literals 5 | (t/assert= 0xa 10) 6 | (t/assert= -0xa -10) 7 | (t/assert= 012 10) 8 | (t/assert= -012 -10) 9 | (t/assert= 2r1010 10) 10 | (t/assert= -2r1010 -10)) 11 | 12 | (t/deftest float-literals 13 | (t/assert= 10. 10.0) 14 | (t/assert= -10. -10.0) 15 | (t/assert= 1e1 10.0) 16 | (t/assert= -1e1 -10.0) 17 | (t/assert= 1e-1 0.1) 18 | (t/assert= -1e-1 -0.1)) 19 | 20 | (t/deftest mixed-float-ops 21 | (t/assert= (+ 1/2 0.5) 1.0) 22 | (t/assert= (+ 0 1.0) 1.0)) 23 | 24 | (t/deftest ratio-literals 25 | (t/assert= 3/4 (/ 3 4)) 26 | (t/assert= -3/4 (/ -3 4)) 27 | (t/assert= 6/8 3/4) 28 | (t/assert= 9/12 3/4) 29 | (t/assert= 3/1 3)) 30 | 31 | (t/deftest ratio-from-divide 32 | (t/assert= (/ 3 4) 3/4)) 33 | 34 | (t/deftest ratio-ops 35 | (t/assert= (+ 1/2 1/2) 1) 36 | (t/assert= (- 1/2 1/2) 0) 37 | (t/assert= (* 1/2 1/2) 1/4) 38 | (t/assert= (/ 1/2 1/2) 1)) 39 | 40 | (t/deftest ratio-accessors 41 | (doseq [[r n d] [[3/2 3 2] [1/9 1 9] [-3/89 -3 89]]] 42 | (t/assert= (numerator r) n) 43 | (t/assert= (denominator r) d))) 44 | 45 | (t/deftest test-int 46 | (doseq [[x i] [[1 1] [3.0 3] [3.5 3] [3.999 3] [3/2 1]]] 47 | (t/assert= (int x) i))) 48 | 49 | (t/deftest test-float 50 | (doseq [[x f] [[1 1.0] [3 3.0] [3.333 3.333] [3/2 1.5] [1/7 (/ 1.0 7.0)]]] 51 | (t/assert= (float x) f))) 52 | 53 | (t/deftest rem-types 54 | (t/assert= Integer (type (rem 5 3))) 55 | (t/assert= Float (type (rem 5.0 3))) 56 | (t/assert= Ratio (type (rem 7/2 3))) 57 | (t/assert= Float (type (rem 7/2 3.0)))) 58 | 59 | (t/deftest quot-types 60 | (t/assert= Integer (type (quot 5 3))) 61 | (t/assert= Float (type (quot 5.0 3))) 62 | (t/assert= Integer (type (quot 7/2 3/7))) 63 | (t/assert= Float (type (quot 7/2 3.0)))) 64 | 65 | (t/deftest test-big-int-eq 66 | (t/assert (-num-eq 1N 1)) 67 | (t/assert (-num-eq 1 1N)) 68 | ;(t/assert= 1N 1) this fails, should it? 69 | (t/assert= 1 1N)) 70 | 71 | (t/deftest test-promotion 72 | (t/assert= BigInteger (type (reduce * 1 (range 1 100)))) 73 | (t/assert (-num-eq 1000000000000000000000N (* 10000000 74 | 10000000 75 | 10000000)))) 76 | 77 | (t/deftest test-wrap-around 78 | (t/assert= Integer (type (unchecked-add 9223372036854775807 1))) 79 | (t/assert (-num-eq -9223372036854775808 (unchecked-add 9223372036854775807 1)))) 80 | -------------------------------------------------------------------------------- /tests/pixie/tests/test-object.pxi: -------------------------------------------------------------------------------- 1 | (ns pixie.tests.test-object 2 | (require pixie.test :as t)) 3 | 4 | (t/deftest test-hash 5 | (t/assert= (hash (var foo)) (hash (var foo))) 6 | (t/assert (not= (hash (var foo)) (hash (var bar))))) 7 | 8 | 9 | (deftype FooType []) 10 | 11 | (t/deftest test-everything-is-an-object 12 | (t/assert (instance? Object 42)) 13 | (t/assert (instance? Object [])) 14 | (t/assert (instance? Object "Foo")) 15 | (t/assert (instance? Object FooType))) 16 | -------------------------------------------------------------------------------- /tests/pixie/tests/test-parser.pxi: -------------------------------------------------------------------------------- 1 | (ns pixie.tests.test-parser 2 | (:require [pixie.test :refer :all] 3 | [pixie.parser :refer :all])) 4 | 5 | (deftest test-and 6 | (let [p (parser [] 7 | ENTRY (and \a \b <- [\a \b])) 8 | c (string-cursor "abc")] 9 | (assert= ((:ENTRY p) c) [\a \b]) 10 | (assert= (current c) \c) 11 | (assert= 2 (snapshot c)))) 12 | 13 | (deftest test-or 14 | (let [p (parser [] 15 | ENTRY (or \b \a)) 16 | c (string-cursor "abc")] 17 | (assert= ((:ENTRY p) c) \a) 18 | (assert= (current c) \b) 19 | (assert= 1 (snapshot c)))) 20 | 21 | 22 | (defparser as-and-bs [] 23 | ENTRY (and S -> value 24 | end 25 | <- value) 26 | S (one+ AB) <- `[:S ~@value] 27 | AB (and A -> a 28 | B -> b 29 | <- [:AB a b]) 30 | A (one+ \a) <- `[:A ~@value] 31 | B (one+ \b) <- `[:B ~@value]) 32 | 33 | (deftest test-as-and-bs 34 | (let [c (string-cursor "aabbaa")] 35 | (assert= ((:S as-and-bs) c) [:S [:AB 36 | [:A \a \a] 37 | [:B \b \b]]]) 38 | (assert (not (at-end? c))) 39 | (assert= (snapshot c) 4) 40 | 41 | (assert (failure? ((:S as-and-bs) c)))) 42 | 43 | (let [c (string-cursor "aabbaa")] 44 | (assert (failure? ((:ENTRY as-and-bs) c)) ) 45 | (assert (not (at-end? c))) 46 | (assert= (snapshot c) 0))) 47 | -------------------------------------------------------------------------------- /tests/pixie/tests/test-readeval.pxi: -------------------------------------------------------------------------------- 1 | (ns pixie.tests.test-readeval 2 | (require pixie.test :as t)) 3 | 4 | (t/deftest test-read 5 | (t/assert= (read-string "0xDEADBEEF") 3735928559) 6 | (t/assert= (read-string "0xDeadBeef") 3735928559) 7 | (t/assert= (read-string "0xdeadbeef") 3735928559) 8 | (t/assert= (read-string "foo") 'foo) 9 | (t/assert= (read-string "()") '()) 10 | (t/assert= (read-string "(1 2 3)") '(1 2 3)) 11 | (t/assert= (read-string "[1 2 3]") [1 2 3]) 12 | (t/assert= (read-string "{:a 1 :b 2 :c 3}") {:a 1 :b 2 :c 3}) 13 | (t/assert= (read-string "\"foo\"") "foo") 14 | (t/assert= (read-string "\"fo\\\\o\"") "fo\\o") 15 | (t/assert= (read-string "false") false) 16 | (t/assert= (read-string "true") true) 17 | (t/assert= (read-string "#{1 2 3}") #{1 2 3}) 18 | (t/assert= (read-string "(foo (bar (baz)))") '(foo (bar (baz))))) 19 | 20 | (t/deftest test-list-unclosed-list-fail 21 | (t/assert-throws? RuntimeException 22 | "Unmatched list open '('" 23 | (read-string "(")) 24 | (t/assert-throws? RuntimeException 25 | "Unmatched list open '('" 26 | (read-string "((foo bar)"))) 27 | 28 | (t/deftest test-vector-unclosed-list-fail 29 | (t/assert-throws? RuntimeException 30 | "Unmatched vector open '['" 31 | (read-string "[")) 32 | (t/assert-throws? RuntimeException 33 | "Unmatched vector open '['" 34 | (read-string "[[foo bar]"))) 35 | 36 | (t/deftest test-map-unclosed-list-fail 37 | (t/assert-throws? RuntimeException 38 | "Unmatched map open '{'" 39 | (read-string "{")) 40 | (t/assert-throws? RuntimeException 41 | "Unmatched map open '{'" 42 | (read-string "{foo {a b}"))) 43 | 44 | (t/deftest test-set-unclosed-list-fail 45 | (t/assert-throws? RuntimeException 46 | "Unmatched set open '#{'" 47 | (read-string "#{")) 48 | (t/assert-throws? RuntimeException 49 | "Unmatched set open '#{'" 50 | (read-string "#{foo #{a}"))) 51 | 52 | (t/deftest test-string-unclosed-fail 53 | (t/assert-throws? RuntimeException 54 | "Unmatched string quote '\"'" 55 | (read-string "\"")) 56 | (t/assert-throws? RuntimeException 57 | "Unmatched string quote '\"'" 58 | (read-string "\"foo"))) 59 | 60 | (t/deftest test-comments-in-forms 61 | (t/assert= (read-string "(foo ; a comment\n )") '(foo)) 62 | (t/assert= (read-string "[foo ; a comment\n ]") '[foo]) 63 | (t/assert= (read-string "{:foo :bar ; a comment\n }") '{:foo :bar}) 64 | (t/assert= (read-string "#{:foo ; a comment\n }") '#{:foo}) 65 | (t/assert= (read-string "{:foo ; a comment\n :bar }") '{:foo :bar})) 66 | 67 | (t/deftest test-comment-reader-macro 68 | (t/assert= (read-string "(foo #_bar baz)") '(foo baz)) 69 | (t/assert= (read-string "(foo #_ bar baz)") '(foo baz)) 70 | (t/assert= (read-string "(foo #_ #_ bar baz)") '(foo)) 71 | (t/assert= (read-string "(foo #_(bar goo) baz)") '(foo baz)) 72 | (t/assert= (read-string "(foo bar #_baz)") '(foo bar)) 73 | (t/assert= (read-string "(foo bar #_ ; comment \n baz)") '(foo bar))) 74 | -------------------------------------------------------------------------------- /tests/pixie/tests/test-sets.pxi: -------------------------------------------------------------------------------- 1 | (ns pixie.test.test-sets 2 | (require pixie.test :as t) 3 | (require pixie.set :as s)) 4 | 5 | (let [magic #{:bibbidi :bobbidi :boo} 6 | work #{:got :no :time :to :dilly-dally}] 7 | 8 | (t/deftest test-union 9 | (let [dreams #{:the :dreams :that :i :wish} 10 | magical-work #{:bibbidi :bobbidi :boo 11 | :got :no :time :to :dilly-dally} 12 | dream-of-magical-work #{:bibbidi :bobbidi :boo 13 | :got :no :time :to :dilly-dally 14 | :the :dreams :that :i :wish}] 15 | (t/assert= (s/union) #{}) 16 | (t/assert= (s/union magic) magic) 17 | (t/assert= (s/union magic magic) magic) 18 | (t/assert= (s/union magic magic magic) magic) 19 | (t/assert= (s/union magic work) magical-work) 20 | (t/assert= (s/union work magic) magical-work) 21 | (t/assert= (s/union magic work dreams) dream-of-magical-work) 22 | (t/assert-throws? (s/union [:i :only] [:love :sets])))) 23 | 24 | (t/deftest test-difference 25 | (t/assert= (s/difference) #{}) 26 | (t/assert= (s/difference magic) magic) 27 | (t/assert= (s/difference magic #{:boo}) #{:bibbidi :bobbidi}) 28 | (t/assert= (s/difference magic work) magic) 29 | (t/assert= (s/difference magic #{:bibbidi} #{:bobbidi}) #{:boo}) 30 | (t/assert-throws? (s/difference [:i :only] [:love :sets]))) 31 | 32 | (t/deftest test-intersection 33 | (t/assert= (s/intersection) #{}) 34 | (t/assert= (s/intersection magic) magic) 35 | (t/assert= (s/intersection magic magic) magic) 36 | (t/assert= (s/intersection magic work) #{}) 37 | (t/assert= (s/intersection magic #{:boo}) #{:boo}) 38 | (t/assert= (s/intersection #{:boo} magic) #{:boo}) 39 | (t/assert= (s/intersection magic #{:bobbidi :boo}) #{:bobbidi :boo}) 40 | (t/assert= (s/intersection magic #{:bibbidi :boo} #{:bobbidi :boo}) #{:boo}) 41 | (t/assert-throws? (s/intersection [:i :only] [:love :sets]))) 42 | 43 | (t/deftest test-subset? 44 | (t/assert (not (s/subset? magic work))) 45 | (t/assert (s/subset? magic magic)) 46 | (t/assert (not (s/subset? magic #{:foo}))) 47 | (t/assert (s/subset? #{:boo} magic)) 48 | (t/assert-throws? (s/subset? [:i :only] [:love :sets]))) 49 | 50 | (t/deftest test-strict-subset? 51 | (t/assert (not (s/strict-subset? magic work))) 52 | (t/assert (not (s/strict-subset? magic magic))) 53 | (t/assert (not (s/strict-subset? magic #{:foo}))) 54 | (t/assert (s/strict-subset? #{:boo} magic)) 55 | (t/assert-throws? (s/strict-subset? [:i :only] [:love :sets]))) 56 | 57 | (t/deftest test-superset? 58 | (t/assert (not (s/superset? magic work))) 59 | (t/assert (s/superset? magic magic)) 60 | (t/assert (not (s/superset? #{:foo} magic))) 61 | (t/assert (s/superset? magic #{:boo})) 62 | (t/assert-throws? (s/superset? [:i :only] [:love :sets]))) 63 | 64 | (t/deftest test-strict-superset? 65 | (t/assert (not (s/strict-superset? magic work))) 66 | (t/assert (not (s/strict-superset? magic magic))) 67 | (t/assert (not (s/strict-superset? #{:foo} magic))) 68 | (t/assert (s/strict-superset? magic #{:boo})) 69 | (t/assert-throws? (s/strict-superset? [:i :only] [:love :sets])))) 70 | -------------------------------------------------------------------------------- /tests/pixie/tests/test-utf8.pxi: -------------------------------------------------------------------------------- 1 | (ns pixie.test.test-utf8 2 | (require pixie.test :as t)) 3 | 4 | (t/deftest test-utf8-string-val 5 | (t/assert= "🍺=👍" "🍺=👍")) 6 | 7 | (t/deftest test-utf8-var-name 8 | (let [🍺 "🍺=👍"] 9 | (t/assert= 🍺 "🍺=👍"))) 10 | -------------------------------------------------------------------------------- /tests/pixie/tests/test-walk.pxi: -------------------------------------------------------------------------------- 1 | (ns pixie.tests.test-walk 2 | (:require [pixie.walk :as w] 3 | [pixie.test :as t])) 4 | 5 | (t/deftest t-prewalk-replace 6 | (t/assert (= (w/prewalk-replace {:a :b} [:a {:a :a} (list 3 :c :a)]) 7 | [:b {:b :b} (list 3 :c :b)]))) 8 | 9 | (t/deftest t-postwalk-replace 10 | (t/assert (= (w/postwalk-replace {:a :b} [:a {:a :a} (list 3 :c :a)]) 11 | [:b {:b :b} (list 3 :c :b)]))) 12 | 13 | (t/deftest t-prewalk-order 14 | (t/assert (= (let [a (atom [])] 15 | (w/prewalk (fn [form] (swap! a conj form) form) 16 | [1 2 {:a 3} (list 4 [5])]) 17 | @a) 18 | [[1 2 {:a 3} (list 4 [5])] 19 | 1 2 {:a 3} [:a 3] :a 3 (list 4 [5]) 20 | 4 [5] 5]))) 21 | 22 | (t/deftest t-postwalk-order 23 | (t/assert (= (let [a (atom [])] 24 | (w/postwalk (fn [form] (swap! a conj form) form) 25 | [1 2 {:a 3} (list 4 [5])]) 26 | @a) 27 | [1 2 28 | :a 3 [:a 3] {:a 3} 29 | 4 5 [5] (list 4 [5]) 30 | [1 2 {:a 3} (list 4 [5])]]))) 31 | 32 | (defrecord Foo [a b c]) 33 | 34 | (t/deftest walk 35 | "Checks that walk returns the correct result and type of collection" 36 | (let [colls ['(1 2 3) 37 | [1 2 3] 38 | #{1 2 3} 39 | {:a 1, :b 2, :c 3} 40 | (->Foo 1 2 3)]] 41 | (doseq [c colls] 42 | (let [walked (w/walk identity c)] 43 | (t/assert (= c walked)) 44 | (t/assert (= (type c) (type walked))) 45 | (if (or (map? c) 46 | (record? c)) 47 | (do 48 | (t/assert (= (reduce + (vals (w/walk 49 | #(map-entry 50 | (key %) 51 | (inc (val %))) 52 | c))) 53 | (reduce + (map (comp inc val) c))))) 54 | (t/assert (= (reduce + (w/walk inc c)) 55 | (reduce + (map inc c))))))))) 56 | 57 | (t/deftest t-stringify-keys 58 | (t/assert (= (w/stringify-keys {:a 1, nil {:b 2 :c 3}, :d 4}) 59 | {"a" 1, nil {"b" 2 "c" 3}, "d" 4}))) 60 | -------------------------------------------------------------------------------- /tests/pixie/tests/utils.pxi: -------------------------------------------------------------------------------- 1 | (ns pixie.tests.utils) 2 | 3 | ;; Here we create a new type which hashes poorly, in fact it's so bad we have a 4 | ;; hash space of only 1. 5 | ;; All members of WorstHasher return (hash "worst hasher") 6 | ;; when hash is called on them. 7 | ;; 8 | ;; This makes debugging, testing and benchmarking anything based off 9 | ;; PersistentHashMap trivial. 10 | 11 | ;; X can be any thing you like. 12 | (defrecord WorstHasher [x]) 13 | (extend -hash WorstHasher (fn [self] (hash "worst hasher"))) 14 | --------------------------------------------------------------------------------