Giter VIP home page Giter VIP logo

flk's Introduction

Fleck wordmark
Fleck is a Clojure-like LISP that runs wherever Bash is.

Fleck screencast

Note

This project is experimental and I am no longer actively working on it. If you need a feature or bug fixed, please send a PR with passing tests and I will review and merge it.

Thank you.

Get it

curl -s https://chr15m.github.io/flk/flk > flk && chmod 755 flk
./flk

Examples | Reference | Contributing | FAQ | make-a-lisp

What?

$ echo '(println "Hello world!") (println "Hostname:" (sh* "hostname")))' > example.clj
$ ./flk example.clj
Hello world!
Hostname: diziet

Why?

Now you can use a humble LISP to do Bash things. Bash as a scripting language has many edges, but it is everywhere. Fleck attempts to round off the edges.

Fleck runs on Bash 4 and higher.

How?

Almost all of this code is from the make-a-LISP project. All I've done is put together a simple Makefile to package it up into an easily deployable single-file bash script.

Reference

A list of variables, macros and functions that are present in Fleck.

Built-ins

This is the set of built-ins from the make-a-lisp project. These more or less work but are generally more limited in functionality than their Clojure equivalents. For example the addition function (+) can only add two integers at a time.

def! | defmacro! | if | do | fn* | try* | sh* | let* | quote | quasiquote | macroexpand | type | = | throw | nil? | true? | false? | string? | symbol | symbol? | keyword | keyword? | number? | fn? | macro? | pr-str | str | prn | println | readline | read-string | slurp | < | <= | > | >= | + | - | * | / | time-ms | list | list? | vector | vector? | hash-map | map? | assoc | dissoc | get | contains? | keys | vals | sequential? | cons | concat | nth | first | last | rest | empty? | count | apply | map | conj | seq | with-meta | meta | atom | atom? | deref | reset! | swap!

  • *ARGV* - list of arguments passed on the command line.

Aliases

These are wrappers around the limited make-a-lisp versions and are much more limited than the Clojure equivalents.

let | when | def | fn | defn

Mal extras

