Re: Lark - A surface syntax for Common Lisp
"Gerry Weaver (as gerryw at compvia dot com)" <[email protected]>
| Newsgroups | gmane.lisp.lispworks.general |
|---|---|
| Message-ID | <[email protected]> |
Hi David, You have a valid point. I don't have the luxury of using Lisp most of the time, so maybe it doesn't impact me as much. However, I will definitely take a look at that. Thanks for the feedback. It is appreciated. On 2/14/26 18:42, David McClain wrote: > 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