Nezufun is a Lisp-ish impure functional programming language with dynamic typing and eager evaluation. It is a simple, stupid language with very little abstraction that can be described as a “low level functional language”
Nezufun is designed to be compiled and run on a custom purpose VM, but could be compiled to native code or run interactively.
The main purpose of Nezufun is to evaluate expressions, so Nezufun programs are lists of expressions to evaluate in order. Since Nezufun is an impure language, some of those expressions can cause side effects when evaluated. Optionally, the results of an evaluated expression can be assigned to a global name which can be referred to later.
Expressions come in a few forms:
Literals can be strings, numbers, or booleans. They always evaluate to their own value.
Strings are encosed in double quotes, and can span multiple lines. The backslash character can be used to insert newlines and the double quote character without ending a string.
"one line string"
"multi
line
string"
"string\nwith \"escape characters\""
Numbers are either integers or floating point values. The precision of these is left implementation defined. Floating point values must always contain a decimal point with at least one digit before and after it. Integers and floats can be prefixed with a minus sign ‘-’ without whitespace to indicate a negative number.
Nezufun doesn’t support scientific notation.
Integers:
42
-8
Floats:
42.0
42.9
-8.2
There are only two boolean literals: true
and false
.
Special forms are reserved for constructs which are evaluated differently from other operators or functions, and therefore cannot be partially applied. Here are the special forms in Nezufun:
let name value_of_name in_expression
if condition true_case false_case
do ( expression* )
and left right
or left right
TODO: Specify how are these compiled differently
Names are identifiers that are bound to values. Names are either bound in the
global space by def
, or they are bound locally by fun
or let
. Whether a
name refers to a global value or a local value is determined statically at
compile time.
TODO: really nail down what names are valid, since nf should be able to support basically any kind of name
The convention is to use kebab-case
, and to use the question mark as a suffix
for the names of predicates, and the exclamation mark as a suffix for functions
which produce side effects. However, Nezufun does not enforce these rules and
any naming convention can be used.
Nezufun is always encoded in ASCII. The following grammar is given in ABNF form according to RFC 5234, with the modification that the following rules are case sensitive.
;--CHAR TYPES--
;; space is one or more space char
space = 1*(%x00-0x20 / %x7F)
digit = %x30-%x39
alpha = %x41-5A / %x61-7A
;; all parens are allowed to have no space before or after them
rparen = [space] "(" [space]
lparen = [space] ")" [space]
;--LITERALS--
; numbers
minus = "-"
decimal = "."
integer = [minus] 1*digit
float = [minus] 1*digit decimal 1*digit
number = integer / float
; strings
quote = %x22
;; everything except a quote or a backslash
str_char = %x00-%x21 / %x23-5B / %x5D-7F
esc_code = "\"" / "\n"
string_lit = quote *(str_char / esc_code) quote
; booleans
bool = "true" / "false"
;--NAMES--
; names can contain letters, "-", "_", "!", "?", and digits, but they must start
; with a letter
name_char = alpha / digit / "-" / "_" / "!" / "?"
name = alpha *name_char
;--FUNCTION APPLICATION--
app = name rparen [expr *(space expr)] lparen
;--SPECIAL FORMS--
lambda = ("fun" / "\") (space name) (space expr)
lambda =/ ("fun" / "\") lparen (name (space expr)) rparen
let = "let" (space name) 2(space expr)
let =/ "let" lparen (name 2(space expr)) rparen
if = "if" 3(space expr)
if =/ "if" lparen (expr 2(space expr)) rparen
do = "do" lparen (expr *(space expr)) rparen
logic = "not" (space expr)
logic =/ "not" lparen [expr] rparen
logic =/ ("and" / "or") 2(space expr)
logic =/ ("and" / "or") lparen [expr *(space expr)] rparen
special = lambda / let / if / do / and / or
;--OTHER BUILTINS--
; 1 = 1 argument
; 2 = 2 arguments
; 2v = 2 or more arguments
; arithmetic functions
arith1 = "ceil" / "floor" / "round" / "trunc" / "sqrt" / "abs" / "sign"
arith1 =/ "sin" / "cos" / "tan" / "asin" / "acos" / "atan"
arith1 =/ "log" / "ln" / "exp"
airth1 =/ "int?" / "float?" / "num?"
arith2 = "%" / "pow" / "gt?" / "lt?" / "gte?" / "lte?"
arith2v = "+" / "-" / "*" / "/" / "min" / "max"
; list functions
list1 = "head" / "tail" / "rev" / "list?" / "nil?"
list2 = "cons" / "app"
list_con = "list" lparen [expr *(space expr)] rparen
nil = "nil"
; generic operators
gen1 = "size" / "print"
gen2 = "eq?"
builtin1 = arith1 / list1 / gen1
builtin2 = arith2 / list2 / gen2
builtin2v = artith2v
builtin = (builtin1 1(space expr)) / (builtin1 lparen *1(expr) rparen)
builtin =/ (builtin2 2(space expr)) / (builtin2 lparen *2(expr) rparen)
builtin =/ (builtin2v 2(space expr)) / (builtin2v lparen *(expr) rparen)
builtin =/ list_con / nil
;--EXPRESSIONS--
expr = number / string / boolean
expr =/ name
expr =/ app
expr =/ special
expr =/ builtin
;--DEFINITIONS AND PROGRAMS--
definition = "def" (space name) (space expr)
definition =/ "def" lparen ((space name) (space expr)) rparen
line = expr / definition
program = [space] line *(space line) [space]