Re: Lark - A surface syntax for Common Lisp
"David McClain (as dbm at refined-audiometrics dot com)" <[email protected]>
| Newsgroups | gmane.lisp.lispworks.general |
|---|---|
| Message-ID | <[email protected]> |
Heh… Those were supposed to be closing parens… but being dyslexic, I constantly get those things confused. Just all the more awful to have a multiplicity of closings. > On Feb 14, 2026, at 17:39, David McClain <[email protected]> wrote: > > Wow! I really admire your tenacity. > > But honestly, I cannot imagine anything more awful than trying to remember how to close off compound expressions. Was that {([ or {[( ? > > I had to write Rust for a year, and faced that dilemma constantly. Why would I ever wish to have this same handicap in my Lisp? > > > >> On Feb 14, 2026, at 12:39, Gerry Weaver (as gerryw at compvia dot com) <[email protected]> wrote: >> >> Hello All, >> >> I've been working on this thing for a while. I'm getting close to the >> point where I'm going to call it good for now. I'm mainly working on the >> stdlib stuff (ie; http server, http client, websockets, etc.). It isn't >> meant to replace Lisp. It is intended to be an ergonomic extension. >> There is a lark command that is similar to that provided by the go >> language go command and provides most of the same functionality. There >> is also a syntax file for Sublime Text. I'm very interested in any >> thoughts or suggestions y'all might have. If enough folks are >> interested, I will try to put it on github at some point. >> >> Here is a rough description of the syntax. It is still evolving a >> little, but mostly stable. >> >> Thanks, >> >> -G >> >> >> >> -- Lark is a readable surface syntax for Common Lisp. >> -- Everything here compiles to standard CL s-expressions. >> >> >> -- ═══════════════════════════════════════════════════════════ >> -- COMMENTS >> -- ═══════════════════════════════════════════════════════════ >> >> -- Single line comment (double dash) >> >> >> -- ═══════════════════════════════════════════════════════════ >> -- VARIABLES >> -- ═══════════════════════════════════════════════════════════ >> >> let x = 42 -- lexical binding (let) >> let* y = x + 1 -- sequential binding (let*) >> let name = "Lark" -- strings >> let pi = 3.14159 -- floats >> let ratio = 2/3 -- rationals >> let hex = 0xFF -- hex literals >> let nothing = nil -- nil >> let yes = true -- boolean true (t) >> let no = false -- boolean false (nil) >> let ch = #\A -- character literal >> let kw = :hello -- keyword symbol >> >> -- Assignment (mutation) >> x := 100 -- setf >> >> -- Destructuring multiple return values >> let (q, r) = floor(17, 5) -- multiple-value-bind >> >> >> -- ═══════════════════════════════════════════════════════════ >> -- FUNCTIONS >> -- ═══════════════════════════════════════════════════════════ >> >> -- Expression form (single expression body) >> fn square(x) = x * x >> >> -- Block form (multiple statements) >> fn greet(name) >> let msg = concat("Hello, ", name, "!") >> print(msg) >> msg >> end >> >> -- With docstring >> fn factorial(n) >> "Compute n! recursively." >> if n <= 1 then 1 else n * factorial(n - 1) end >> end >> >> -- Optional parameters >> fn connect(host, port = 8080) = print("%s:%d", host, port) >> >> -- Keyword parameters >> fn makeUser(name:, age: 0, role: "guest") >> hash(name: name, age: age, role: role) >> end >> -- Call with keywords: makeUser(name: "Gerry", age: 30) >> >> -- Rest parameters >> fn logAll(...args) = forEach(args, |a| print(a)) >> >> -- CL lambda-list keywords also work >> fn withRest(a, b, &rest others) = list(a, b, others) >> >> -- Return values >> fn safeDivide(a, b) >> if b == 0 then return nil end >> a / b >> end >> >> -- Multiple return values >> fn divmod(a, b) >> return (floor(a, b)) -- returns quotient and remainder >> end >> >> >> -- ═══════════════════════════════════════════════════════════ >> -- CLOSURES (LAMBDAS) >> -- ═══════════════════════════════════════════════════════════ >> >> -- Single expression >> let double = |x| x * 2 >> >> -- Multi-statement >> let counter = |start| do >> let n = start >> n := n + 1 >> n >> end >> >> -- No parameters >> let greetWorld = || print("Hello, World!") >> >> -- Function references (like CL #'name) >> let f = &square -- #'square >> map([1, 2, 3], &square) -- (mapcar #'square '(1 2 3)) >> >> >> -- ═══════════════════════════════════════════════════════════ >> -- DATA STRUCTURES >> -- ═══════════════════════════════════════════════════════════ >> >> -- Lists >> let nums = [1, 2, 3, 4, 5] >> let empty = [] >> >> -- Vectors >> let vec = #(10, 20, 30) >> >> -- Hash tables (dictionaries) >> let config = {host: "localhost", port: 8080, debug: true} >> let empty = {} >> >> -- Access >> config["host"] -- lark-ref (string key) >> nums[0] -- lark-ref (index) >> >> -- Dot access (method-style) >> config.host -- not valid for hashes, use [] >> nums.length -- (length nums) >> >> -- Quoted symbols and lists >> let sym = 'hello -- (quote hello) >> let quoted = '(1, 2, 3) -- (quote (1 2 3)) >> >> >> -- ═══════════════════════════════════════════════════════════ >> -- OPERATORS >> -- ═══════════════════════════════════════════════════════════ >> >> -- Arithmetic >> 1 + 2 -- addition >> 10 - 3 -- subtraction >> 4 * 5 -- multiplication >> 10 / 3 -- division (CL exact: 10/3) >> 17 mod 5 -- modulo >> 17 rem 5 -- remainder >> -x -- negation >> >> -- Comparison >> x == y -- equal >> x != y -- not equal >> x === y -- eq (identity) >> x !== y -- not eq >> x < y -- less than >> x > y -- greater than >> x <= y -- less or equal >> x >= y -- greater or equal >> >> -- Logical >> x and y -- short-circuit and >> x or y -- short-circuit or >> not x -- negation >> >> -- Pipeline (threading) >> "hello world" |> upper() |> split(" ") >> -- → (split (upper "hello world") " ") >> >> -- Data flows left to right: >> [1, 2, 3, 4, 5] >> |> filter(|x| x > 2) >> |> map(|x| x * 10) >> |> reduce(0, |acc, x| acc + x) >> >> >> -- ═══════════════════════════════════════════════════════════ >> -- STRINGS >> -- ═══════════════════════════════════════════════════════════ >> >> let s = "hello" >> let interp = "x is ${x} and 2+2 is ${2 + 2}" -- string interpolation >> let raw = "line1\nline2\ttab" -- escape sequences >> >> -- Triple-quoted strings (no escaping needed) >> let html = """ >> <html> >> <body><h1>Hello</h1></body> >> </html> >> """ >> >> let json = """ >> {"host": "localhost", "port": 8080, "debug": true} >> """ >> >> let regex = """\d+\.\d+""" -- backslashes are literal >> >> -- Triple-quoted with interpolation >> let page = """ >> <html> >> <title>${title}</title> >> <body>${content}</body> >> </html> >> """ >> >> -- String operations >> split("a,b,c", ",") -- ["a", "b", "c"] >> join(["a", "b"], "-") -- "a-b" >> trim(" hi ") -- "hi" >> upper("hello") -- "HELLO" >> lower("HELLO") -- "hello" >> replace("hello", "l", "r") -- "herlo" (first) >> replaceAll("hello", "l", "r") -- "herro" (all) >> startsWith?("hello", "he") -- true >> endsWith?("hello", "lo") -- true >> contains?("hello", "ell") -- true (works on strings, lists, hashes) >> indexOf("hello", "ll") -- 2 >> substr("hello", 1, 3) -- "el" >> concat("a", "b", "c") -- "abc" >> repeat("ha", 3) -- "hahaha" >> lines("a\nb\nc") -- ["a", "b", "c"] >> words("hello world") -- ["hello", "world"] >> chars("abc") -- [#\a, #\b, #\c] >> padLeft("42", 5) -- " 42" >> padRight("hi", 5) -- "hi " >> len("hello") -- 5 >> >> >> -- ═══════════════════════════════════════════════════════════ >> -- COLLECTIONS >> -- ═══════════════════════════════════════════════════════════ >> >> -- Functional operations >> map([1, 2, 3], |x| x * 2) -- [2, 4, 6] >> filter([1, 2, 3, 4], |x| x > 2) -- [3, 4] >> reject([1, 2, 3, 4], |x| x > 2) -- [1, 2] >> reduce([1, 2, 3], 0, |a, b| a + b) -- 6 >> find([1, 2, 3], |x| x > 1) -- 2 >> any?([1, 2, 3], |x| x > 2) -- true >> all?([1, 2, 3], |x| x > 0) -- true >> sort([3, 1, 2], |a, b| a < b) -- [1, 2, 3] >> forEach([1, 2, 3], |x| print(x)) -- side effects >> >> -- Container operations >> len([1, 2, 3]) -- 3 >> size({a: 1, b: 2}) -- 2 >> keys({a: 1, b: 2}) -- ["a", "b"] >> values({a: 1, b: 2}) -- [1, 2] >> has?({a: 1}, "a") -- true >> pairs({a: 1, b: 2}) -- [("a" . 1), ("b" . 2)] >> merge({a: 1}, {b: 2}) -- {a: 1, b: 2} >> contains?([1, 2, 3], 2) -- true >> flatten([[1, 2], [3, [4]]]) -- [1, 2, 3, 4] >> unique([1, 2, 2, 3]) -- [1, 2, 3] >> >> -- Mutation >> let lst = [1, 2, 3] >> push!(lst, 4) -- lst is now (4 1 2 3) >> pop!(lst) -- returns 4 >> reverse!(lst) -- in-place reverse >> let h = {a: 1, b: 2} >> delete!(h, "a") -- remove key >> >> -- Slicing >> take([1, 2, 3, 4], 2) -- [1, 2] >> drop([1, 2, 3, 4], 2) -- [3, 4] >> zip([1, 2], ["a", "b"]) -- [(1 "a"), (2 "b")] >> range(5) -- [0, 1, 2, 3, 4] >> range(1, 5) -- [1, 2, 3, 4] >> range(0, 10, 2) -- [0, 2, 4, 6, 8] >> enumerate(["a", "b"]) -- [(0 . "a"), (1 . "b")] >> >> >> -- ═══════════════════════════════════════════════════════════ >> -- CONTROL FLOW >> -- ═══════════════════════════════════════════════════════════ >> >> -- If/elseif/else (expression — returns value) >> let label = if x > 100 then "big" >> elseif x > 10 then "medium" >> else "small" >> end >> >> -- If/then (statement) >> if ready then >> process() >> end >> >> -- Match (pattern matching) >> fn describe(val) >> match val with >> | 0 -> "zero" >> | 1 -> "one" >> | n when n < 0 -> "negative" >> | _ -> "other" >> end >> end >> >> -- Type matching >> fn handle(msg) >> match msg with >> | _: String -> print("string: %s", msg) >> | _: Integer -> print("integer: %d", msg) >> | _ -> print("unknown") >> end >> end >> >> -- Named bindings in match >> match result with >> | x when x > 0 -> print("positive: %d", x) >> | x -> print("non-positive: %d", x) >> end >> >> >> -- ═══════════════════════════════════════════════════════════ >> -- LOOPS >> -- ═══════════════════════════════════════════════════════════ >> >> -- For-in (iteration) >> for item in [1, 2, 3] do >> print(item) >> end >> >> -- For-range (numeric) >> for i = 0, 10 do >> print(i) >> end >> >> -- For-range with step >> for i = 0, 100, 5 do >> print(i) >> end >> >> -- While loop >> let n = 10 >> while n > 0 do >> print(n) >> n := n - 1 >> end >> >> -- Loop (CL loop pass-through) >> loop >> for i from 1 to 10 >> when i mod 2 == 0 >> collect i >> end >> >> -- For-collect (loop comprehension) >> for x in range(10) when x > 5 collect x * 2 >> >> -- For-sum >> for x in [1, 2, 3, 4] sum x * x >> >> -- List comprehension (bracket syntax) >> [x * 2 for x in range(10)] >> [x * 2 for x in range(10) if x > 5] >> >> -- Comprehension with destructuring >> [concat(k, "=", v) for (k, v) in pairs(config)] >> >> >> -- ═══════════════════════════════════════════════════════════ >> -- ERROR HANDLING >> -- ═══════════════════════════════════════════════════════════ >> >> -- Try/on/ensure (→ handler-case + unwind-protect) >> try >> riskyOperation() >> on Error e do >> print("caught: %s", e) >> on TypeError e do >> print("type error: %s", e) >> ensure >> cleanup() >> end >> >> >> -- ═══════════════════════════════════════════════════════════ >> -- LOCAL FUNCTIONS >> -- ═══════════════════════════════════════════════════════════ >> >> -- Where block (→ labels, mutually recursive) >> where >> fn isEven(n) = n == 0 or isOdd(n - 1) >> fn isOdd(n) = n != 0 and isEven(n - 1) >> in >> isEven(42) >> end >> >> -- Flet block (→ flet, non-recursive) >> flet >> fn double(x) = x * 2 >> fn triple(x) = x * 3 >> in >> double(5) + triple(5) >> end >> >> >> -- ═══════════════════════════════════════════════════════════ >> -- CLASSES AND METHODS >> -- ═══════════════════════════════════════════════════════════ >> >> -- Class definition (→ defclass) >> class Point(x: 0, y: 0) >> >> -- Inheritance >> class Point3D(z: 0) extends Point >> >> -- Struct definition (→ defstruct: faster, typed, no CLOS overhead) >> struct PacketHeader(version: 0, length: 0, type: :tcp) >> >> -- Struct with inheritance (single only) >> struct ExtHeader(extra: nil) extends PacketHeader >> >> -- Struct constructor: makeStructName(slot: value, ...) >> -- let pkt = makePacketHeader(version: 1, type: :udp) >> >> -- Struct predicate: structName?(x) >> -- packetHeader?(pkt) → true >> >> -- Slot access works the same as class: pkt.version, pkt.type >> >> -- Methods (typed params → defmethod) >> fn describe(p: Point) = format(nil, "(%d, %d)", p.x, p.y) >> >> -- Generic declaration >> generic area(shape) >> >> -- Method qualifiers via annotations >> @before >> fn validate(p: Point) >> print("about to use point") >> end >> >> @after >> fn logUsage(p: Point) >> print("point was used") >> end >> >> >> -- ═══════════════════════════════════════════════════════════ >> -- MACROS >> -- ═══════════════════════════════════════════════════════════ >> >> -- Macro definition >> macro unless(test, &body body) >> `(if (not ,test) (progn ,@body)) >> end >> >> -- Backtick templates work for quasiquoting >> macro myIf(test, consequent, alternate) >> `(cond (,test ,consequent) (t ,alternate)) >> end >> >> >> -- ═══════════════════════════════════════════════════════════ >> -- PACKAGES >> -- ═══════════════════════════════════════════════════════════ >> >> package MyLib >> use: [:cl] >> export: [:myFunc, :myVar] >> end >> >> >> -- ═══════════════════════════════════════════════════════════ >> -- ANNOTATIONS >> -- ═══════════════════════════════════════════════════════════ >> >> -- @inline — declare function inline >> @inline >> fn fastAdd(a, b) = a + b >> >> -- @const — compile-time constant >> @const let maxSize = 1024 >> >> -- @type, @optimize, @ignore — CL declarations >> -- Used inside function bodies for type hints >> -- @type fixnum x >> -- @optimize (speed 3) (safety 0) >> -- Or use s-expression escape for complex declarations: >> @(declaim (optimize (speed 3) (safety 0))) >> >> -- @when/@unless — conditional compilation (feature flags) >> @when(:sbcl) >> fn sbclOnly() = print("SBCL specific") >> >> >> -- ═══════════════════════════════════════════════════════════ >> -- TYPE DEFINITIONS >> -- ═══════════════════════════════════════════════════════════ >> >> -- Type aliases (→ deftype) >> type Byte = @(unsigned-byte 8) >> type Index = @(integer 0 #.most-positive-fixnum) >> >> >> -- ═══════════════════════════════════════════════════════════ >> -- PRINTING AND FORMAT >> -- ═══════════════════════════════════════════════════════════ >> >> -- print: adds newline >> print("hello") -- simple >> print("x = %d", x) -- printf-style: %s %d %f %x %o %b %e %g >> print(x, y, z) -- multiple values (space-separated) >> >> -- printn: no newline >> printn("waiting...") >> >> -- format: CL format with printf syntax >> let s = format(nil, "%.2f", 3.14159) -- returns string >> format(t, "output: %s\n", result) -- prints to stdout >> >> >> -- ═══════════════════════════════════════════════════════════ >> -- INTEROP WITH COMMON LISP >> -- ═══════════════════════════════════════════════════════════ >> >> -- S-expression escape: @(raw CL code) >> @(defvar *global* 42) >> @(declaim (optimize (speed 3))) >> >> -- Backtick quasiquote >> let form = `(+ 1 2 3) >> >> -- All CL functions available directly >> let result = mapcar(&car, '((1, 2), (3, 4))) >> >> -- Name translation: camelCase → kebab-case >> -- hashTable → hash-table >> -- processOrders → process-orders >> -- empty? → emptyp (? → p) >> -- reverse! → nreverse (! → f) >> -- HTMLParser → html-parser >> >> >> -- ═══════════════════════════════════════════════════════════ >> -- FFI (FOREIGN FUNCTION INTERFACE) >> -- ═══════════════════════════════════════════════════════════ >> >> -- C library bindings (requires CFFI) >> -- clib "libm" >> -- fn sqrt(x: double) -> double >> -- fn pow(base: double, exp: double) -> double >> -- end >> >> -- C struct definitions >> -- cstruct Timeval >> -- sec: long >> -- usec: long >> -- end >> >> -- FFI types: int, uint, int8-64, uint8-64, float, double, >> -- ptr, cstr, void, bool, size, ssize >> >> -- FFI operations: >> -- alloc(type), cfree(ptr), nullPtr(), nullPtr?(p) >> -- sizeOf(type), memRef(ptr, type), memSet(ptr, type, val) >> -- cStr(s), fromCStr(ptr), slot(ptr, type, name) >> -- ptrInc(ptr, offset), ptrAddress(ptr), makePtr(addr) >> >> >> -- ═══════════════════════════════════════════════════════════ >> -- SCRIPTING / EMBEDDING API >> -- ═══════════════════════════════════════════════════════════ >> >> -- Create an isolated scripting environment >> -- let env = larkEnv("my-scripts") >> >> -- Execute Lark code in the environment >> -- larkRun(env, "let x = 42") >> -- larkRunFile(env, "scripts/handlers.lk") >> >> -- Exchange data between host and scripts >> -- let val = larkGet(env, "x") >> -- larkSet(env, "config", myConfig) >> >> -- Expose host functions to scripts >> -- larkExpose(env, "notify", |msg| sendSlack(msg)) >> >> -- Call script functions from host >> -- larkCall(env, "onEvent", eventData) >> >> -- Hot reload >> -- larkReload(env) >> >> -- Quick one-shot (no environment) >> -- larkEval("2 + 2") >> -- larkEvalFile("script.lk") >> >> -- Cleanup >> -- larkDestroy(env) >> >> >> >> _______________________________________________ >> Lisp Hug - the mailing list for LispWorks users >> [email protected] >> http://www.lispworks.com/support/lisp-hug.html > _______________________________________________ Lisp Hug - the mailing list for LispWorks users [email protected] http://www.lispworks.com/support/lisp-hug.html