nakkaya / ferret Goto Github PK
View Code? Open in Web Editor NEWFerret is a free software lisp implementation for real time embedded control systems.
Home Page: https://ferret-lang.org
License: BSD 2-Clause "Simplified" License
Ferret is a free software lisp implementation for real time embedded control systems.
Home Page: https://ferret-lang.org
License: BSD 2-Clause "Simplified" License
Might be a good idea to document prominently that one must use e.g. (vec ...)
instead of [...]
. This is a stumbling block for new users.
You can use the hash-map syntactic sugar {:a 12}
successfully due to #7 fix.
This is a stupid n00b question. But I'm coming back to look at Ferret again to see if I can use it for a particular project.
I presume you can allocate, read and write standard mutable C arrays from inside it. But looking through the documentation again I don't actually see an example of that.
Are there examples anywhere? Ie. of just creating a 10 element array of ints and reading / writing to it? From Ferret.
A for
of the form:
(for [p start-low]
...)
Throws the following error:
WARN unsopported form => [p start_low]
I am using doseq
instead.
I create inctest.clj with the following content
(println ((fn [f] (f 0)) inc))
When compiling, an error occurs:
$ g++ -std=c++11 -pthread inctest.cpp
inctest.cpp:2812:40: error: 'inc' does not refer to a value
run(println(),run(FN__4519(),inc));
^
inctest.cpp:2685:24: note: declared here
class inc final : public lambda_i{
Note: replacing inc
with (identity inc)
works and prints the number 1
as expected.
Firstly, thank you so much @nakkaya, I'm so keen to get started with ferret! But I've fallen at the first hurdle: I can't get a simple hello world to work.
Why does:
(println "hello world")
work, but:
(println (str "hello" "world"))
not work? Here's the output:
$ ferret -ci cli.clj
23:18:16 info dir => ./
23:18:16 info file => cli.clj
23:18:18 info compiled => ./cli.cpp
23:18:18 info building => g++ -std=c++11 -x c++ cli.cpp
23:18:18 warning build error
23:18:18 warning cli.cpp: In function ‘void cli::main()’:
cli.cpp:2203:29: error: ‘str’ was not declared in this scope
run(println(),run(str,obj<string>("hello",(number_t)5),obj<string>("world",(number_t)5)));
I can see the error clearly, ‘str’ was not declared in this scope
, but how can that be? Thank you!
Using the rem
function results in this error:
error: 'remainder' was not declared in this scope
__result = obj<number>(remainder(number::to<real_t>(num), number::to<real_t>(div)));
This also means that the mod
function does not work.
I wanted to start playing with Ferret on my macOS 10.12 dev machine. I'm new to emacs, so I started with spacemacs. I was able to set it up for interactive use, but the build script is failing for me.
Specifically, tangled src/src/template.clj
has missing <code-templates>
section and others.
Most likely it is the issue of :noweb-ref
property inheritance failure in spacemacs as reported here:
https://www.mail-archive.com/[email protected]/msg112308.html
This failure is silent. Instead of a warning or error it simply emits empty string. Which confused me at first.
I tried to experiment with
(setq org-use-property-inheritance t)
(setq org-babel-use-quick-and-dirty-noweb-expansion nil)
but without success. I haven't found any solution to this problem.
Any ideas how to fix this?
One solution for me would be if you could stop relying on :noweb-ref
property inheritance and explicitly name all code snippets and include them in proper places. Thanks!
The following code generates a C++ compiler error:
; other.clj
(defn foo [] "hi")
; main.clj
(require 'other)
(print (other/foo))
Compiling the generated C++ code results in this error:
src/main.ino: In function 'void ferret::program::run()':
src/main.ino:1819:28: error: 'other_foo' was not declared in this scope
run(print(),run(other_foo));
^
Hi!
I have discovered this project while looking for a way to build a parser targeting C, exposing therefore a C libray.
The plan I have read on HN about the REPL is to emit C++ directly to https://root.cern.ch/cling. Sounds nice. For me the REPL is the biggest (or maybe second, after persistent data structures) innovation Lisp introduced and I would love to have one in ferret
.
I cannot commit any time but I will look into it, any hint/update on this can go in this issue.
Thanks for working on this, it is super awesome (and the literate file is super!).
Hi!
I'm new to ferret, but I'm wondering whether
an LLVM frontend would make sense for ferret.
Can add Teensy 2.0 to this list:
https://ferret-lang.org/#outline-container-sec-1
Here is the LED flash example ported to Teensy 2.0:
Implement -O flag for something other than a.out.
I noticed some strange behavior for println
. With a memory pool configured, printing strings above a certain length causes the program to stop functioning properly, I'm guessing because of some kind of memory overflow. It would be reasonable to expect bad behavior if the code/string in question were excessively long, but it does not require a very long string to cause this issue. For example, the program below exhibits this behavior when compiled and run on an Arduino Uno:
(configure-runtime!
FERRET_MEMORY_POOL_SIZE 512)
(println "123456789012345678901234")
If the string provided to println
is shortened by a few characters, or if the memory pool is not configured, the program will function normally.
(println (flatten nil))
compiles in ferret and c++ for me, but seg faults at runtime. The docs say it should return an empty list.
❯ /bin/bash -c 'ferret --disable-formatting -c -o '\''bazel-out/darwin-fastbuild/bin/bazel/rules_ferret/tests/lazy_sum.cpp'\'' -b '\''bazel-out/darwin-fastbuild/bin/bazel/rules_ferret/tests/lazy_sum'\'' -i '\''bazel/rules_ferret/tests/lazy_sum.clj'\'''
20:31:29 INFO dir => bazel/rules_ferret/tests/
20:31:29 INFO file => bazel/rules_ferret/tests/lazy_sum.clj
20:31:31 INFO compiled => bazel-out/darwin-fastbuild/bin/bazel/rules_ferret/tests/lazy_sum.cpp
20:31:31 INFO building => g++ -std=c++11 -x c++ lazy_sum.cpp -o bazel-out/darwin-fastbuild/bin/bazel/rules_ferret/tests/lazy_sum
20:31:31 WARN build error
20:31:31 WARN ld: can't open output file for writing: bazel-out/darwin-fastbuild/bin/bazel/rules_ferret/tests/lazy_sum, errno=2 for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
g++ -std=c++11 -x c++ lazy_sum.cpp -o bazel-out/darwin-fastbuild/bin/bazel/rules_ferret/tests/lazy_sum
should be
g++ -std=c++11 -x c++ bazel-out/darwin-fastbuild/bin/bazel/rules_ferret/tests/lazy_sum.cpp -o bazel-out/darwin-fastbuild/bin/bazel/rules_ferret/tests/lazy_sum
If calls to native-declare
are made in a required package, they are generated in reverse order in the ferret compiler output. For instance:
; main.clj
(require 'xyz)
; xyz.clj
(native-declare "const int XYZ_SIZE = 123;")
(native-declare "int xyz_arr[XYZ_SIZE];")
Ferret will generate this from the above code:
int xyz_arr[XYZ_SIZE];
const int XYZ_SIZE = 123;
This will result in a C++ compiler error because XYZ_SIZE is not yet declared when it is used.
A workaround for this is to include both native declarations in a single use of native-declare
, i.e.:
(native-declare
"const int XYZ_SIZE = 123;
int xyz_arr[XYZ_SIZE];")
@nakkaya just leaving this here as it is something I noticed. Thanks again for this wonderful software!
The following file content, when "test.sh" is executed, results in an abort from the unit test executable generated and compiled by Ferret. Any thoughts?
My computer is running Linux Mint 19.2. with the following 'uname -a': Linux 4.15.0-64-generic #73-Ubuntu SMP Thu Sep 12 13:16:13 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux
The g++ version is: g++ (Ubuntu 7.4.0-1ubuntu1~18.04.1) 7.4.0
The Ferret version is: ferret-lisp 0.4.0-bb18aba
File "gravity-compensation.clj":
(defn calculate-free-air-correction-local [elevation-meters]
(* -3.086E-4 elevation-meters))
File "unit-test.clj":
(require '[gravity-compensation :as gc])
(deftest
calculate-free-air-correction-local-test
((is (= -3.086E-3 (gc/calculate-free-air-correction-local 100)))))
File "test.sh":
#!/bin/bash
echo
echo "Compiling unit-test"
ferret --compile --release --disable-formatting --silent
--input unit-test.clj
--output unit-test
echo "Executing unit-test"
echo
./unit-test
echo
echo "Done"
echo
The expression (assoc {:a 1, :b 2, :c 3} :a 9))
should produce the result: {:a 9, :b 2, :c 3}
.
However in ferret this produces the result: {:a 9, :b 1, :c 2}
.
The :a
key is updated appropriately with the new value 9
, but the :b
and :c
keys are getting the shifted original values of :a
and :b
.
Building with more recent versions of org-mode
generates incomplete source files: Only the first source block with a given name is tangled into the output file.
This is, because ferret uses the same name for multiple source blocks, which is documented to have undefined behavior: https://orgmode.org/manual/Structure-of-code-blocks.html
Tested with org-20190904
:
± make
[build] bin/ferret
Syntax error compiling at (compiler/core.clj:44:11).
Exception in thread "main" Syntax error compiling at (compiler/core.clj:44:11).
...
Caused by: java.lang.RuntimeException: No such var: parser/transform
... 72 more
Compilation failed: Subprocess failed
make: *** [Makefile:58: bin/ferret] Error 1
± cat src/compiler/compiler/parser.clj
(ns user)
(ns compiler.parser
(:refer-clojure :exclude [drop peek])
(:gen-class)
(:require [compiler.io :as io]
[fast-zip.core :as zip]
[clojure.walk :as walk]))
(defn form?
([s]
#(form? s %))
([s f]
(and (seq? f)
(= (first f) s))))
Thanks for Ferret it’s awesome.
The current FFI is non ideal when large C libraries are required. I want to use OpenGL with Ferret. The only solution now AFAIK is to write tedious glue code for every single OpenGL method (and there are loads of little ones).
Compare with Clojure where Java libraries can be consumed trivially e.g. (.glDoSomething 567).
Since Ferret is a new language, why can’t it be designed to lean into the host? Like Clojure leans into the JVM. Why can’t we just use C routines natively, calling them as if they were Lisp functions? If they return pointers or whatever, then why can’t we use the native Ferret C++ pointer type to store them? (EDIT - I’m aware that Ferret has a pointer type. What I would hope for is that it could be created and used easily without inline strings)
The readme says "Source Code - https://github.com/nakkaya/ferret" but src
is in .gitignore?
Hello -- I am trying to get ferret to work on MacOS, but having some problems. First the build script works correctly, tangling out the source and running lein uberjar.
./build
Setting ‘package-selected-packages’ temporarily since "emacs -q" would overwrite customizations
Setting ‘package-selected-packages’ temporarily since "emacs -q" would overwrite customizations
Package ‘htmlize-’ is unavailable
Compiling ferret.core
Compiling ferret.core
Created /Users/bbeckman/Documents/ferret/src/target/interim.jar
Created /Users/bbeckman/Documents/ferret/src/target/ferret.jar
However, after that, ferret does not execute. It's looking for a version of JDK that I do not have
$ ./ferret
./ferret: line 8: /Library/Java/JavaVirtualMachines/jdk1.8.0_121.jdk/Contents/Home/bin/java: No such file or directory
./ferret: line 8: exec: /Library/Java/JavaVirtualMachines/jdk1.8.0_121.jdk/Contents/Home/bin/java: cannot execute: No such file or directory
I have version 131, not 121.
$ java -version
java version "1.8.0_131"
Java(TM) SE Runtime Environment (build 1.8.0_131-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.131-b11, mixed mode)
I have not been able to find anything in the .org file that refers to jdk 121 (e.g., find . -type f -exec grep 121 "{}" ';'
doesn't find anything relevant to the build). My version of leiningen is up-to-date.
$ lein upgrade
The script at /usr/local/bin/lein will be upgraded to the latest stable version.
Do you want to continue [Y/n]? y
Upgrading...
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 137 100 137 0 0 398 0 --:--:-- --:--:-- --:--:-- 449
100 12871 100 12871 0 0 11375 0 0:00:01 0:00:01 --:--:-- 11375
Leiningen is already up-to-date.
Leiningen 2.7.1 on Java 1.8.0_131 Java HotSpot(TM) 64-Bit Server VM
I can't figure this one out. Blocked until someone help.
I have a sort of odd use case for ferret where I'm using it to interop with some C++ code in order to generate FFI bindings. A part of this requires generating source code in a different language and having the ability to apply templates would be helpful. Having format
or even str
would be useful for this case.
I went ahead and hacked up a replacement for str
that relies on the std::string
class, but would be nice to have str
and format
in core. Happy to send a PR with my str
, though not sure it handles all cases and, as I said, it requires std::string
.
Thanks for ferret, it's kind of weird but I like it as a hacky way to get at C++. :)
I'm a little bit confused as to how ferret.jar
is generated. Is the actual source code in ferret.org
?
I'm really interested in the possibility of writing libraries in Clojure / Ferret that can be called from a C++ framework.
Something like gen-class in Clojure.
Is this possible? Is it part of the goal for Ferret?
I realized that :as
is not supported when destructuring sequences:
(let [[a b c :as l] (list 1 2 3)]
(print a b c l))
Not a huge deal as you can rewrite the above as:
(let [l (list 1 2 3)
[a b c] l]
(print a b c l))
... but :as
is a useful shorthand.
If a map is defined in a required package, ferret will fail with an error. For example, attempting to compile the following with ferret:
; main.clj
(require 'other)
; other.clj
(def m {:a 1})
Will produce this error:
Exception in thread "main" java.lang.IllegalArgumentException: No method in multimethod 'emit' for dispatch value: null
at clojure.lang.MultiFn.getFn(MultiFn.java:156)
at clojure.lang.MultiFn.invoke(MultiFn.java:238)
at ferret.core$emit_ast$fn__1045.invoke(core.clj:480)
at clojure.lang.PersistentList.reduce(PersistentList.java:117)
at clojure.core$reduce.invoke(core.clj:6518)
at ferret.core$emit_ast.invoke(core.clj:479)
at ferret.core$fn__1066.invoke(core.clj:514)
at clojure.lang.MultiFn.invoke(MultiFn.java:238)
at ferret.core$emit_ast$fn__1045.invoke(core.clj:480)
at clojure.core.protocols$fn__6523.invoke(protocols.clj:167)
at clojure.core.protocols$fn__6478$G__6473__6487.invoke(protocols.clj:19)
at clojure.core.protocols$seq_reduce.invoke(protocols.clj:31)
at clojure.core.protocols$fn__6506.invoke(protocols.clj:101)
at clojure.core.protocols$fn__6452$G__6447__6465.invoke(protocols.clj:13)
at clojure.core$reduce.invoke(core.clj:6519)
at ferret.core$emit_ast.invoke(core.clj:479)
at ferret.core$emit_source.invoke(core.clj:490)
at ferret.core$compile__GT_cpp.invoke(core.clj:670)
at ferret.core$build_solution.invoke(core.clj:724)
at ferret.core$_main.doInvoke(core.clj:761)
at clojure.lang.RestFn.applyTo(RestFn.java:137)
at ferret.core.main(Unknown Source)
The following example prints alternating false
/true
. But if you uncomment the first line to configure the memory pool, then this will constantly print true
- even though the value in the atom is false
!
;(configure-runtime! FERRET_MEMORY_POOL_SIZE 512)
(def a (atom false))
(forever
(println @a)
(swap! a not)
(sleep 500))
Actually, with the memory pool configured, the above example will always print true
regardless of whether the value in the atom is true
or false
.
If the value in the atom is a number, however, the atom behaves appropriately, regardless of whether the memory pool is configured or not. The following example works fine:
(configure-runtime! FERRET_MEMORY_POOL_SIZE 512)
(def a (atom 0))
(forever
(println @a)
(swap! a inc)
(sleep 500))
It looks like reduce
, when provided only two arguments, will invoke f
with the first member of sr
as acc
and the first value of the sequence sf
as the next x
argument in the sequence. This in effect records the first two items in the sequence.
In other words, the expected behavior of reduce
called as:
(reduce f xs)
should be the same as:
(reduce f (first xs) (rest xs))
But it currently is:
(reduce f (second xs) (cons (first xs) (rest (rest xs))))
In situations where the same function is referenced in multiple files, ferret code generation can result in the function definitions being included multiple times. In the following example the bar
function is called directly in main
, and also indirectly via the foo
function:
main.clj:
(require 'foo)
(require 'bar)
(foo/foo)
(bar/bar 0)
foo.clj:
(require 'bar)
(defn foo []
(bar/bar 1))
bar.clj
(defn bar [x]
x)
If you compile main.clj with ferret, it will emit a .cpp file with multiple definitions of the bar
function. Trying to compile that .cpp file will result in these errors:
src/main.cpp:2537:7: error: redefinition of 'class main::bar_bar'
class bar_bar {
^~~~~~~
src/main.cpp:2527:7: note: previous definition of 'class main::bar_bar'
class bar_bar {
^~~~~~~
src/main.cpp:2564:12: error: redefinition of 'ferret::var main::bar_bar::invoke(ferret::ref) const'
inline var bar_bar::invoke(ref _args_) const {
^~~~~~~
src/main.cpp:2551:12: note: 'ferret::var main::bar_bar::invoke(ferret::ref) const' previously defined here
inline var bar_bar::invoke(ref _args_) const {
^~~~~~~
Using this:
(def start-low (list 3 6 12 23 16 17))
Both of these forms fail with the same error:
(map (fn [p] (gpio/pin-mode p :output)) start-low)
(doseq [p start-low]
(do
(gpio/pin-mode p :output)
(gpio/digital-write p :low)))
Here is the error:
error: 'p__13889' was not declared in this scope
::pinMode(number::to<number_t>(p__13889), OUTPUT);;
^
Note that the gpio/digital-write
inside the loop does not throw an error.
The Arduino version of this fn accepts INPUT_PULLUP.
In the ferret fn it could be possible to add a check for :input_pullup
but maybe the fix should use a macro like this:
(defmacro mode-enum [mode]
(clojure.string/upper-case (name mode)))
which would convert a key like :input
to INPUT
.
$ ferret -c -i lazy-sum.clj
10:08:34 info dir => ./
10:08:34 info file => lazy-sum.clj
10:08:41 info compiled => ./lazy-sum.cpp
10:08:41 info building => g++ -std=c++11 -x c++ lazy-sum.cpp
10:08:42 warning build error
10:08:42 warning lazy-sum.cpp: In instantiation of ‘ferret::object_i<rc>::object_i() [with rc = ferret::memory::gc::rc<std::atomic<unsigned int> >]’:
lazy-sum.cpp:915:52: required from here
lazy-sum.cpp:667:22: error: use of deleted function ‘std::atomic<unsigned int>::atomic(const std::atomic<unsigned int>&)’
class rc{
^
In file included from lazy-sum.cpp:63:0:
/usr/include/c++/4.8/atomic:620:7: error: declared here
atomic(const atomic&) = delete;
^
lazy-sum.cpp:694:25: note: synthesized method ‘constexpr ferret::memory::gc::rc<std::atomic<unsigned int> >::rc()’ first required here
object_i() { }
^
$ ferret --help
ferret-lisp build: 0d31551
$ uname -srv
Linux 3.19.0-80-generic #88~14.04.1-Ubuntu SMP Fri Jan 13 14:54:07 UTC 2017
$ g++ --version
g++ (Ubuntu 4.8.4-2ubuntu1~14.04.4) 4.8.4
Copyright (C) 2013 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
When I run the example listed in the attach-interrupt
selection this does not register the interrupt correctly:
(gpio/attach-interrupt input-pin :change control-light)
Are *command-line-args*
supposed to be accessible?
When I put:
;;; with-args.clj
(println *command-line-args*)
In with-args.clj
, and run:
$ ferret -i file-name.clj -c
[+] Compiling
[+] Formatting Code
[+] Building Binary
[+] Compiler: g++
[+] Options:
-std=c++11
[+] Include Path:
[+] Library Path:
[+] Link:
[+] Done
And do:
$ ./a.out 1 2 3
I get:
()
Is there a compiler config I'm missing?
Currently, it looks like map is defined as only applying f to everything in a single collection. This is missing a lot of the functionality of other lisp's map as being able to map across non-binary functions is very useful.
(map + (list 1 2 3) (list 4 5 6))
I have noticed that ferret’s goal isnt speed, but having the ability to wrap/embed C/C++ is actually a great way to achieve it.
Just as we see ferret-genann, there could be something related to vector/matrix/linalg C/C++ libs easily right?
ArrayFire has been open-sourced and the guys even have a repo just for wrappers and, could it be a good choice for such ends?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.