@everseen/square v0.0.2
square
A tiny lisp-like language written in js, aims to be both fun and productive.
Q & A
What's this?
Square is a small language written in JS with a lisp-like grammer. The language's design goal is to use as few control keys like
<Ctrl>
,<Shift>
as possible, so the code structure is determined by.
,[]
,;
and/
. In addition, there are builtin supports for expanding vectors using...
, concat operator..
and other elegant simple syntax, which tends to be more productive.Square is parsed by a recursive descent algorithm, the source code is less than 1000 lines so it's easy to read and extend. Square also has an embedded JS runtime that provides 70~80% JS functionality.
Why called square?
Square use
[]
rather than()
which appears in most lisp languages, so it looks really "square".
Variable
[= a 'foo']
[= b 2]
[= c [.. 1 10]]
[= e 0b11.11]
[= f 0xff.ff]
[= [x] [1 2]]
[= [. x] [1 2 3]] ; x = 2
[= [... x] [1 2 3]] ; x = 3
[= [. [x] ... y] [1 [2] 3 4]] ; x = 2, y = 4
[= [. x . ... [y] ... [. z .]] [1, 2, 3, [4], [5, 6, 7]]] ; x = 2, y = 4, z = 6
Control flow
[match a
[[and [> q a] [< p a]] foo]
[[[regex '[a-z]+' 'gi'].test a] [bar]]]
[if true true]
[if true true false]
[begin
[= i 0]
[while [< i 10]
[console.log i]
[+= i 1]]]
Function
[= foo /[] 2]
[foo]
[= foo /[. z] [console.log [.. z 4]]
[foo 'ignored' 0]
[= fib /[n] [begin
[console.log n]
[match n
[[< n 0] 0]
[[< n 2] 1]
[+
[fib [- n 1]]
[fib [- n 2]]]]]]
[console.log [[.. 1 10].map /[x] [fib x]]]
[= cons /[x g] /[f] [f x g]]
[= car /[pair] [pair /[x .] x]]
[= cdr /[pair] [pair /[. g] [g]]]
[= ones [cons 1 /[] ones]]
[console.log [car ones]]
[console.log [car [cdr [cdr ones]]]]
Continuation
[= cc /[] [callcc /[cc] [cc cc]]]
[begin
[= start [cc]]
[console.log 'loop']
[start start]]
[= gen /[yield] [begin
[[.. 1 10].forEach /[x] [callcc /[cc] [yield [x cc]]]]]]
[begin
[= p [callcc /[outcc] [gen outcc]]]
[if [Array.isArray p]
[begin
[= [value incc] p]
[console.log value]
[incc]]]]
Structure
[= stack /[vec] [begin
[= this [Object]]
[= this.vec [vec.slice 0]]
[= this.clear /[] [= this.vec []]]
[= this.push /[x] [begin
[= this.vec [.. this.vec [x]]]]]
[= this.pop /[] [begin
[= [... x] this.vec]
[this.vec.splice [- this.vec.length 1] 1]
x]]
this]]
[= v [1 2 3]]
[= s [stack v]]
[= x [s.pop]]
[s.clear]
[s.push 1]
[s.push 0]
[= y [s.pop]]
[x y v] ; [3 0 [1 2 3]]
Comment
; 单行注释
; 行内注释 ;
Module
[= http [import 'http']] ; actually require('http')
[= squareModule [import 'module.sq']]
[[importDyn 'path'].then /[path] [path.resolve '.']] ; dynamic import
[export add /[a b] [+ a b]]
[= sub /[a b] [- a b]]
[export sub]
Example
[= http [import'http']]
[= path [import 'path']]
[= fs [import 'fs']]
[= self [path.resolve 'examples/2.sq']]
[= stream [fs.createReadStream self]]
; line comment
; line comment
[= app /[req res] [begin
[console.log ; inline comment ; req.url]
[stream.on 'end' /[] [res.end]]
[stream.pipe res]]]
[= server [http.createServer app]]
[setTimeout /[] [begin
[console.warn '---- NOW TURNING OFF SERVER ----']
[server.close]]
4000]
[server.listen 8080 /[] [console.log '---- SERVER LISTENING ON 8080 ----']]
BNF
<lit> ::= <num> | <str> | <bool>
<unOp> ::= '!' | '-'
<binOp> ::= '-' | '..' | '/' | '+' | '*' | '>' | '<' | '%' | '^' | '==' | '!=' | ...
<dot> ::= '.' <id> <dot>
<expandItem> ::= '.' | '...' | <id> | <expand>
<expand> ::= '[' <expandItem>+ ']'
<func> ::= '/' (<expand> | '['']') <expr>
<unOpExpr> ::= <unOp> <expr>
<assign> ::= '[' '=' (<id> <dot>* | <expand>) <expr> ']'
<binOpExpr> ::= '[' <binOp> <expr> <expr> ']'
<call> ::= '[' <expr>* ']'
<expr> ::= (<id> | <lit> | <func> | <assign> | <binOpExpr> | <unOpExpr> | <call>) <dot>*