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 crumbOr with clang,
clang src/*.c -Wall -lm -o crumbFrom 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.handunistd.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:
stringintegerfloatfunction/native functionlistvoid
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
contentsinto the file designated bypath, returns nothing. path:stringcontents: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, ...:stringfn: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
aandbare equal, returns1if so, else returns0.
- Checks if
-
(less_than a b)- Checks if
ais less thanb, returns1if so, else returns0. a:integerorfloatb:integerorfloat
- Checks if
-
(greater_than a b)- Checks if
ais greater thanb, returns1if so, else returns0. a:integerorfloatb:integerorfloat
- Checks if
Logical Operators
-
(not a)- Returns
0ifais1, and1ifais0. a:integer, which is1or0
- Returns
-
(and a b)- Returns
1ifaandbare both1, else returns0 a:integer, which is1or0b:integer, which is1or0
- Returns
-
(or a b)- Returns
1ifaorbare1, else returns0 a:integer, which is1or0b:integer, which is1or0
- Returns
Arithmetic
-
(add arg1 arg2 arg3 ...)- Returns
arg1+arg2+arg3+ ... - Requires a minimum of two args
arg1,arg2,arg3, ...:integerorfloat
- Returns
-
(subtract arg1 arg2 arg3 ...)- Returns
arg1-arg2-arg3- ... - Requires a minimum of two args
arg1,arg2,arg3, ...:integerorfloat
- Returns
-
(divide arg1 arg2 arg3 ...)- Returns
arg1/arg2/arg3/ ... - Requires a minimum of two args
arg1,arg2,arg3, ...:integerorfloat
- Returns
-
(multiply arg1 arg2 arg3 ...)- Returns
arg1*arg2*arg3* ... - Requires a minimum of two args
arg1,arg2,arg3, ...:integerorfloat
- Returns
-
(remainder a b)- Returns the remainder of
aandb. a:integerorfloatb:integerorfloat
- Returns the remainder of
-
(power a b)- Returns
ato the power ofb. a:integerorfloatb:integerorfloat
- Returns
-
(random)- Returns a random number from 0 to 1.
Control
Types
-
void -
(integer a)- Returns
aas aninteger. a:string,float, orinteger.
- Returns
-
(string a)- Returns
aas astring. a:string,float, orinteger.
- Returns
-
(float a)- Returns
aas afloat. a:string,float, orinteger.
- Returns
-
(type a)- Returns the type of
aas 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:stringorlist.
- Returns the length of
-
(join arg1 arg2 arg3 ...)- Returns all args joined together.
- All args must be of the same type.
arg1,arg2,arg3, ...:stringorlist.
-
(get x index1)or(get x index1 index2)- Returns the item in
xatindex1. If x is astring, this is a single char. - If
index2is supplied, returns a sub-array or substring fromindex1toindex2, not includingindex2. x:stringorlist.index1:int.index2:int.
- Returns the item in
-
(insert x item)or(insert x item index)- Returns a
listorstring, in whichitemwas inserted intoxatindex. Does not overwrite any data. - If
indexnot supplied,itemis assumed to be put at the end ofx. x:stringorlist.item:stringifxisstring, else anyindex:int.
- Returns a
-
(set x item index)- Returns a
listorstring, in which the item located atindexinx, was replaced byitem.Overwrites data. x:stringorlist.item:stringifxisstring, else anyindex:int.
- Returns a
-
(delete x index1)or(delete x index1 index2)- Returns a
stringorlist, whereindex1was removed fromx. - If
index2is supplied, all items fromindex1toindex2are removed, not includingindex2. x:stringorlist.index1:int.index2:int.
- Returns a
-
(map arr fn)- Returns a list created by calling
fnon every item ofarr, and using the values returned byfnto populate the returned array. arr:listfn:function, which is in the form{item i -> ...}, whereitemis the current item, andiis the current index.
- Returns a list created by calling
-
(reduce arr fn initial_acc)or(reduce arr fn)- Returns a value, computed via running
fnon every item inarr. With every iteration, the last return fromfnis passed to the next application offn. The final returned value fromfnis the value returned fromreduce. arr:list.fn:function, which is in the form{acc item i -> ...}, whereitemis the current item,accis the accumulator (the result offnfrom the last item), andiis the current index.accisinitial_accif supplied, orvoidif not.
- Returns a value, computed via running
-
(range n)- Returns a list with the integers from
0ton, 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
iteminx. Returnsvoidif not found. x:stringorlistitem:stringifxisstring, 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 crumbThis 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.