[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-70504":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":13,"openIssues":13,"contributorsCount":14,"subscribersCount":14,"size":14,"stars1d":13,"stars7d":13,"stars30d":15,"stars90d":14,"forks30d":14,"starsTrendScore":16,"compositeScore":17,"rankGlobal":10,"rankLanguage":10,"license":10,"archived":18,"fork":18,"defaultBranch":19,"hasWiki":20,"hasPages":18,"topics":21,"createdAt":10,"pushedAt":10,"updatedAt":22,"readmeContent":23,"aiSummary":24,"trendingCount":14,"starSnapshotCount":14,"syncStatus":25,"lastSyncTime":26,"discoverSource":27},70504,"rust-but-lisp","ThatXliner\u002Frust-but-lisp","ThatXliner","Rust but LISP","",null,"Rust",148,1,0,19,3,0.9,false,"main",true,[],"2026-06-12 02:02:34","# rlisp\n\n> **Hello, Hacker News.** You're not wrong. This is a weekend project, not a production compiler — some Rust syntax is missing (turbofish is fixed now, lifetime bounds are on the list). The point isn't completeness; it's exploring what happens when you bolt Lisp macros onto Rust semantics. If that sounds interesting, read on. If you're looking for something to be mad about, [the issue tracker is open](https:\u002F\u002Fgithub.com\u002FThatXliner\u002Frust-but-lisp\u002Fissues).\n\nRust semantics in LISP syntax. Write s-expressions, output Rust source: `(s-expr → .rs → binary)`.\n\n```lisp\n(struct Point\n  (x f64)\n  (y f64))\n\n(impl Point\n  (fn distance ((&self) (other &Point)) f64\n    (let dx (- (. self x) (. other x)))\n    (let dy (- (. self y) (. other y)))\n    (. (+ (. dx powf 2.0) (. dy powf 2.0)) sqrt)))\n\n(fn main () ()\n  (let p1 (raw_new Point (x 0.0) (y 0.0)))\n  (let p2 (raw_new Point (x 3.0) (y 4.0)))\n  (println! \"Distance: {}\" (. p1 distance (& p2))))\n```\n\nOwnership, borrowing, lifetimes, generics, traits, pattern matching — all expressed as s-expressions. `rustc` still does type checking, borrow checking, and optimization. rlisp just handles the syntax.\n\n![Build demo](assets\u002Fsuccess_demo.gif)\n\nPretty diagnostics with [Ariadne](https:\u002F\u002Fcodeberg.org\u002Fzesterer\u002Fariadne):\n\n![Parse error demo](assets\u002Fparse_error_demo.gif)\n\n## Install\n\n```bash\ngit clone https:\u002F\u002Fgithub.com\u002FThatXliner\u002Frust-but-lisp.git\ncd rlisp\ncargo install --path .\n```\n\n## Usage\n\n```bash\nrlisp compile file.lisp   # transpile to file.rs\nrlisp build file.lisp     # transpile and compile with rustc\nrlisp run file.lisp       # transpile, compile, and run\n```\n\n## Quick reference\n\n| LISP | Rust |\n|------|------|\n| `(fn add ((x i32) (y i32)) i32 (+ x y))` | `fn add(x: i32, y: i32) -> i32 { (x + y) }` |\n| `(let x 42)` \u002F `(let mut x 42)` | `let x = 42;` \u002F `let mut x = 42;` |\n| `(struct Point (x f64) (y f64))` | `struct Point { x: f64, y: f64 }` |\n| `(enum Option (generic T) (Some T) None)` | `enum Option\u003CT> { Some(T), None }` |\n| `(match val ((Some x) (handle x)) (None ()))` | `match val { Some(x) => { handle(x) }, None => { } }` |\n| `(if (> x 0) (println! \"yes\") (println! \"no\"))` | `if (x > 0) { println!(\"yes\") } else { println!(\"no\") }` |\n| `(impl Point ((fn new (...) ...)))` | `impl Point { fn new(...) ... }` |\n| `(trait Display ((fn fmt (...) Result)))` | `trait Display { fn fmt(...) -> Result; }` |\n| `(. obj field)` \u002F `(. obj method arg)` | `obj.field` \u002F `obj.method(arg)` |\n| `(raw_new Point (x 1.0) (y 2.0))` | `Point { x: 1.0, y: 2.0 }` |\n| `(lambda (x y) (+ x y))` | `\\|x, y\\| { x + y }` |\n| `(for x in iter (body))` \u002F `(loop (body))` \u002F `(while cond (body))` | `for x in iter { body }` \u002F `loop { body }` \u002F `while cond { body }` |\n| `(pub fn foo () i32 42)` | `pub fn foo() -> i32 { 42 }` |\n\nBinary operators (`+`, `-`, `*`, `\u002F`, `==`, etc.) emit infix: `(+ a b)` → `(a + b)`, `(+ a b c)` → `(a + b + c)`.\n\nKebab-case identifiers with hyphens are converted to `__` (double underscore): `page-header` → `page__header`. Collisions (e.g. `foo-bar` and `foo__bar` both → `foo__bar`) emit a warning.\n\n**Full reference:** [SYNTAX.md](SYNTAX.md) covers generics, lifetimes, visibility, modules, turbofish, inline Rust, const\u002Fstatic, if-let, while-let, else-if, match guards, derive, where clauses, supertraits, associated types, type aliases, and more.\n\n## Macros\n\nrlisp macros are compile-time s-expression transformers — no `proc_macro` crate, no token streaming, no syn\u002Fquote. A macro is just a function from s-expressions to s-expressions.\n\nMacro bodies use three special forms borrowed from LISP:\n\n| Form | Meaning |\n|------|---------|\n| `(quasiquote template)` | \"Quote this template, but allow unquotes inside\" — like a tagged template literal |\n| `(unquote name)` | \"Insert the value of `name` here\" — a hole in the template |\n| `(unquote-splicing name)` | \"Splice the list `name` into the surrounding list\" — for inserting multiple forms |\n\nThink of `quasiquote` as \"return this exact s-expression, except for the `unquote` holes.\" Without it, you'd have to manually construct every parenthesis with `list` and `cons`.\n\n```lisp\n;; Define a when macro: (when condition body...)\n(defmacro when (condition &rest body)\n  (quasiquote (if (unquote condition) (do (unquote-splicing body)))))\n\n;; Macro expansion:\n;;   (when (> x 10) (print \"big\") (print \"huge\"))\n;; → (if (> x 10) (do (print \"big\") (print \"huge\")))\n;; → if x > 10 { print(\"big\"); print(\"huge\") }\n\n(defmacro double (x)\n  (quasiquote (+ (unquote x) (unquote x))))\n\n;; (double 21) → (+ 21 21) → 21 + 21\n\n(fn main () ()\n  (let x 21)\n  (println! \"Double: {}\" (double x))\n  (when (> x 10)\n    (println! \"x is greater than 10\")\n    (println! \"this too\")))\n```\n\n`&rest` captures all remaining arguments into a list, and `unquote-splicing` flattens that list into the surrounding form. This is how variadic macros work.\n\n## Loops\n\n```lisp\n(while (> x 0)\n  (println! \"{}\" x)\n  (-= x 1))\n\n(loop (println! \"tick\"))\n\n(for x in 0..10\n  (println! \"{}\" x))\n\n;; for with destructuring (double parens for tuple patterns)\n(for ((i val)) in (. v iter) enumerate\n  (println! \"{}: {}\" i val))\n```\n\n## Closures\n\n```lisp\n;; untyped\n(let add (lambda (x y) (+ x y)))\n\n;; typed with return type\n(let mul (lambda ((x i32) (y i32)) i32 (* x y)))\n\n;; move closure\n(let s \"hello\")\n(let greet (lambda move () (println! \"{}\" s)))\n```\n\n## Modules, visibility, and imports\n\n```lisp\n(pub fn public_api () i32 42)\n(pub (crate) fn internal () i32 0)       ;; pub(crate)\n(pub (super) fn parent_visible () i32 1)  ;; pub(super)\n\n(pub struct Config\n  (pub host String)                        ;; public field\n  (port u16))                              ;; private field\n\n(pub mod utils (                          ;; inline module\n  (pub fn helper () i32 1)\n  (fn private () i32 0)))\n\n(mod external_lib)                        ;; external module decl\n\n(use std::collections::HashMap)\n(use std::io::{self,Write,Read})\n(use std::fmt::Display as Fmt)\n```\n\n## Inline Rust\n\nDrop into raw Rust with `(rust \"...\")` for anything rlisp doesn't express natively.\nThe string is emitted verbatim into the generated `.rs` file (with LISP escape sequences unescaped):\n\n```lisp\n(fn raw_example () i32\n  (rust \"let x: i32 = 42; x * 2\"))\n\n(fn main () ()\n  (rust \"let message: &str = \\\"from raw Rust\\\";\")\n  (println! \"{}\" (rust \"message\")))\n```\n\n## Lifetimes, turbofish, and control flow\n\n```lisp\n;; Lifetime annotations on function definitions\n(fn longest (generic 'a) ((x &'a str) (y &'a str)) (&'a str)\n  (if (> (. x len) (. y len)) x y))\n\n;; Turbofish via :: special form\n(let nums ((:: (. (0..10) collect) Vec\u003Ci32>)))\n\n;; Break, continue, return\n(for x in 0..10\n  (if (== x 5) (break))\n  (if (== x 3) (continue))\n  (println! \"{}\" x))\n\n;; Type casts\n(let pi 3.14159)\n(let approx (as pi u32))\n\n;; if-let and while-let\n(if-let (Some v) (. map get key)\n  (println! \"found: {}\" v)\n  (println! \"missing\"))\n\n(while-let (Some v) ((. iter next))\n  (println! \"{}\" v))\n\n;; Unsafe blocks\n(unsafe\n  (rust \"let ptr: *const i32 = &42;\")\n  (rust \"*ptr\"))\n```\n\n## Why\n\nMostly for fun. I wanted to see what Rust feels like with the syntax stripped away but the type system and borrow checker still there.\n\n**Macros** are the obvious practical win: in LISP they're just functions that return s-expressions, running at compile time. No proc_macro, no token streaming. You write `defmacro`, you get quasiquote, you're done.\n\n**Structural editing** is another thing you don't appreciate until you try it. S-expressions are trivially balanced. You can't accidentally leave a brace dangling.\n\nAnd **the uniformity** grows on you. Expressions, types, patterns, statements — they all look the same. A function signature uses the same syntax as a match arm. It's less to keep in your head.\n\n## Support\n\nHey, if you like this project and read this far, consider starring it on GitHub! \n\nAdditionally, please check out some of my more serious projects, namely [Xclif](https:\u002F\u002Fgithub.com\u002FThatXliner\u002Fxclif) (file-based routing CLI framework), and [Quillium](https:\u002F\u002Fquillium.bryanhu.com?utm_source=github&utm_medium=readme&utm_id=rust-but-lisp) (inline branching for prose; think Git for writing)\n\n## License\n\nMIT\n","该项目提供了一种使用LISP语法编写Rust代码的方式。它允许开发者通过s-表达式来定义结构体、实现方法、函数等，最终输出标准的Rust源代码，并可通过`rustc`编译执行。rlisp支持Rust的所有核心特性，如所有权、借用、生命周期、泛型和模式匹配等，均以LISP风格的s-表达式形式呈现。此工具适合对探索不同编程范式感兴趣的研究者或希望尝试新编码方式的Rust开发者，在不影响类型检查、借用规则及优化的前提下，为习惯LISP语法的程序员提供了另一种编写高效安全Rust程序的选择。",2,"2026-06-11 03:32:32","CREATED_QUERY"]