These functions are pulled from a selection of mal/lib/*.mal.

partial | inc | dec | zero | identity | reduce | foldr

Fleck extras

These functions are hand crafted Fleck specials designed to make common shell scripting tasks easier.

  • (str-replace STRING FIND REPLACE) - Replace all occurrences of the string FIND in STRING with the string REPLACE.
  • (str-split STRING SPLIT-CHARACTER) - Split STRING into a list of strings on the single characters SPLIT-CHARACTER.
  • (str-pos HAYSTACK NEEDLE) - Returns the position of string NEEDLE in string HAYSTACK or -1 if not found.
  • (str-upper-case STRING) - Converts string to all upper-case.
  • (str-lower-case STRING) - Converts string to all lower-case.
  • (str-capitalize STRING) - Converts first character of the string to upper-case, all other characters to lower-case.
  • (dc OPERATOR ARRAY-OF-NUMBERS) - Wraps the dc command to do decimal math. E.g. (dc '+ [1 2 3]) yeilds 6.
  • (env [KEY] [VALUE]) - Returns a hash-map of environment variables. Returns the value of KEY if present. Sets the value of KEY to VAL if the latter is present.
  • (sh! COMMAND ARGS) - Run a bash command with arguments in a subshell. Returns [stdout stderr return-code] from the resulting call.
  • (sh-env COMMAND) - Run a bash command string in the current shell, modifying current env, and return the stdout result (useful for export, source etc.).

Interop

  • (env [KEY] [VALUE]) - See above section.
  • (sh! COMMAND [ARGS]) - See above section.
  • (sh-env COMMAND) - See above section.
  • (sh* COMMAND) - Run arbitrary bash strings in a subshell and return the stdout result.

For examples of writing your own Fleck functions in Bash see src/extras.sh. Functions should set the special return value r and use mal type casting functions like _string to wrap the result in a reference. Internal Fleck functions such as _string automatically do this and can be used bare. Use _fref to make your function available to the Fleck namespace e.g. _fref "my-bash-function" _my_bash_function.

Compile

To compile flk itself run make. This combines the original mal scripts with various bash and flk functions into a single binary.

You can make a pure bash script from your Fleck script by bundling your script and Fleck together into a new script.

Say you have a Fleck script called wow.clj, you can bundle it as follows:

make DEST=wow INSERT=./wow.clj NOREPL=1

This will produce a new standalone script called wow with Fleck + wow.clj bundled together.

When you run wow the embedded wow.clj will be run by the embedded Fleck.

Contributing

Flk is built from the mal sources and uses its test framework.

To contribute please follow these guidelines:

  • Add any new bash functions to extras.sh as this is merged into mal at build time.
  • Add new flk functions into src in a new .clj file, and then add the file name to the LOCALMALS list in the Makefile so it gets included in the build.
  • Any changes, bugfixes, etc. to mal itself should be submitted upstream.
  • Document any new functions in this README.
  • Put unit tests for new functions in a .mal file in the tests folder, and to the Makefile test clause.

FAQ

Think of this as homoiconic Bash rather than Clojure, and code as if you're in Bash.

Will my favourite piece of Clojure run in this?

No, it's bash.

Some subset of Clojure-like code will run. See the documentation and examples.

How do I access command line arguments?

Use the special global list *ARGV*.

How do I access and modify environment variables?

Check the (env) function above. See also examples/environment-variables.clj.

How can I execute a one-liner of Fleck code?

Either of these methods will work:

flk <<< '(println "hi")'
echo '(println "hi")' | flk

Why can't I add more than 2 numbers together?

It's bash. Try the dc function: (dc '+ [1 2 3 4])

Where are the floating point numbers?

It's bash. Try the dc function for decimals: (dc '* [8.2 3.5])

dc is set to keep four fractional digits in its results.

How do I cast a string to a number?

Try (read-str "42") but also Bash doesn't care and (+ "1" 1) will yeild 2.

Why can't I iterate on a string?

Try (seq "somestring").

How do I do destructuring?

You can't.

How do I use a key/hash-map as a function in lookups?

You can't. You'll get an error with something like (:a {:a 12}) or ({:a 12} :a).

Instead you must use get like this: (get {:a 12} :a).

Can I use anything as a hash-map key?

Seems unlikely. Better stick to strings.

This is even slower than Python!

Yes.

PS That is not actually a question.

Haven't I seen this before somewhere?

You're probably thinking of Gherkin, the original Clojure-like LISP in Bash by Alan Dipert. Gherkin helped kick off the make-a-lisp revolution. You might also be thinking of babashka which is a bare-metal solution using real Clojure.

Why is it called Fleck?

At 36k and running on any machine with Bash 4, the name seemed appropriate.

 fleck

    n. A tiny mark or spot.
    n. A small bit or flake.

flk's People

Contributors

abochannek avatar chr15m avatar profavery avatar rosineygp 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

flk's Issues

What is the license?

I see upstream mal is MPL-2.0 (at that, the GPL-incompatible flavor thereof), but given that MPL is file-level copyleft, I can't use that to make assumptions about flk's license. If you could throw a LICENSE or COPYING file in the tree and tag a new version, that would be awesome (otherwise, it's not safe to use, or to package in Linux distros, Brew, etc.)

How do I get CLI args?

Hi!

I have and maintain a number of bash scripts to do multiple tasks around a dev infrastructure.
I was quite excited when I saw fleck because it gave me hope to finally use a decent language and still benefit from not needing to install anything specific to run my scripts on a target machine.

Now.. My scripts need CLI arguments, like deploy -on target_machine some_project. But I can't seem to understand how a fleck script would get those CLI args...

Imagining the contrived example of a sillyecho script, what I was looking for was something like:

#!/usr/bin/env flk

(map (fn [l] (println l)) ARGV)

Where ARGV would be some construct that would give me the arguments from the CLI.

This would be invoked like: sillyecho One Two Three
And would output:

One
Two
Three

Is this possible?
If not, could this be added in?

Thanks!

Keyword as a function seems broken

➜  bin flk                                                            
Fleck
user> ({:foo "bar"} :foo)
/home/dan/bin/flk: line 1359: __5bal7_hmap_572: command not found
(:foo)
user> (:foo {})
/home/dan/bin/flk: line 1359: ʞfoo: command not found
({})
user> (:foo {:foo :bar})
/home/dan/bin/flk: line 1359: ʞfoo: command not found
({:foo :bar})
user>

Am I doing something wrong?

Some suggestions

Hi! Nice project!

Some suggestions, though (maybe I'll work on some of them sometime):

  • add tests + CI pipeline
  • stop versioning the flk file and use github releases instead
  • split flk into a sourceable file + bin entrypoint: I would like to reuse flk's data structure implementations in my existing bash scripts, for example, but flk has code that is run outside functions (autorun when sourced)
  • add an flk --eval option
  • add formula to brew

WDYT?

Unable to pass a literal asterisk to a `sh*` call

Given a file called core-fns.txt containing strings like this:

 [hello]=hello
 [something]=something

The following invocation fails to capture the [hello]:

(sh* "grep -oE '.*\]' core-fns.txt")

Internally the asterisk is escaped into __STAR__ and passed through to bash. The following behaves in an identical way:

(sh* "grep -oE '.__STAR__\]' core-fns.txt")

@kanaka I know it's been a long time since you looked at this but if you have any hints off the top if your head how a user can get around this to pass an asterisk do let me know.

env does not work on mac os

> flk 
Fleck 24461e38
user> (env)
env: illegal option -- 0
usage: env [-iv] [-P utilpath] [-S string] [-u name]
           [name=value ...] [utility [argument ...]]
{}

stdin doesnt read to EOF. only till end of first form

stdin doesnt read to EOF. only till end of first form

$ cat test.flk 
(println "Hello world!") (println "Hostname:" (sh* "hostname")))
$ ./flk test.flk 
Hello world!
Hostname: vash
$ cat test.flk | ./flk 
Hello world!
$ echo "(do $(cat test.flk))" | ./flk 
Hello world!
Hostname: vash

range should be better supported

Not only is there no "range" function implemented directly, I can't even use (sh* "seq 10") to get a number range to do stuff like pinging a IP Range.

Makefile compilation mode failing [needs more info]

When attempting to compile a script using the Makefile example in README:
make DEST=wow INSERT=./wow.clj NOREPL=1 I get the following error:
"./flk: line 1510: warning: here-document at line 1431 delimited by end-of-file (wanted `INLINEMALFILE')"

I am using GNU bash, version 4.4.23(1)-release (x86_64-pc-linux-gnu)

I will keep plugging away at it to see if I can find a cause, but perhaps a reader can shed light on this for me.

Add Shebang support for scripts?

👋

Tried the following and couldn't get it to run:

#!/usr/bin/env flk
(println "hello world")
$ ./test.clj
$

Currently looking into what we should do to make this work 🤔

Help request serialize

There is a good way to serialize and deserialize variables... I build a pmap code and it runs creating a subshell and eval... the result is save in /dev/shm/<key> and I need to save it save and back the values

_pmap_with_type () {
    local constructor="${1}"; shift
    local f="${1}"; shift
    local items="${ANON["${1}"]}"; shift

    # core count
    local cores=0
    while read -r line; do
        if [[ "$line" =~ "processor" ]]; then
            ((cores++))
        fi
    done < "/proc/cpuinfo"

    # create id for shared memory
    time_ms; local pmap_id="${ANON["${r}"]}"

    eval "${constructor}"; local new_seq="${r}"

    local index=0
    for v in ${items}; do        
        (
            eval ${f%%@*} "${v}" "${@}" 
            [[ "${__ERROR}" ]] && r= && return 1;

            # send result to shared memory
            echo "${index} ${r} \"${ANON["${r}"]}\"" >> "/dev/shm/pmap_${pmap_id}"
        ) &

        ((index++))
        
        # threads = cores + 1
        if [[ $(jobs -r -p | wc -l) -gt $cores ]]; then
            wait -n
        fi
    done

    # wait for inital threads
    wait
    
    while  IFS= read -r line; do
        declare -a _array_line="(${line})"

        _obj_type "${_array_line[1]}"
        eval "_${r}" "${_array_line[2]}"
        _conj! "${new_seq}" "${r}";

    done < <(sort -n "/dev/shm/pmap_${pmap_id}")
    rm "/dev/shm/pmap_${pmap_id}"

    r="${new_seq}"
}

The result of code execution:

(1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765 10946 17711 28657 46368 75025 121393 196418 317811 514229 832040 1346269 2178309 3524578 5702887 9227465 14930352 24157817 39088169 63245986 102334155 165580141 267914296 433494437 701408733 1134903170 1836311903 2971215073 4807526976 7778742049 12586269025 20365011074 32951280099 53316291173 86267571272 139583862445 225851433717 365435296162 591286729879 956722026041 1548008755920)


 pmap 8138 ms
(1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765 10946 17711 28657 46368 75025 121393 196418 317811 514229 832040 1346269 2178309 3524578 5702887 9227465 14930352 24157817 39088169 63245986 102334155 165580141 267914296 433494437 701408733 1134903170 1836311903 2971215073 4807526976 7778742049 12586269025 20365011074 32951280099 53316291173 86267571272 139583862445 225851433717 365435296162 591286729879 956722026041 1548008755920)


 map 32914 ms

Which versions of bash does this support?

I'm running OSX catalina... and getting the following errors:
$ bash --version
GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin19)
Copyright (C) 2007 Free Software Foundation, Inc.
$ ./flk
./flk: line 9: declare: -A: invalid option
declare: usage: declare [-afFirtx] [-p] [name[=value] ...]
./flk: line 1089: syntax error near unexpected token <' ./flk: line 1089: [<]=num_lt'
./flk: line 1090: =]=num_lte: No such file or directory
./flk: line 1091: [: missing ]' ./flk: line 1092: [: missing ]'
./flk: line 1093: [+]=num_plus: command not found
./flk: line 1094: [-]=num_minus: command not found
./flk: line 1095: [STAR]=num_multiply: command not found
./flk: line 1096: [/]=num_divide: No such file or directory
./flk: line 1097: [time-ms]=time_ms: command not found
./flk: line 1099: [list]=_list: command not found
./flk: line 1100: [list?]=list?: command not found
./flk: line 1101: [vector]=_vector: command not found
./flk: line 1102: [vector?]=vector?: command not found
./flk: line 1103: [hash-map]=_hash_map: command not found
./flk: line 1104: [map?]=hash_map?: command not found
./flk: line 1105: [assoc]=assoc: command not found
./flk: line 1106: [dissoc]=dissoc: command not found
./flk: line 1107: [get]=get: command not found
./flk: line 1108: [contains?]=contains?: command not found
./flk: line 1109: [keys]=keys: command not found
./flk: line 1110: [vals]=vals: command not found
./flk: line 1112: [sequential?]=sequential?: command not found
./flk: line 1113: [cons]=cons: command not found
./flk: line 1114: [concat]=concat: command not found
./flk: line 1115: [nth]=nth: command not found
./flk: line 1116: [first]=_first: command not found
./flk: line 1117: [rest]=_rest: command not found
./flk: line 1118: [empty?]=empty?: command not found
./flk: line 1119: [count]=count: command not found
./flk: line 1120: [apply]=apply: command not found
./flk: line 1121: [map]=map: command not found
./flk: line 1123: [conj]=conj: command not found
./flk: line 1124: [seq]=seq: command not found
./flk: line 1126: [with-meta]=with_meta: command not found
./flk: line 1127: [meta]=meta: command not found
./flk: line 1128: [atom]=_atom: command not found
./flk: line 1129: [atom?]=atom?: command not found
./flk: line 1130: [deref]=deref: command not found
./flk: line 1131: [reset!]=reset_BANG: command not found
./flk: line 1132: syntax error near unexpected token )' ./flk: line 1132: [swap!]=swap_BANG)'

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.