Tofu is a project mainly written in ..., it's free.
A very impractical small programming language.
Tofu is a small programming language made just for fun. Don't try to find any practical value from the language.
There are 5 built-in types in Tofu.
Every type is derived from function type, so all values are like function. Actually there are no types; there are only functions, just behave as other types.
Following code defines some functions.
abs <- (number): (number < 0)("-1", 1) * number
max <- ([numbers]): {
top <- numbers(0)
foreach numbers, (number): {
.top <- (top > number)(top, number)
}
top
}
You may be able to infer some interesting rules.
The assignment operator is not =
, but <-
.
No named function definition syntax; However, you can assign function values to variable name, like JavaScript, Lua. Tofu supports lambda functions with following syntax:
(arg1, arg2): form
(arg1, arg2): { form; form2 }
Two literal types. General function literal (block), and string literal.
There's no exact numeric type, but only a string type.
For example, 123
same as "123"
.
When boolean values are called, they behave like trinary operator in the other well-known languages such as C and Java.
There's no specific number type. Instead, it can use arithmetic operators
like +
, *
with string values.
Instead of the return
keyword in other languages, it just returns last
expression of function block, in similar way to begin
of Scheme.
All values are functions and all functions are values in the Tofu programming language. To call function generally, you should name it. 'Naming' in computer programming means assignment or binding.
add <- (x, y): x + y
We can call evaluating (x + y)
add(x, y)
. After this naming, add(3, 9)
means (3 + 9)
viz. 12
. Functions can be more complex and longer.
multiply <- (x, y): {
result <- 0; i <- 0
loop (): {
result <- add(result, x)
i <- i + 1; i <= y
}
result
}
When the function contains two or more expressions, curly brackets should wrap its body, and EOL or semicolon separates each expression.
However, above code is a just example. You can multiply easily by *
.
multiply <- (x, y): x * y
Functions keep their own context environment. Read a following example code:
accumulator <- (initval <- 0): {
(num <- 0): {
.initval <- initval + num
}
}
acc <- accumulator(1)
acc(2)
acc(3)
acc()
The last call returns 6
. The form .var <- val
is similar to
(set! var val)
in Scheme.
Language | Definition | Assignment |
---|---|---|
Tofu | name <- value |
.name <- value |
Scheme | (define name value) |
(set! name value) |
JavaScript | var name = value |
name = value |
Lua | local name = value |
name = value |
Python (3 or higher) | name = value |
nonlocal name = value |
Perl | my $name = value |
$name = value |
You can omit parentheses in function call when possible and readable. Following two codes share the same behavior.
func 1, 2, 3
func(1, 2, 3)
However you cannot omit it when there are no arguments to pass into func
.
Following expression returns func
itself, not the value in the calling func
.
func
If you want to call func
without any arguments, a pair of parentheses should
be explicit.
func()
Omitting parentheses of function's arguments makes calling higher-order function and applying function with list or dictionary prettier.
# function with list and dictionary
func []
func [1, 2, 3]
func {}
func { a <- 1; b <- 2 }
# higher-order function
map (filter [1, 2, 3, 4], (x): x % 2 == 1), (x): {
log(x & "\n")
}
Tofu programming language has no attributes. It is just syntactic sugar for calling function. Following two expressions are the same.
function.attribute
function("attribute")
In other words, all function call can be also attribute accessor of the object. Likewise setting attribute is translated into calling function. The name of attribute passes into first parameter and value to set passes into second it.
function.attribute <- value
function("attribute", value)
You can apply this feature to imitate object's method call. It will behave as higher-order function.
object.method()
object("method")()
You can use non-negative numeral as the name of attributes also.
list.0 # list(0)
list.0 <- value # list(0, value)
There are no operators in Tofu programming language. However, pseudo-methods call can seem to use operators. Following expressions are the same.
value operator operand # pseudo-operator style
value.operator(operand) # pseudo-method style
value("operator")(operand) # higher-order function style
The pseudo-operator syntax can be used only when just one argument passes into method.
There are two types of the string literal in Tofu programming language.
The double quotation string literal is a widely used way to represent string values. It can contains any characters except double quotation character.
"To be, or not to be."
"1234"
"!@#@!#@!#"
"string with
a line break"
To contain double quotation character, it should be escaped with backslash.
"Lin Chi said, \"If you meet the Buddha on the road, kill him.\""
The numerical string literals are used usually to represent numbers. Because
there's no specific number types in Tofu. However, 123
equals "123"
.
42
"3.14159265"
To contain one or more values, you can use containers. There are two built-in container types.
[]
, [1, 2, 3]
, ["hello", [1, 2], (x): x + 1]
{ name <- "Hong Minhee"; age <- 20 }
Lists have index numbers each element which starts from 0, and, it does't limit the number of elements. It accepts any type.
list <- ["a", "b", "c", [1, 2, 3]]
You can count the number of elements in the list. Use count
function.
count(list) # 4
To iterate the list, use foreach
function. This is a higher-order function
which takes a function takes one or two arguments: value and optional index
number of element.
foreach list, (value): stdout.write(value ++ "\n"
# it prints elements of list
foreach list, (value, index): { # it prints elements and its index
stdout.write(index ++ ". " ++ value) # of list
stdout.write("\n")
}
If you need for
-like loop of C/C++, use function range
. It generates a
list that contains sequential numbers. It is equal to the function of the
same name in Python.
range(5) # [0, 1, 2, 3, 4]
range(3, 7) # [3, 4, 5, 6]
range(2, 12, 3) # [2, 5, 8, 11]
foreach range(1, 10), stdout.write # it prints: 123456789
Maps have keys of string and its values. The key and its value are called
"pair", and you can translate pairs of the map to the two-dimensional list by
function pairs
.
name <- { family <- "Hong"; given <- "Minhee" }
pairs(name) # [["family", "Hong"], ["given", "Minhee"]]
There are keys
and values
also.
keys(name) # ["family", "given"]
values(name) # ["Hong", "Minhee"]
There is no order in the map, so, order of the list which returned by pairs
,
keys
and values
depend on implementation.
Functions count
and foreach
are generic: it works well for maps also.
count(name) # 2
foreach name, (key): write(stdout, key ++ ": " ++ name(key) ++ "\n")
# above prints: (but order of lines are not deterministic.)
# family: Hong
# given: Minhee
All types of values in Tofu are immutable.
Lists and maps are also immutable. So you cannot append a list or set a new key of a map. Instead, they returns a new list or a new map.
lst <- [1, 2, 3]
newlst <- lst ++ [4, 5] # [1, 2, 3, 4, 5]
newlst2 <- newlst << 6 # [1, 2, 3, 4, 5, 6]
In the same manner, strings are immutable.
str <- "abcd"
newstr <- str ++ "ef" # "abcdef"
replace(newstr, "ab", "AB") # "ABcdef"
<program> ::= { <expr> <terminate> } [ <expr> ]
<termiate> ::= ( ";" | <newline> ) [ <terminate> ]
<expr> ::= "(" <expr> ")"
| <literal>
| <id>
| <attr>
| <apply>
| <operator>
| <define>
<literal> ::= <str-literal>
| <dict-literal>
| <func-def>
| <list-literal>
<str-literal> ::= /"([^"]|\\.)*"/
| <number>
<number> ::= /\d+/
<dict-literal> ::= "{" <program> "}"
<func-def> ::= "(" <params> ")" ":" <dict-literal>
<params> ::= { <id> "," } [ <id> ]
<list-literal> ::= "[" { <expr> "," } [ <expr> ] "]"
<lvalue> ::= [ "." ] <id>
| <attr>
<id> ::= /[^[:digit:][:space:]][^[:space:]]*/ except "<-"
<attr> ::= <expr> "." ( <id> | <number> )
<apply> ::= <expr> "(" <args> ")"
| <expr> <args>
<args> ::= { <expr> "," } [ <expr> ]
<operator> ::= <expr> <id> <expr>
<define> ::= <lvalue> "<-" <expr>
Tofu is designed and implemented by Hong Minhee. http://dahlia.kr/
The implementation is distributed under MIT license.
Copyright (c) 2010 Hong Minhee <http://dahlia.kr/>
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.