Skip to content
/ funky Public

Funky LISP is a LISP-based scripting language.

Notifications You must be signed in to change notification settings

gregghz/funky

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

66 Commits
 
 
 
 
 
 
 
 

Repository files navigation

Funky LISP

Funky LISP is a scripting language designed for interoperation between short-lived processes as through a Unix pipe or sockets. Features include loose typing, lazy evaluation, native support for JSON objects, implicit currying, LISP-style macros, and a little non-LISPy syntactic sugar to help mitigate the paren hell that most LISP n00bs whine about.

Quick Start

If you have clang or gcc, you should be able to compile everything fine. In order to run the tests, you will need valgrind.

$ cd src

$ make funky

$ echo ‘(+ 3 2 1)’ | ./funky

Intro

For the most part, Funky is pretty much another LISP.

Procedure Definition

Defining procedures works basically like with Common LISP:

(def foo (x y) (+ x y))

The procedure’s name immediately follows the call to the definition macro (so it’s not inside the operand definition expression like in scheme-descended LISP’s).

Naming a procedure is optional. If you omit the procedure name in a def call, it becomes an anonymous procedure (a lambda).

((def (x y) (+ x y)) 3 4)

Output:

7

Macro Definition

Macros work much like they do in other lisps.

(mac yig (x y) (append x y))

(yig (1 2 3) (4 5 6))

OUTPUT:

(1 2 3 4 5 6)

Macros can also be anonymous.

((mac (x y) (append x y)) (1 2 3) (4 5 6))

Output:

(1 2 3 4 5 6)

List Construction and Sugar

We’ve added some sugar to help make things a little more legible to the unenlightened. The following lines are interchangeable…

[1 2 3]

(list 1 2 3)

Commas are white-space characters so people coming from something like a Python background can construct lists the way they’re used to. The following lines are interchangeable.

[1,2,3]

(list 1 2 3)

Grids

We have a native hash table type called a “grid”

(grid (pair “key” “val”) (pair “one” 1))

There’s syntactic sugar so it can be annotated the way you might do for JSON or Python dicts. The following lines are interchangeable:

(grid (pair “key” “val”) (pair “one” 1))

{“key” : “val”, “one” : 1}

The “.” operator gets elements from a grid. The following lines are interchangeable:

(get mygrid “foo”)

mygrid . “foo”

Output: FOO

In order to improve our ability to consume Javascript, the . and : operations break the symbol boundary. The following lines are interchangeable:

{“blah”:429857, “narghe”:92090}

{“blah” : 429857, “narghe” : 92090}

As are these:

gridThing . “key”

gridThing.”key”

If a grid’s key is an atom rather than a string, you needn’t surround it in quotes when getting its value.

(set! yourmom {stinks:true})

yourmom.stinks

Output:

true

Comments

Though this is an anticommentist (http://www.anticommentist.org) project and the goal is to make the language unambiguous enough that comments are not needed for the vast majority of algorithms, we wanted to be able to make scripts in Funky usable from the shell with shebangs. So we lifted the shell script convention of # denoting comments. Everything from # to the end of the line will be ignored by the interpreter.

Currying, Arity and Strict Application

All procedures in Funky are variatic (they can take any number of arguments). The output of the procedure may change based on the number of arguments given.

Funky impicitly curries incomplete procedure calls.

(def foo (x y z) (+ x y z))

(set! threeplus (foo 3))

(set! threeplustwoplus (threeplus 2))

(threeplustwoplus 2)

Output:

7

When extra arguments are given, they are stored in the &args list for the scope of that procedure. The following funk will print the first two arguments given on their own lines, all subsequent arguments on the same line, and will return the last argument given (per the rules of print).

(def bar (x y) (print x) (print y) (apply print &args))

(bar 3 4 5 6 7 8 9)

If it’s more important that you have each operand bound to an argument, you can enforce that with strict-apply. When strictly applying a procedure, incomplete calls or calls with too many arguments evaluate down to an error.

(strict-apply foo (list 1 2))

Output:

(err “Argument count does not match the operand count”)

Application Sugar

Having lots of nested procedure calls can get a bit hard for some folks to read. The first thing that most folks complain about when learning LISP is the abundance of parenthesis. We have sugar to help keep the nesting from getting out of control. Given the following procedure definitions…

(def addthree (x) (+ x 3))

(def addmore (x y z) (+ x y z))

… the following three lines are interchangeable with each other:

(addthree (+ 2 2))

(+ 2 2) -> addthree

addthree <- (+ 2 2)

… and these are interchangeable with each other:

(apply addmore [1 2 3])

[1 2 3] ~> addmore

addmore <~ [1 2 3]

… and these lines are also interchangeable with each other:

(strict-apply addmore [1 2 3])

[1 2 3] S> addmore

addmore <S [1 2 3]

This sugar, when combined with implicit currying, can chain together to break deeply nested expressions into small, legible parts:

(seq 1 10) -> (map (def (x) (+ x 3))) ~> print

Output:

4 5 6 7 8 9 10 11 12 13

Applying Grids

When grids are applied to procedures, apply will try to correlate each operand with a key in the grid and bind the value as its argument.

{fooby:”FOOBY”, barby:”BARBY”} ~> (def (fooby barby) (print fooby barby))

Output:

FOOBY BARBY

If the operand is not found as a key in the grid, nil is passed in for the unfound argument.

{barby:”BARBY”} ~> (def (fooby barby) (print fooby barby))

Output:

nil BARBY

Strict-apply is not very useful when applying grids. Nothing from grids ends up in the &args local variable. In the future, it may become worth-while to have incomplete application of grids result in a curried procedure asking for the ungiven arguments instead. But that sounds like arity madness to me.

Laziness

Like other LISP languages, Funky is eagerly evaluated at its core. But since LISP is the True Way of Computing, laziness is easy to add to the language.

The “repeat” procedure will take any expression and repeat it infinitely. The “take” procedure will take the first N elements from the list given (where N is the first argument and the list is the second argument).

(+ 3 2) -> repeat -> (take 4)

Thus we only evaluate repeat until we get the desired four elements from it and then its evaluation.

Documentation

The most unambiguous documentation for this language will always be in the tests. Look at src/test/*.in and the related *.out files to see how everything is generally expected to work. Plan is to (eventually) generate docs from the test data. :P

Gotchas

Some things you’ll want to know:

  1. We don’t (and probably won’t ever) use the ’ token to quote an expression. Instead, the single quote character behaves as it does in most shell scripting languages; signifying the beginning and end of a quoted string. (Just like double-quote does). You can use the quote macro to quote things instead: (quote foo)
  2. We use a “true” keyword instead of “t” to represent truthiness because single-character global constants are stupid.
  3. The funk machine currently reads until EOF before evaluating the text you gave it. This is dumb and should change soon.
  4. Not all of the necessary features are finished
  5. It’s slow/inefficient. It’ll be faster. Promise.
  6. Closures broke while trying to optimize garbage collection. But since we have currying and macros, I’m not losing sleep over that yet. They’ll be back soon.

License

Copyright (c) 2013, Anthony “Ishpeck” Tedjamulia All rights reserved.

Redistribution and use in source and other forms, with or without modification, are permitted provided that the following conditions are met:

Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in other form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

About

Funky LISP is a LISP-based scripting language.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published