kode is a programming language that transpiles to JavaScript.
It is highly inspired by and tries to be compatible with CoffeeScript, while adding some features and further its minimalistic approach:
a = 1...3
for i in 0..5
Square brackets around ranges are optional.
log 'hello' ▸ hello
error 'world!' ▸ world!
Simple shortcuts for log
, warn
and error
methods of console
.
"abc"[-2] ▸ 'b'
[1,2,3][-2] ▸ 2
v[-n]
is a shortcut for v[-n..-n][0]
for number literals n
if
a ➜ X
b ➜ Y
➜ Z
is a shortcut for
if a then X
else if b then Y
else Z
switch x
1
2 3 ➜ X
'abc' ➜ Y
➜ Z
is a shortcut for
switch x
when 1,2,3 then X
when 'abc' then Y
else Z
1 is 'number' and '' is 'string' and {} is Object and [] is Array ▸ true
1 is Number or '' is String or [] is 'array' ▸ false
is
is a shortcut for typeof
and instanceof
.
The first is used, when the right hand side is a string.
"0xFF" is num and "-4.536" is num and 42 is num ▸ true
'' is str and "a" is str and new String("abc") is str ▸ true
{} is obj and new Object() is obj ▸ true
null is obj or new Map() is obj or [] is obj ▸ false
is num
does a string coersion via parseFloat
in the test.
is str
checks both, type == 'string' or instanceof String.
is obj
evaluates to true for plain Objects only.
empty [] and empty {} and empty '' and empty null and empty NaN ▸ true
empty 0 or empty 'a' or empty [null] or empty Infinity ▸ false
Returns true for null, undefined, NaN and empty array, object or string.
valid 0 and valid 'a' and valid [null] and valid Infinity ▸ true
valid [] or valid {} or valid '' or valid null or valid NaN ▸ false
Just the negation of empty
.
obj each (k,v) -> [k,v]
obj each (v) -> v
The each
operator takes an object, array or string on the left hand side and
a function on the right hand side.
The function is called for each key/value, index/item, index/character pair.
A new object, array or string is build from the results and returned:
'hello' each (c) -> c+c ▸ 'hheelllloo'
[1,3] each (i,v) -> [1-i,v*v] ▸ [9,1]
{a:1,b:3} each (k,v) -> ['▸'+k, k+v] ▸ { '▸a': 'a1', '▸b': 'b3' }
If the function takes only one argument, it is the value/item/character and a single return value is expected.
x = null
for a in x
log 'hello'
The above code would throw an exception at runtime in CoffeeScript. kode generates code that doesn't fail if x is not an array.
class C
@: ->
is a shortcut for
class C
constructor: ->
l = [1,2,3]
a = [x for x in l] ▸ [1,2,3]
a = (x for x in l) ▸ [1,2,3]
kode doesn't distuinguish between round and square brackets around list comprehensions.
false ? 1 : 2 ▸ 2
A nifty if
then
else
shortcut.
It introduces some ambiguity in a few corner cases, which can be resolved by different spacing:
null ? a: 'b' ▸ {a:'b'}
kode gives you the option to use the old school function
style classes of CoffeeScript v1:
function C
@: ->
The generated code might be a bit more ugly, but the function based classes don't suffer from some of the limitations of the new class syntax. For example, keywords can be used as method names and super doesn't have to be called first in the constructor. New and old school styles can be used in the same module, but obviously not in the same class hierarchy.
CoffeeScript has a very nice way of initializing arrays:
a = [
1
2
3
]
If you decide to join these into a single line, you have a problem: for each of the lines a comma must be inserted. The same goes for objects that span over multiple lines.
In kode, you don't need to insert commata after number or string literals and POD structures. Those are all valid expressions:
a = [ 1 2 3 ]
a = { b:1 c:2 d:3 }
a = b:1 c:2 d:3
a = b:[ c:2 'd' 3 ]
a = [ [1 2] [d:3] ]
test 'something' ->
it 'should' ->
on 'event' @myCallback
log 'a:' a , 'd:' 3 # some commas still make sense :-)
s = noon a:1 b:2
log s ▸ a 1
▸ b 2
Operator that converts argument into string in noon notation.
Handles recursion, unlike JSON.stringify
.
myObj = a:1 b:2 c:d:3
dbg myObj ▸ file.kode:2:0 myObj
▸ a 1
▸ b 2
▸ c
▸ d 3
dbg '1st' 0 '2nd' myObj.c ▸ file.kode:7:0
▸ 1st 0 2nd d 3
Logs file and position followed by arguments in noon notation. If first argument is an identifier, appends its name to the file position.
f = (a) -> ▴ a ; a ▸ file.kode:1:11 ▴ assert failed! a
▴ 'good' f 1
▴ 'bad' f 0 ▸ file.kode:3:0 ▴ bad f(0)
Logs file position, message and condition code if condition isn't truish. If message is omitted, 'assert failed!' will be used.
●▸ sunny times
fun = ->
● funny times
Math.sqrt x for x in 0..100000
fun() for i in 1..2 ▸ funny times 12 ms
▸ funny times 11 ms
●▪ sunny times ▸ sunny times 26 ms
Logs time difference between matching ●▸
●▪
pairs.
●
is a shortcut that can be used in functions: ●▪
is inserted automatically before the function returns.
process.hrtime.bigint
is used for the timing and the measurements come with μs accuracy.
Due to JavaScripts just in time compilation optimizations, the initial results might be greater than later runs.
a = copy b # shallow copy of b
a = clone b # deep copy of b
Operators that return a shallow or deep copy of their argument. Only plain Objects, Arrays and Strings are copied. Functions, Maps, Sets, etc. are not copied.
a eql b # deep comparison
Does a deep comparison of its operands. Only plain Objects, Arrays and Strings are compared.
kode comes with its own testing 'framework' (in quotes here, because it's too much of a word for such a minimalistic thing:)
▸ test
▸ sub test
something ▸ result
something else ▸ other result
If the ▸
has nothing on its left hand side, it denotes a test section.
Otherwise the expressions on both sides are compared. The files in kode/test
provide plenty of examples how to use this:
the output of two runs:
log Y5 r2 'warning!' ▸ output in dark red on bright yellow
kode injects ansi color code wrappers if it discovers function calls with names like R1
or g8
.
Color functions: 'r' 'g' 'b' 'c' 'm' 'y' 'w' for foreground (uppercase for background) and a number between 1 and 8.
kode is mostly compatible with CoffeeScript. Converting to kode shouldn't be too painful.
Stuff I rarely used and therefore didn't bother to re-implement:
- literal coffeescript
unless
until
or=
by
...when
outside ofswitch
- wrapper code
- string interpolation in object keys
- implicitly returning arrays if last expression is a loop
use
keyword for importinclude
keyword to merge source files- render comments
- sourcemaps
- error messages
▸ return
issues
- trailing if after obj args:
@func obj:1 if truish
- indentation issue in else branches:
if 1 then else # this works @s = Immutable lines:lines if 1 then else # this doesn't :( @s = Immutable lines:lines