Coder Social home page Coder Social logo

nakkaya / ferret Goto Github PK

View Code? Open in Web Editor NEW
1.1K 41.0 48.0 2.18 MB

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

Makefile 100.00%
clojure lisp compiler bare-metal microcontroller embedded-systems arduino teensy atmega arm

ferret's People

Contributors

0atman avatar akkartik avatar chr15m avatar drone29a avatar nakkaya avatar spacepluk avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ferret's Issues

Using syntactic sugar fails.

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.

Question : Using C Arrays

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.

Lambda with a function as parameter throws an error

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.

Elementary error: ‘str’ was not declared in this scope

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!

rem function doesn't work

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.

:noweb-ref property inheritance failure with spacemacs on macOS

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!

calling function in another package produces C++ compiler error

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));
                            ^

REPL in ferret

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!).

LLVM frontend?

Hi!

I'm new to ferret, but I'm wondering whether
an LLVM frontend would make sense for ferret.

println stops working for strings above a certain length

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.

(flatten nil) seg faults

(println (flatten nil)) compiles in ferret and c++ for me, but seg faults at runtime. The docs say it should return an empty list.

ferret uses incorrect path for intermediate c++ file when compiling with g++

❯ /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

native-declare in required packages are generated in reverse order

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];")

Abort During Unit Test Execution

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

assoc is broken

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.

duplicate source block names trip up recent versions of org-babel-tangle

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))))

Easier FFI

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)

Builds with wrong JDK?

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.

More advanced string creation functions

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++. :)

Where is the source code?

I'm a little bit confused as to how ferret.jar is generated. Is the actual source code in ferret.org?

Question : C++ classes?

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?

:as not supported when destructuring sequences

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.

map defined in required package causes error

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)

boolean atom doesn't work when memory pool is configured

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))

reduce without explicit initial accumulator value produces incorrect result

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))))

Code generation can result in functions being included more than once

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 {
            ^~~~~~~

gpio/pin-mode does not work inside loops

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.

lazy-sum.clj example fails on current release [0d31551]

$ 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.

*command-line-args* always empty

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?

map across arbitrary number of collections

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))

ferret-matrix?

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?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.