This was a fun project to help me learn more about PLD and Go, but I don't really have much time for it or interest in it anymore. You are more than welcome to fork it, or check out ABS which accomplishes many of the same goals and has an active developer community.
Simple interpreted programming language for similar use-cases as Python and
Node. Meant to be cozy
no matter what languages you already know.
This is a WIP. See the TODO.
cozy
(always spelled all-lowercase) is a simple, medium-to-high-level,
interpreted, general purpose programming language. It can be used for many of
the same tasks as shell scripts, Python, Node, and Ruby. It's dynamically and
strongly typed, with some with features that work well with pseudo-functional
programming but C-language-family syntax. There are no OOP constructs like
classes; instead we have first-class functions, closures, plain data structures,
and file-based modules.
It's meant to feel comfortable and familiar no matter what languages you might already know (hence the name). Performance isn't an explicit goal, but cozy is fairly fast (time the examples and try a benchmarking tool against the http server if you're curious). It's also meant to have a relatively simple host-language implementation (for example, much of the standard libarary is written in cozy itself) to make debugging easy and porting to other host languages in the future straightforward.
# contrived example to show off some features and syntax; see the ./examples
# directory for more.
# reduce is a built-in method on arrays, int? is a built-in type checking
# function, and sum is a built-in method on arrays, but this example shows
# how they might be implemented by a user.
# let is for immutable variables
let reduce = fn (fun, xs, init) {
# mutable is only available within blocks, not at at the top level
mutable acc = init
foreach i, x in xs {
acc = fun(x, acc, i)
}
return acc
}
# identifiers can have unicode (for example, chinese characters),
# and question marks
let ints? = fn (xs) {
foreach x in xs {
# util is a builtin module
if util.type(x) != "integer" && util.type(x) != "float" {
return false
}
}
# returns can be implicit (without the `return` keyword)
true
}
let sum = fn (xs) {
# basic assertions and a TAP-producing test library are built in
util.assert(ints?(xs), "expected only numbers!")
return reduce(
fn (x, acc) {
return x + acc
}, xs, 0)
}
# only one level of equality checking, unlike JS's == vs ===
print(sum([1, 2, 3, 4]) == 10) # true
For more examples and documentation, see the examples and stdlib directories. The examples also serve as a second test suite. For vim, CLOC, and Ctags support, see the editor directory. Screenshot in vim:
Originally designed by writing a bunch of examples and a small stdlib. Implementation started as a fork from skx's version of the language from the Go Interpreters Book, and also includes some pieces of prologic's upstream version and ABS, among others (see comments). The differences between cozy and Monkey are too numerous to list here; it's best to think of it as a totally separate language. It's written in pure Go with minimal third-party dependencies, with a large amount of the standard library implemented in cozy itself.
This is the first large Go program I've worked on, so contributions, especially in areas where I didn't write idiomatic Go, are definitely welcome. See CONTRIBUTING for contribution guidelines.
Clone the repo and run make
, and either copy the binary to somewhere in your
path or run make install
. Write some code (see the examples), and run cozy ./your-code.cz
. You can also run without a specified file, in which case your
entered code will be evaluated when you exit with ctrl+d
.
print
adds an ending newline, use orsys.STDOUT
/sys.STDERR
for raw text- No undefined or uninitialized variables Comments are Python/Shell style Errors
- are values, so you can pass them around and use
panic
(like in Go) Using set
anddelete
on hashes returns a new hashlet
is for immutable- variables;
mutable
is for mutable ones; this is because setting mutable variables should be more annoying to do than setting mutable ones. - Uses Go's GC; porting to a different language might require writing a new GC.
- Semicolons are optional Most statements are expressions, including if/else;
- this also means implicit
returns (without the
return
keyword) are possible - No top level mutable variables, because all top level variables are exported
- Parens and braces are optional in
for
,foreach
, andif
expressions, as long as what would be between them is only one expression (would normally be typed on one line) - No ternary expressions, switch statements, or pattern matching; if statements are expressions and type-checking is dynamic, so there's no need for extra keywords or syntax
- REPL history is stored at
$HOME/.cozy_history
, and the size (in lines) can be configured with the env varCOZY_HISTSIZE
- REPL config is stored at
$HOME/.cozy_init
and can contain any valid cozy code
Global functions:
error
creates a new error objectimport
imports another cozy file as a modulepanic
prints an error contents and exitsprint
Write values to STDOUT with newlines
Builtin modules (see examples for docs):
core
fs
http
json
math
net
sys
time
util
See also the standard library (written mostly in cozy itself).
cozy doesn't care about formatting. You can use two spaces, four spaces, seventeen spaces, three spaces and a tab, or whatever. Semicolons are also optional in most cases (similar to the rules in JavaScript), and parenthesis and curly braces are also optional in conditions and loops.
The semi-official style which should be followed when submitting changes is fairly obvious from the examples and standard library:
- Four spaces to indent
- Use a space between identifiers and operators, with the exception of mutating postfix operators
- Use a space between the
fn
keyword and opening paren - Use a space between opening/closing parens and opening/closing braces
- Docstrings should be at the top of the function
- Imports should be at the top of the module
- Line length should not exceed 80 characters
- Semicolons should not be used except in ambiguous situations
- Identifiers should use
snake_case
This code is licensed MIT. I've used code from various Monkey implementations, which are usually licensed MIT. Other code used in cozy includes comments with notes on the licenses.