The Crumb Programming Language
Crumb is a high level, functional, interpreted, dynamically typed, general-purpose programming language, with a terse syntax, and a verbose standard library.
It features:
- Strictly no side effects* to help you write functional code
- The ability to localize the effects of imported Crumb files.
- Dynamic typing and garbage collection.
- 0 keywords, everything is a function.
*With the exception of IO
Click here to Get Started.
table = (map (range 10) {_ y ->
<- (map (range 10) {item x ->
<- (multiply (add x 1) (add y 1))
})
})
From examples/mult-table.crumb
From examples/game-of-life.crumb
Find more examples under the examples
directory.
Syntax
Crumb utilizes a notably terse syntax definition. The whole syntax can described in 6 lines of EBNF. Additionally, there are no reserved words, and only 7 reserved symbols.
EBNF
program = start, statement, end; statement = {return | assignment | value}; return = "<-", value; assignment = identifier, "=", value; value = application | function | int | float | string | identifier; application = "(", {value}, ")"; function = "{", [{identifier}, "->"], statement, "}";
Crumb syntax diagram, generated with DrawGrammar.
Tokens
"="
"("
")"
"{"
"}"
"->"
"<-"
identifier
int
float
string
start
end
Specifics
Strings are characters surrounded by quotes, for example:
"hello world"
"this is\nsplit between new lines"
"\e[31mthis text is in red\e[0m"
Escape codes in crumb are equivalent to their respective C escape codes. The list of supported escape codes is:
"\a"
"\b"
"\f"
"\n"
"\r"
"\t"
"\v"
"\e"
"\\"
"\""
Integers are groups of number characters, that may be preceded by -
for example:
Floats are like integers, but have a decimal in them, for example:
Identifiers are any collection of characters, that are not separated by whitespace, don't begin with quotes or numbers, and are not any reserved symbols, for example:
Comments start with "//", and end with the end of a line, for example:
// this is a program that prints hi
(print "hi") // this prints hi
Getting Started
Install
For a quick start, you can use Replit, by forking this repl.
First, clone this repo,
git clone https://github.com/liam-ilan/crumb.git
Then compile with gcc,
gcc src/*.c -Wall -lm -o crumb
Or with clang,
clang src/*.c -Wall -lm -o crumb
From there, run a quick example,
./crumb examples/car.crumb
Or run your own code,
The Crumb interpreter is built for POSIX compliant systems, and utilizes
ioctl.h
andunistd.h
. To use Crumb on windows, either use WSL, or use a Linux container.
Basics
All function calls are done with s-expressions (think lisp). For example,
In this case, the function print
is applied with the string
"hello world"
as an argument.
All data in crumb is one of 6 different types:
string
integer
float
function
/native function
list
void
We can store this data in variables, for example,
We can combine data together to form lists,
magic_list = (list 123 "hello" 42.0)
Lists are always passed by value.
We can encapsulate code in functions using curly braces,
f = {
(print "Funky!")
}
(f) // prints "Funky"
Functions can get arguments, denoted using the "->" symbol. For example,
add_two_things = {a b ->
(print (add a b))
}
(add_two_things 3 5) // prints 8
They can also return values using the "<-" symbol,
geometric_mean = {a b ->
<- (power (multiply a b) 0.5)
}
(print (geometric_mean 3 5)) // prints 5.83...
Functions operate in a few important ways:
- Function applications are dynamically scoped.
- Functions cannot create side effects.
- Like in JavaScript and Python, functions can be passed into other functions as arguments.
Most of the features you may expect in a programming language are implemented in the form of functions. For example, here is a Fizzbuzz program using the add
, loop
, if
, remainder
, is
, and print
functions,
(loop 100 {i ->
i = (add i 1)
(if (is (remainder i 15) 0) {
(print "fizzbuzz\n")
} {
(if (is (remainder i 3) 0) {
(print "fizz\n")
} {
(if (is (remainder i 5) 0) {
(print "buzz\n")
} {
(print i "\n")
})
})
})
})
You should now be ready to write your own Crumb programs! More info on how to build applications with events, files, code-splitting, and more, is found in the standard library documentation below.
Standard Library
IO
-
arguments
- A list command line arguments, like argv in C.
-
(print arg1 arg2 arg3 ...)
- Prints all arguments to stdout, returns nothing.
-
(input)
- Gets a line of input from stdin.
-
(rows)
- Returns the number of rows in the terminal.
-
(columns)
- Returns the number of columns in the terminal.
-
(read_file path)
- Returns the contents of the file designated by
path
, in a string path
:string
- Returns the contents of the file designated by
-
(write_file path contents)
- Writes the string
contents
into the file designated bypath
, returns nothing. path
:string
contents
:string
- Writes the string
-
(event)
- Returns the ANSI string corresponding with the current event. This may block for up to 0.1 seconds.
-
(use path1 path2 path3 ... fn)
- Crumb's code splitting method. Runs code in file paths, in order, on a new scope. Then uses said scope to apply
fn
. path1
,path2
,path3
, ...:string
fn
:function
- Crumb's code splitting method. Runs code in file paths, in order, on a new scope. Then uses said scope to apply
Comparisons
-
(is a b)
- Checks if
a
andb
are equal, returns1
if so, else returns0
.
- Checks if
-
(less_than a b)
- Checks if
a
is less thanb
, returns1
if so, else returns0
. a
:integer
orfloat
b
:integer
orfloat
- Checks if
-
(greater_than a b)
- Checks if
a
is greater thanb
, returns1
if so, else returns0
. a
:integer
orfloat
b
:integer
orfloat
- Checks if
Logical Operators
-
(not a)
- Returns
0
ifa
is1
, and1
ifa
is0
. a
:integer
, which is1
or0
- Returns
-
(and a b)
- Returns
1
ifa
andb
are both1
, else returns0
a
:integer
, which is1
or0
b
:integer
, which is1
or0
- Returns
-
(or a b)
- Returns
1
ifa
orb
are1
, else returns0
a
:integer
, which is1
or0
b
:integer
, which is1
or0
- Returns
Arithmetic
-
(add arg1 arg2 arg3 ...)
- Returns
arg1
+arg2
+arg3
+ ... - Requires a minimum of two args
arg1
,arg2
,arg3
, ...:integer
orfloat
- Returns
-
(subtract arg1 arg2 arg3 ...)
- Returns
arg1
-arg2
-arg3
- ... - Requires a minimum of two args
arg1
,arg2
,arg3
, ...:integer
orfloat
- Returns
-
(divide arg1 arg2 arg3 ...)
- Returns
arg1
/arg2
/arg3
/ ... - Requires a minimum of two args
arg1
,arg2
,arg3
, ...:integer
orfloat
- Returns
-
(multiply arg1 arg2 arg3 ...)
- Returns
arg1
*arg2
*arg3
* ... - Requires a minimum of two args
arg1
,arg2
,arg3
, ...:integer
orfloat
- Returns
-
(remainder a b)
- Returns the remainder of
a
andb
. a
:integer
orfloat
b
:integer
orfloat
- Returns the remainder of
-
(power a b)
- Returns
a
to the power ofb
. a
:integer
orfloat
b
:integer
orfloat
- Returns
-
(random)
- Returns a random number from 0 to 1.
Control
Types
-
void
-
(integer a)
- Returns
a
as aninteger
. a
:string
,float
, orinteger
.
- Returns
-
(string a)
- Returns
a
as astring
. a
:string
,float
, orinteger
.
- Returns
-
(float a)
- Returns
a
as afloat
. a
:string
,float
, orinteger
.
- Returns
-
(type a)
- Returns the type of
a
as astring
.
- Returns the type of
List and String
-
(list arg1 arg2 arg3 ...)
- Returns a
list
, with the arguments as it's contents.
- Returns a
-
(length x)
- Returns the length of
x
x
:string
orlist
.
- Returns the length of
-
(join arg1 arg2 arg3 ...)
- Returns all args joined together.
- All args must be of the same type.
arg1
,arg2
,arg3
, ...:string
orlist
.
-
(get x index1)
or(get x index1 index2)
- Returns the item in
x
atindex1
. If x is astring
, this is a single char. - If
index2
is supplied, returns a sub-array or substring fromindex1
toindex2
, not includingindex2
. x
:string
orlist
.index1
:int
.index2
:int
.
- Returns the item in
-
(insert x item)
or(insert x item index)
- Returns a
list
orstring
, in whichitem
was inserted intox
atindex
. Does not overwrite any data. - If
index
not supplied,item
is assumed to be put at the end ofx
. x
:string
orlist
.item
:string
ifx
isstring
, else anyindex
:int
.
- Returns a
-
(set x item index)
- Returns a
list
orstring
, in which the item located atindex
inx
, was replaced byitem
.Overwrites data. x
:string
orlist
.item
:string
ifx
isstring
, else anyindex
:int
.
- Returns a
-
(delete x index1)
or(delete x index1 index2)
- Returns a
string
orlist
, whereindex1
was removed fromx
. - If
index2
is supplied, all items fromindex1
toindex2
are removed, not includingindex2
. x
:string
orlist
.index1
:int
.index2
:int
.
- Returns a
-
(map arr fn)
- Returns a list created by calling
fn
on every item ofarr
, and using the values returned byfn
to populate the returned array. arr
:list
fn
:function
, which is in the form{item i -> ...}
, whereitem
is the current item, andi
is the current index.
- Returns a list created by calling
-
(reduce arr fn initial_acc)
or(reduce arr fn)
- Returns a value, computed via running
fn
on every item inarr
. With every iteration, the last return fromfn
is passed to the next application offn
. The final returned value fromfn
is the value returned fromreduce
. arr
:list
.fn
:function
, which is in the form{acc item i -> ...}
, whereitem
is the current item,acc
is the accumulator (the result offn
from the last item), andi
is the current index.acc
isinitial_acc
if supplied, orvoid
if not.
- Returns a value, computed via running
-
(range n)
- Returns a list with the integers from
0
ton
, not includingn
. n
:integer
, which is greater than or equal to 0.
- Returns a list with the integers from
-
(find x item)
- Returns the index of
item
inx
. Returnsvoid
if not found. x
:string
orlist
item
:string
ifx
isstring
, else any
- Returns the index of
Development
When debugging the interpreter, it may be useful to compile with the -g
flag.
gcc src/*.c -g -Wall -lm -o crumb
This will allow Valgrind to provide extra information,
valgrind --leak-check=full -s ./crumb -d YOURCODE.crumb
To obtain debug information about how your code is interpreted (Tokens, AST, etc.), add the -d
flag.
./crumb -d YOURCODE.crumb
Credit
from Hacker News https://ift.tt/3SPqrW2
No comments:
Post a Comment
Note: Only a member of this blog may post a comment.