Aa
Cliff Click Language Hacking
Install / Use
/learn @cliffclick/AaREADME
aa
Cliff Click Language Hacking
To-the-metal performance: every abstraction used can be peeled away, yielding code that you would expect from a tightly written low-level program. This is a primary driver for me, the guy who's been writing compilers for high performance computing (including Java) all my life. Ease-of-programming is a close second, but only after you've declared that performance is not your main goal. Hence I support syntactic sugar on expensive operations (e.g. accidental auto-boxing in a hot loop can cost you 10x in speed) but only if the type-system cannot remove it and you've not declared that you are programming for speed.
Modern: Statically typed. Functional programming with full-strength type inference - types are everywhere optional. Minimal syntax. REPL (i.e. incremental static typing) and separate compilation. Typed C-style macros: most syntax errors in dead code are typed.
My intent is a modern language that can be used where C or C++ is used: low-level high-performance work and a systems implementation language (e.g. OS's, device drivers, GC's), but bring in all the goodness of the last 20 years of language improvements.
The language is intended to "look like" C or Java, but with all types optional and all keywords removed. The whole no-keywords thing is an experiment I may back away from; a primary goal is to be readable - sometimes keywords feel like clutter and sometimes they are an code-anchor for your scanning eye.
I specifically intend to look at real-time constraints, and the notion of Time in a language.
Part of modern coding is the use of Garbage Collection, and the structured use of malloc/free - so I intend to add Rust-style memory management.
Part of modern coding is the use of multiple cores, so I intend to explore a couple of different concurrency models. A notion of a true 'final' field, made with a final-store (as opposed to declared as a final type) - you can make cyclic final structures, and once final all future reads will see the final value. No odd exceptions for "early publishing" a pointer.
And of course, this is a Work-In-Progress!!!!
GRAMMAR
BNF | Comment
--- | -------
prog = stmts END |
stmts= [tstmt or stmt][; stmts]*[;]? | multiple statments; final ';' is optional
tstmt= tvar = :type | type variable assignment
stmt = [id[:type] [:]=]* ifex | ids are (re-)assigned, and are available in later statements.
stmt = ^ifex | Early function exit
ifex = apply |
ifex = apply ? stmt | trinary logic; the else-clause will default to 0
ifex = apply ? stmt : stmt | trinary logic
apply = expr+ | Lisp-like application-as-adjacent, e.g. str 5
expr = term [binop term]* | gather all the binops and sort by prec
term = uniop term | Any number of uniops
term = id++ | post-inc/dec operators
term = id-- | post-inc/dec operators
term = tfact bop+ stmts bop- | Balanced/split operator arity-2, e.g. array lookup, ary [ idx ]
term = tfact bop+ stmts bop- stmt | Balanced/split operator arity-3, e.g. array assign, ary [ idx ]:= val
term = tfact post | A term is a tfact and some more stuff...
post = empty | A term can be just a plain 'tfact'
post = (tuple) post | Application argument list
post = .field post | Field and tuple lookup
post = .field [:]= stmt | Field (re)assignment. Plain '=' is a final assignment
post = .field++ OR .field-- | Field reassignment.
tfact= fact[:type] | Optionally typed fact
fact = id | variable lookup
fact = num | number
fact = "string" | string
fact = bop+ stmts bop- | Balanced/split operator, arity-1, e.g. array decl with size: [ 17 ]
fact = bop+ [stmts,[stmts,]*] bop- | Balanced/split operator, arity-N, e.g. array decl with elements: [ "Cliff", "John", "Lisa" ]
fact = (stmts) | General statements parsed recursively
fact = tuple | Tuple builder
fact = func | Anonymous function declaration
fact = @{ stmts } | Anonymous struct declaration; assignments declare fields
fact = {binop} | Special syntactic form of binop; no spaces allowed; returns function constant
fact = {uniop} | Special syntactic form of uniop; no spaces allowed; returns function constant
tuple= ( stmts,[stmts,] ) | Tuple; final comma is optional
binop= +-*%&/<>!= [] ]= | etc; primitive lookup; can determine infix binop at parse-time, also pipe but GFM screws up
uniop= -!~ [] | etc; primitive lookup; can determine infix uniop at parse-time
bop+ = [ | Balanced/split operator open
bop- = ] ]= ]:= | Balanced/split operator close
func = { [id[:type]* ->]? stmts } | Anonymous function declaration, if no args then the -> is optional
str = [.\%]* | String contents; \t\n\r% standard escapes
str = %[num]?[.num]?fact | Percent escape embeds a 'fact' in a string; "name=%name\n"
type = tcon OR tvar OR tfun[?] OR tstruct[?] OR ttuple[?] | // Types are a tcon or a tfun or a tstruct or a type variable. A trailing ? means 'nilable'
tcon = int, int[1,8,16,32,64], flt, flt[32,64], real, str[?] | Primitive types
tfun = { [[type]* ->]? type } | Function types mirror func decls
ttuple = ( [[type],]* ) | Tuple types are just a list of optional types; the count of commas dictates the length, zero commas is zero length. Tuple fields are always final.
tstruct = @{ [id [tfld];]* } | Struct types are field names with optional types or values.
tfld = ! : type ! = ifex | Fields are untyped or typed or final-assigned (with computed expression type)
tvar = id | Type variable lookup
SIMPLE EXAMPLES
Code | Comment
---- | -------
1 | 1:int
Prefix unary operator | ---
-1 | -1:int application of unary minus to a positive 1
!1 | 0:int convert 'truthy' to false
Infix binary operator | ---
1+2 | 3:int
1-2 | -1:int
1<=2 | 1:int Truth === 1
1>=2 | 0:int False === 0
Binary operators have precedence | ---
1+2*3 | 7:int standard precedence
1+2 * 3+4 *5 | 27:int
(1+2)*(3+4)*5 | 105:int parens overrides precedence
1// some comment<br>+2 | 3:int with bad comment
1 < 2 | 1:int true is 1, 1 is true
1 > 2 | 0:int false is 0, 0 is false
Float | ---
1.2+3.4 | 4.6:flt
1+2.3 | 3.3:flt standard auto-widening of int to flt
1.0==1 | 1:int True; int widened to float
Simple strings | ---
"Hello, world"| "Hello, world":str
str(3.14) | "3.14":str Overloaded str(:flt)
str(3) | "3":str Overloaded str(:int)
str("abc") | "abc":str Overloaded str(:str)
Variable lookup | ---
math_pi | 3.141592653589793:flt Should be math.pi but name spaces not implemented
primitive function lookup | ---
+ | "Syntax error; trailing junk" unadorned primitive not allowed
_+_ | {{+:{int int -> int}, +:{flt flt -> flt}} returns a union of '+' functions. The underscores indicate a binary op
{_+_} | Same as above
{!_} | !:{int -> int1} function taking an int and returning a bool. The under indicates a prefix unary op
Function application, traditional paren/comma args | ---
_+_(1,2) | 3:int
_-_(1,2) | -1:int binary version
-_(1) | -1:int unary version
Errors; mismatch arg count | ---
!() | Call to unary function '!', but missing the one required argument
math_pi(1) | A function is being called, but 3.141592653589793 is not a function type
_+_(1,2,3) | Passing 3 arguments to +{flt64 flt64 -> flt64} which takes 2 arguments
Arguments separated by commas and are full statements | ---
_+_(1, 2 * 3) | 7:int
_+_(1 + 2 * 3, 4 * 5 + 6) | 33:int
(1;2 ) | 2:int just parens around two statements
(1;2;) | 2:int final semicolon is optional
_+_(1;2 ,3) | 5:int full statements in arguments
Syntax for variable assignment | ---
x=1 | 1:int assignments have values
x:=1 | 1:int Same thing, not final
x=y=1 | 1:int stacked assignments ok
x=y= | Missing ifex after assignment of 'y'
x=1+y | Unknown ref 'y'
x=2; y=x+1; x*y | 6:int
1+(x=2*3)+x*x | 43:int Re-use ref immediately after def; parses as: x=(2*3); 1+x+x*x
x=(1+(x=2)+x) | Cannot re-assign ref 'x'
x=(1+(x:=2)+x) | 5:int RHS executes first, so parses as: x:=2; x=1+x+x
x++ | 0:int Define new variable as 0, and return it before the addition
x:=0; x++; x | 1:int Define new variable as 0, then add 1 to it, then return it
x++ + x-- | 1:int Can be used in expressions
Conditionals | ---
0 ? 2 : 3 | 3:int Zero is false
2 ? 2 : 3 | 2:int Any non-zero is true; "truthy"
math_rand(1)?(x=4):(x=3);x | :int8 x defined on both arms, so available after but range bound
math_rand(1)?(x=2): 3 ;4 | 4:int x-defined on 1 side only, but not used thereafter
math_rand(1)?(y=2;x=y*y):(x=3);x | :int8 x defined on both arms, so available after, while y is not
math_rand(1)?(x=2): 3 ;x | 'x' not defined on false arm of trinary No partial-defs
math_rand(1)?(x=2): 3 ;y=x+2;y | 'x' not defined on false arm of trinary More complicated partial-def
math_rand(1)?1 | :int1 Can skip last arm, will default to 0
x:=0; math_rand(1) ? x++; x | :int1 Side effects in the one arm
0 ? (x=2) : 3;x | `'x' not defined on false arm
Related Skills
node-connect
346.4kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
107.2kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
346.4kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
346.4kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
