{"id":44383412,"url":"https://github.com/helgesverre/sema","last_synced_at":"2026-02-17T01:00:58.282Z","repository":{"id":337905597,"uuid":"1155642124","full_name":"HelgeSverre/sema","owner":"HelgeSverre","description":"A Lisp with first-class LLM primitives, implemented in Rust","archived":false,"fork":false,"pushed_at":"2026-02-14T20:14:14.000Z","size":4300,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-02-14T21:44:16.644Z","etag":null,"topics":["ai","anthropic","interpreter","lisp","llm","openai","programming-language","rust","scheme"],"latest_commit_sha":null,"homepage":"https://sema-lisp.vercel.app/","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/HelgeSverre.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-02-11T18:32:52.000Z","updated_at":"2026-02-14T20:14:18.000Z","dependencies_parsed_at":"2026-02-12T07:04:02.542Z","dependency_job_id":"6c38f66f-4ec5-4d5f-9a89-54beddc2c916","html_url":"https://github.com/HelgeSverre/sema","commit_stats":null,"previous_names":["helgesverre/sema"],"tags_count":17,"template":false,"template_full_name":null,"purl":"pkg:github/HelgeSverre/sema","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HelgeSverre%2Fsema","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HelgeSverre%2Fsema/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HelgeSverre%2Fsema/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HelgeSverre%2Fsema/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/HelgeSverre","download_url":"https://codeload.github.com/HelgeSverre/sema/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HelgeSverre%2Fsema/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29528226,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-17T00:57:22.232Z","status":"ssl_error","status_checked_at":"2026-02-17T00:54:25.811Z","response_time":115,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai","anthropic","interpreter","lisp","llm","openai","programming-language","rust","scheme"],"created_at":"2026-02-12T00:13:51.966Z","updated_at":"2026-02-17T01:00:58.253Z","avatar_url":"https://github.com/HelgeSverre.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"assets/logo.png\" alt=\"Sema\" width=\"640\"\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  A Lisp with first-class LLM primitives, implemented in Rust.\n\u003c/p\u003e\n\nSema is a Scheme-like Lisp where prompts are s-expressions, conversations are persistent data structures, and LLM calls are just another form of evaluation. It combines a Scheme core with Clojure-style keywords (`:foo`), map literals (`{:key val}`), and vector literals (`[1 2 3]`).\n\n## Installation\n\n```bash\ncargo install --git https://github.com/HelgeSverre/sema sema\n```\n\nOr build from source:\n\n```bash\ngit clone https://github.com/HelgeSverre/sema\ncd sema\ncargo build --release\n# Binary is at target/release/sema\n```\n\n## Quick Start\n\n```bash\nsema                          # Start the REPL\nsema script.sema              # Run a file\nsema -e '(+ 1 2)'             # Evaluate an expression\nsema -p '(map sqr (range 5))' # Evaluate and always print\n```\n\n```scheme\n;; In the REPL:\nsema\u003e (define (greet name) (format \"Hello, ~a!\" name))\nsema\u003e (greet \"world\")\n\"Hello, world!\"\n\nsema\u003e (map (lambda (x) (* x x)) (range 1 6))\n(1 4 9 16 25)\n\nsema\u003e (define person {:name \"Ada\" :age 36})\nsema\u003e (:name person)\n\"Ada\"\n```\n\n## Why Sema?\n\n- **LLMs as language primitives** — prompts, messages, conversations, tools, and agents are first-class data types, not string templates bolted on\n- **Multi-provider** — Anthropic, OpenAI, Gemini, Groq, xAI, Mistral, Ollama, and more, all auto-configured from environment variables\n- **Practical Lisp** — closures, tail-call optimization, macros, modules, error handling, HTTP, file I/O, regex, JSON — everything you need to build real programs\n- **Embeddable** — clean Rust crate structure, builder API, sync interface\n\n### Why Not Sema?\n\n- No full numeric tower (rationals, bignums, complex numbers)\n- No continuations (`call/cc`)\n- No hygienic macros (`syntax-rules`)\n- Single-threaded — concurrency is at the provider level only (LLM batch calls)\n- Young language — stdlib is solid but not battle-tested at scale\n\n---\n\n## Language Reference\n\n### Data Types\n\n| Type         | Syntax               | Examples                              |\n| ------------ | -------------------- | ------------------------------------- |\n| Integer      | digits               | `42`, `-7`, `0`                       |\n| Float        | digits with `.`      | `3.14`, `-0.5`, `1e10`                |\n| String       | double-quoted        | `\"hello\"`, `\"line\\nbreak\"`, `\"\\x1B;\"` |\n| Boolean      | `#t` / `#f`          | `#t`, `#f`                            |\n| Nil          | `nil`                | `nil`                                 |\n| Symbol       | bare identifier      | `foo`, `my-var`, `+`                  |\n| Keyword      | colon-prefixed       | `:name`, `:type`, `:ok`               |\n| Character    | `#\\` prefix          | `#\\a`, `#\\space`, `#\\newline`         |\n| List         | parenthesized        | `(1 2 3)`, `(+ a b)`                  |\n| Vector       | bracketed            | `[1 2 3]`, `[\"a\" \"b\"]`                |\n| Map          | curly-braced         | `{:name \"Ada\" :age 36}`               |\n| HashMap      | `(hashmap/new ...)`  | `(hashmap/new :a 1 :b 2)`             |\n| Prompt       | `(prompt ...)`       | LLM prompt (see below)                |\n| Message      | `(message ...)`      | LLM message (see below)               |\n| Conversation | `(conversation/new)` | LLM conversation (see below)          |\n| Tool         | `(deftool ...)`      | LLM tool definition (see below)       |\n| Agent        | `(defagent ...)`     | LLM agent (see below)                 |\n| Promise      | `(delay expr)`       | Lazy evaluation (see below)           |\n| Record       | `define-record-type` | `(define-record-type point ...)`      |\n| Bytevector   | `#u8(...)` literal   | `#u8(1 2 3)`, `#u8()`                 |\n\n### String Escape Sequences\n\n| Escape       | Description                          | Example               |\n| ------------ | ------------------------------------ | --------------------- |\n| `\\n`         | Newline                              | `\"line\\nbreak\"`       |\n| `\\t`         | Tab                                  | `\"col1\\tcol2\"`        |\n| `\\r`         | Carriage return                      | `\"text\\r\"`            |\n| `\\\\`         | Backslash                            | `\"path\\\\file\"`        |\n| `\\\"`         | Double quote                         | `\"say \\\"hi\\\"\"`        |\n| `\\0`         | Null character                       | `\"\\0\"`                |\n| `\\x\u003chex\u003e;`   | Unicode scalar (R7RS, 1+ hex digits) | `\"\\x1B;\"`, `\"\\x3BB;\"` |\n| `\\uNNNN`     | Unicode code point (4 hex digits)    | `\"\\u03BB\"` (λ)        |\n| `\\UNNNNNNNN` | Unicode code point (8 hex digits)    | `\"\\U0001F600\"` (😀)   |\n\n### Special Forms\n\nThese are built into the evaluator — they control evaluation order and cannot be redefined.\n\n#### Definitions \u0026 Assignment\n\n```scheme\n(define x 42)                          ; bind a value\n(define (square x) (* x x))           ; define a function (shorthand)\n(set! x 99)                           ; mutate an existing binding\n```\n\n#### Functions\n\n```scheme\n(lambda (x y) (+ x y))                ; anonymous function\n(fn (x) (* x x))                      ; fn is an alias for lambda\n(fn (x . rest) rest)                   ; rest parameters with dot notation\n```\n\n#### Conditionals\n\n```scheme\n(if (\u003e x 0) \"positive\" \"non-positive\")\n\n(cond\n  ((\u003c x 0) \"negative\")\n  ((= x 0) \"zero\")\n  (else \"positive\"))\n\n(case (:status response)\n  ((:ok) \"success\")\n  ((:error :timeout) \"failure\")\n  (else \"unknown\"))\n\n(when (\u003e x 0) (println \"positive\"))    ; only runs body if true\n(unless (\u003e x 0) (println \"not positive\"))\n```\n\n#### Bindings\n\n```scheme\n(let ((x 10) (y 20))                   ; parallel bindings\n  (+ x y))\n\n(let* ((x 10) (y (* x 2)))            ; sequential bindings (y sees x)\n  (+ x y))\n\n(letrec ((even? (fn (n) (if (= n 0) #t (odd? (- n 1)))))\n         (odd?  (fn (n) (if (= n 0) #f (even? (- n 1))))))\n  (even? 10))\n\n;; Named let (loop with TCO)\n(let loop ((i 0) (sum 0))\n  (if (= i 100)\n    sum\n    (loop (+ i 1) (+ sum i))))\n```\n\n#### Sequencing \u0026 Logic\n\n```scheme\n(begin expr1 expr2 ... exprN)          ; evaluate in order, return last\n(and a b c)                            ; short-circuit, returns last truthy or #f\n(or a b c)                             ; short-circuit, returns first truthy or #f\n```\n\n#### Iteration\n\n```scheme\n;; Scheme do loop: (do ((var init step) ...) (test result ...) body ...)\n(do ((i 0 (+ i 1))\n     (sum 0 (+ sum i)))\n    ((= i 10) sum))                    ; =\u003e 45\n\n;; Do loop with body (side effects)\n(do ((i 0 (+ i 1)))\n    ((= i 5))\n  (println i))                         ; prints 0..4\n```\n\n#### Lazy Evaluation\n\n```scheme\n(define p (delay (+ 1 2)))             ; create a promise (not evaluated yet)\n(force p)                              ; =\u003e 3 (evaluate and memoize)\n(force p)                              ; =\u003e 3 (returns cached value)\n(force 42)                             ; =\u003e 42 (non-promise passes through)\n(promise? p)                           ; =\u003e #t\n(promise-forced? p)                    ; =\u003e #t (after forcing)\n```\n\n#### Record Types\n\n```scheme\n;; Define a record type with constructor, predicate, and accessors\n(define-record-type point\n  (make-point x y)\n  point?\n  (x point-x)\n  (y point-y))\n\n(define p (make-point 3 4))\n(point? p)                             ; =\u003e #t\n(point-x p)                           ; =\u003e 3\n(point-y p)                           ; =\u003e 4\n(record? p)                           ; =\u003e #t\n(type p)                              ; =\u003e :point\n(equal? (make-point 1 2) (make-point 1 2))  ; =\u003e #t\n```\n\n#### Error Handling\n\n```scheme\n(try\n  (/ 1 0)\n  (catch e\n    (println (format \"Error: ~a\" (:message e)))\n    (:type e)))        ; =\u003e :eval\n\n(throw \"something went wrong\")         ; throw any value\n(throw {:code 404 :reason \"not found\"})\n```\n\nError maps in `catch` have keys: `:type`, `:message`, `:stack-trace`, and variant-specific keys (`:value`, `:expected`/`:got`, `:name`).\n\n#### Macros\n\n```scheme\n(defmacro unless2 (test . body)\n  `(if ,test nil (begin ,@body)))\n\n(unless2 #f (println \"runs!\"))\n\n(macroexpand '(unless2 #f (println \"x\")))  ; see expansion\n(gensym \"tmp\")                              ; hygienic-ish symbol generation\n```\n\n#### Modules\n\n```scheme\n;; math-utils.sema\n(module math-utils\n  (export square cube)\n  (define (square x) (* x x))\n  (define (cube x) (* x x x))\n  (define (internal-helper x) x))      ; not exported\n\n;; main.sema\n(import \"math-utils.sema\")\n(square 5)   ; =\u003e 25\n(cube 3)     ; =\u003e 27\n```\n\n#### Metaprogramming\n\n```scheme\n(eval '(+ 1 2))                        ; evaluate data as code\n(read \"(+ 1 2)\")                       ; parse string to value\n(read-many \"(+ 1 2) (* 3 4)\")          ; parse multiple forms\n(type 42)                              ; =\u003e \"integer\"\n(string-\u003esymbol \"foo\")                 ; type conversions\n(keyword-\u003estring :bar)                 ; =\u003e \"bar\"\n```\n\n### Standard Library\n\n#### Arithmetic \u0026 Math\n\n```scheme\n(+ 1 2 3)        ; =\u003e 6       (- 10 3)      ; =\u003e 7\n(* 4 5)          ; =\u003e 20      (/ 10 2)      ; =\u003e 5\n(mod 10 3)       ; =\u003e 1\n\n(abs -5)         ; =\u003e 5       (min 1 2 3)   ; =\u003e 1\n(max 1 2 3)      ; =\u003e 3       (pow 2 10)    ; =\u003e 1024\n(sqrt 16)        ; =\u003e 4.0     (log 100)     ; =\u003e 4.605...\n(floor 3.7)      ; =\u003e 3.0     (ceil 3.2)    ; =\u003e 4.0\n(round 3.5)      ; =\u003e 4.0\n\n;; Trigonometry\n(sin pi)         (cos pi)     (math/tan pi)\n(math/asin 1)    (math/acos 0)  (math/atan 1)  (math/atan2 1 1)\n\n;; Utility\n(math/exp 1)     (math/log10 100)  (math/log2 8)\n(math/gcd 12 8)  (math/lcm 4 6)\n(math/quotient 10 3)  (math/remainder 10 3)\n(math/random)    (math/random-int 1 100)\n(math/clamp 15 0 10)  (math/sign -5)\n(modulo 10 3)    ; =\u003e 1 (Scheme alias)\n(expt 2 10)      ; =\u003e 1024 (Scheme alias)\n(ceiling 3.2)    ; =\u003e 4 (alias for ceil)\n(truncate 3.7)   ; =\u003e 3\n(even? 4)        ; =\u003e #t\n(odd? 3)         ; =\u003e #t\n\n;; Constants\npi               ; =\u003e 3.14159...\ne                ; =\u003e 2.71828...\n\n;; Bitwise\n(bit/and 5 3)    (bit/or 5 3)    (bit/xor 5 3)\n(bit/not 5)      (bit/shift-left 1 4)  (bit/shift-right 16 2)\n```\n\n#### Strings\n\n```scheme\n(string-append \"hello\" \" \" \"world\")   ; =\u003e \"hello world\"\n(string-length \"hello\")               ; =\u003e 5\n(string-ref \"hello\" 0)                ; =\u003e #\\h (returns char)\n(substring \"hello\" 1 3)               ; =\u003e \"el\"\n(str 42)                              ; =\u003e \"42\" (any value to string)\n(format \"~a is ~a\" \"Sema\" \"great\")    ; =\u003e \"Sema is great\"\n\n;; Slash-namespaced string operations\n(string/split \"a,b,c\" \",\")            ; =\u003e (\"a\" \"b\" \"c\")\n(string/join '(\"a\" \"b\" \"c\") \", \")     ; =\u003e \"a, b, c\"\n(string/trim \"  hello  \")             ; =\u003e \"hello\"\n(string/trim-left \"  hi\")             ; =\u003e \"hi\"\n(string/trim-right \"hi  \")            ; =\u003e \"hi\"\n(string/upper \"hello\")                ; =\u003e \"HELLO\"\n(string/lower \"HELLO\")                ; =\u003e \"hello\"\n(string/contains? \"hello\" \"ell\")      ; =\u003e #t\n(string/starts-with? \"hello\" \"he\")    ; =\u003e #t\n(string/ends-with? \"hello\" \"lo\")      ; =\u003e #t\n(string/replace \"hello\" \"l\" \"r\")      ; =\u003e \"herro\"\n(string/index-of \"hello\" \"ll\")        ; =\u003e 2\n(string/chars \"abc\")                  ; =\u003e (#\\a #\\b #\\c)\n(string/repeat \"ab\" 3)                ; =\u003e \"ababab\"\n(string/pad-left \"42\" 5 \"0\")          ; =\u003e \"00042\"\n(string/pad-right \"hi\" 5)             ; =\u003e \"hi   \"\n(string/number? \"42\")                 ; =\u003e #t\n(string/map char-upcase \"hello\")       ; =\u003e \"HELLO\"\n(string/capitalize \"hello\")            ; =\u003e \"Hello\"\n(string/title-case \"hello world\")      ; =\u003e \"Hello World\"\n(string/reverse \"hello\")               ; =\u003e \"olleh\"\n\n;; Characters\n#\\a                                   ; character literal\n#\\space  #\\newline  #\\tab             ; named characters\n(char-\u003einteger #\\A)                   ; =\u003e 65\n(integer-\u003echar 65)                    ; =\u003e #\\A\n(char-alphabetic? #\\a)                ; =\u003e #t\n(char-numeric? #\\5)                   ; =\u003e #t\n(char-whitespace? #\\space)            ; =\u003e #t\n(char-upper-case? #\\A)                ; =\u003e #t\n(char-upcase #\\a)                     ; =\u003e #\\A\n(char-downcase #\\Z)                   ; =\u003e #\\z\n(char-\u003estring #\\a)                    ; =\u003e \"a\"\n(string-\u003echar \"a\")                    ; =\u003e #\\a\n(string-\u003elist \"abc\")                  ; =\u003e (#\\a #\\b #\\c)\n(list-\u003estring '(#\\h #\\i))            ; =\u003e \"hi\"\n\n;; Character comparison (R7RS)\n(char=? #\\a #\\a)                      ; =\u003e #t\n(char\u003c? #\\a #\\b)                      ; =\u003e #t\n(char\u003e? #\\b #\\a)                      ; =\u003e #t\n(char\u003c=? #\\a #\\b)                     ; =\u003e #t\n(char\u003e=? #\\b #\\a)                     ; =\u003e #t\n(char-ci=? #\\A #\\a)                   ; =\u003e #t (case-insensitive)\n\n;; Type conversions\n(string-\u003enumber \"42\")                 ; =\u003e 42\n(number-\u003estring 42)                   ; =\u003e \"42\"\n(string-\u003esymbol \"foo\")                ; =\u003e foo\n(symbol-\u003estring 'foo)                 ; =\u003e \"foo\"\n(string-\u003ekeyword \"name\")              ; =\u003e :name\n(keyword-\u003estring :name)               ; =\u003e \"name\"\n```\n\n#### Lists\n\n```scheme\n(list 1 2 3)                          ; =\u003e (1 2 3)\n(cons 0 '(1 2 3))                     ; =\u003e (0 1 2 3)\n(car '(1 2 3))                        ; =\u003e 1\n(cdr '(1 2 3))                        ; =\u003e (2 3)\n(first '(1 2 3))                      ; =\u003e 1\n(rest '(1 2 3))                       ; =\u003e (2 3)\n(cadr '(1 2 3))                       ; =\u003e 2 (car/cdr compositions: caar..cdddr)\n(caddr '(1 2 3))                      ; =\u003e 3\n(last '(1 2 3))                       ; =\u003e 3\n(nth '(10 20 30) 1)                   ; =\u003e 20\n\n;; Association lists\n(define alist '((\"a\" 1) (\"b\" 2) (\"c\" 3)))\n(assoc \"b\" alist)                     ; =\u003e (\"b\" 2) (alist lookup)\n(assq 'b '((a 1) (b 2)))             ; =\u003e (b 2) (uses eq? comparison)\n(assv 2 '((1 \"one\") (2 \"two\")))      ; =\u003e (2 \"two\") (uses eqv? comparison)\n(length '(1 2 3))                     ; =\u003e 3\n(append '(1 2) '(3 4))                ; =\u003e (1 2 3 4)\n(reverse '(1 2 3))                    ; =\u003e (3 2 1)\n(range 5)                             ; =\u003e (0 1 2 3 4)\n(range 1 5)                           ; =\u003e (1 2 3 4)\n\n;; Higher-order functions\n(map (fn (x) (* x x)) '(1 2 3))      ; =\u003e (1 4 9)\n(map + '(1 2 3) '(10 20 30))          ; =\u003e (11 22 33)  (multi-list)\n(filter even? '(1 2 3 4 5))           ; =\u003e (2 4)\n(foldl + 0 '(1 2 3 4 5))             ; =\u003e 15\n(foldr cons '() '(1 2 3))            ; =\u003e (1 2 3)\n(reduce + '(1 2 3 4 5))              ; =\u003e 15\n(for-each println '(\"a\" \"b\" \"c\"))     ; side-effect iteration\n(sort '(3 1 4 1 5))                   ; =\u003e (1 1 3 4 5)\n(sort-by length '(\"bb\" \"a\" \"ccc\"))    ; =\u003e (\"a\" \"bb\" \"ccc\")\n(apply + '(1 2 3))                    ; =\u003e 6\n\n;; Sublists\n(take 3 '(1 2 3 4 5))                ; =\u003e (1 2 3)\n(drop 2 '(1 2 3 4 5))                ; =\u003e (3 4 5)\n(flatten '(1 (2 (3)) 4))             ; =\u003e (1 2 3 4)\n(zip '(1 2 3) '(\"a\" \"b\" \"c\"))        ; =\u003e ((1 \"a\") (2 \"b\") (3 \"c\"))\n(partition even? '(1 2 3 4 5))        ; =\u003e ((2 4) (1 3 5))\n\n;; Searching\n(member 3 '(1 2 3 4))                ; =\u003e (3 4)\n(any even? '(1 3 5 6))               ; =\u003e #t\n(every even? '(2 4 6))               ; =\u003e #t\n(list/index-of '(10 20 30) 20)       ; =\u003e 1\n(list/unique '(1 2 2 3 3 3))         ; =\u003e (1 2 3)\n\n;; Grouping\n(list/group-by even? '(1 2 3 4 5))   ; =\u003e {#f (1 3 5) #t (2 4)}\n(list/interleave '(1 2 3) '(a b c))  ; =\u003e (1 a 2 b 3 c)\n(list/chunk 2 '(1 2 3 4 5))          ; =\u003e ((1 2) (3 4) (5))\n(frequencies '(a b a c b a))          ; =\u003e {a 3 b 2 c 1}\n(interpose \", \" '(\"a\" \"b\" \"c\"))      ; =\u003e (\"a\" \", \" \"b\" \", \" \"c\")\n\n;; Aggregation\n(list/sum '(1 2 3 4 5))               ; =\u003e 15\n(list/min '(3 1 4 1 5))               ; =\u003e 1\n(list/max '(3 1 4 1 5))               ; =\u003e 5\n\n;; Random\n(list/shuffle '(1 2 3 4 5))           ; =\u003e (3 1 5 2 4) (random order)\n(list/pick '(1 2 3 4 5))              ; =\u003e 3 (random element)\n\n;; Construction\n(list/repeat 3 0)                      ; =\u003e (0 0 0)\n(make-list 3 0)                        ; =\u003e (0 0 0) (alias)\n(iota 5)                               ; =\u003e (0 1 2 3 4)\n(iota 3 10)                            ; =\u003e (10 11 12)\n(iota 4 0 2)                           ; =\u003e (0 2 4 6)\n\n;; Splitting\n(list/split-at '(1 2 3 4 5) 3)        ; =\u003e ((1 2 3) (4 5))\n(list/take-while (fn (x) (\u003c x 4)) '(1 2 3 4 5))  ; =\u003e (1 2 3)\n(list/drop-while (fn (x) (\u003c x 4)) '(1 2 3 4 5))  ; =\u003e (4 5)\n```\n\n#### Vectors\n\n```scheme\n(vector 1 2 3)                        ; =\u003e [1 2 3]\n[1 2 3]                               ; literal syntax\n(vector-\u003elist [1 2 3])                ; =\u003e (1 2 3)\n(list-\u003evector '(1 2 3))               ; =\u003e [1 2 3]\n```\n\nMost list functions work on vectors too.\n\n#### Bytevectors\n\n```scheme\n#u8(1 2 3)                            ; literal syntax\n(bytevector 1 2 3)                    ; =\u003e #u8(1 2 3)\n(make-bytevector 4)                   ; =\u003e #u8(0 0 0 0)\n(make-bytevector 3 255)               ; =\u003e #u8(255 255 255)\n(bytevector-length #u8(1 2 3))        ; =\u003e 3\n(bytevector-u8-ref #u8(10 20 30) 1)   ; =\u003e 20\n(bytevector-u8-set! #u8(1 2 3) 0 9)  ; =\u003e #u8(9 2 3) (COW, original unchanged)\n(bytevector-copy #u8(1 2 3 4 5) 1 3) ; =\u003e #u8(2 3)\n(bytevector-append #u8(1 2) #u8(3 4)); =\u003e #u8(1 2 3 4)\n(bytevector-\u003elist #u8(65 66))         ; =\u003e (65 66)\n(list-\u003ebytevector '(1 2 3))           ; =\u003e #u8(1 2 3)\n(utf8-\u003estring #u8(104 105))           ; =\u003e \"hi\"\n(string-\u003eutf8 \"hi\")                   ; =\u003e #u8(104 105)\n```\n\n#### Maps\n\n```scheme\n(hash-map :a 1 :b 2)                 ; =\u003e {:a 1 :b 2}\n{:a 1 :b 2}                          ; literal syntax\n(get {:a 1 :b 2} :a)                 ; =\u003e 1\n(:a {:a 1 :b 2})                     ; =\u003e 1  (keywords are functions)\n(assoc {:a 1} :b 2)                  ; =\u003e {:a 1 :b 2}\n(dissoc {:a 1 :b 2} :a)             ; =\u003e {:b 2}\n(merge {:a 1} {:b 2} {:c 3})        ; =\u003e {:a 1 :b 2 :c 3}\n(keys {:a 1 :b 2})                   ; =\u003e (:a :b)\n(vals {:a 1 :b 2})                   ; =\u003e (1 2)\n(contains? {:a 1} :a)               ; =\u003e #t\n(count {:a 1 :b 2})                  ; =\u003e 2\n(map/entries {:a 1 :b 2})           ; =\u003e ((:a 1) (:b 2))\n(map/from-entries '((:a 1) (:b 2))) ; =\u003e {:a 1 :b 2}\n\n;; Higher-order map operations\n(map/map-vals (fn (v) (* v 2)) {:a 1 :b 2})     ; =\u003e {:a 2 :b 4}\n(map/map-keys (fn (k) (string-\u003ekeyword (string/upper (keyword-\u003estring k)))) {:a 1})\n(map/filter (fn (k v) (\u003e v 1)) {:a 1 :b 2 :c 3}) ; =\u003e {:b 2 :c 3}\n(map/select-keys {:a 1 :b 2 :c 3} '(:a :c))      ; =\u003e {:a 1 :c 3}\n(map/update {:a 1} :a (fn (v) (+ v 10)))           ; =\u003e {:a 11}\n```\n\n#### HashMaps\n\nFor performance-critical workloads with many keys, use `hashmap` for O(1) lookups instead of the sorted `map`:\n\n```scheme\n(hashmap/new :a 1 :b 2 :c 3)          ; create a hashmap\n(hashmap/get (hashmap/new :a 1) :a)    ; =\u003e 1\n(hashmap/assoc (hashmap/new) :a 1)     ; add entries\n(hashmap/to-map (hashmap/new :b 2 :a 1))  ; =\u003e {:a 1 :b 2} (sorted)\n(hashmap/keys (hashmap/new :a 1 :b 2)) ; =\u003e (:a :b) (unordered)\n(hashmap/contains? (hashmap/new :a 1) :a)  ; =\u003e #t\n\n;; Generic get, assoc, keys, vals, count, contains? also work on hashmaps\n(get (hashmap/new :a 1 :b 2) :a)       ; =\u003e 1\n(assoc (hashmap/new) :x 42)            ; =\u003e hashmap with :x 42\n```\n\n#### Predicates \u0026 Type Checking\n\n```scheme\n(null? '())        (nil? nil)         (empty? \"\")\n(list? '(1))       (vector? [1])      (map? {:a 1})\n(pair? '(1 2))     ; #t (non-empty list, Scheme compat)\n(number? 42)       (integer? 42)      (float? 3.14)\n(string? \"hi\")     (symbol? 'x)       (keyword? :k)\n(char? #\\a)        (record? r)        (bytevector? #u8())\n(promise? (delay 1))  (promise-forced? p)\n(bool? #t)         (fn? car)\n(zero? 0)          (even? 4)          (odd? 3)\n(positive? 1)      (negative? -1)\n(eq? 'a 'a)        (= 1 1)\n\n;; Scheme aliases: boolean? = bool?, procedure? = fn?, equal? = eq?\n\n;; LLM type predicates\n(prompt? p)        (message? m)       (conversation? c)\n(tool? t)          (agent? a)\n```\n\n#### I/O\n\n```scheme\n;; Console\n(display \"no newline\")                ; print without newline\n(println \"with newline\")              ; print with newline\n(print \"also no newline\")             ; alias for display\n(newline)                             ; print a newline\n(read-line)                           ; read a line from stdin\n\n;; Files\n(file/read \"data.txt\")               ; =\u003e file contents as string\n(file/write \"out.txt\" \"content\")     ; write (overwrite)\n(file/append \"log.txt\" \"line\\n\")     ; append\n(file/read-lines \"data.txt\")         ; =\u003e list of lines\n(file/write-lines \"out.txt\" '(\"a\" \"b\"))\n(file/delete \"tmp.txt\")              ; delete file\n(file/rename \"old.txt\" \"new.txt\")    ; rename/move\n(file/copy \"src.txt\" \"dst.txt\")      ; copy\n(file/exists? \"data.txt\")            ; =\u003e #t/#f\n(file/is-file? \"data.txt\")           ; =\u003e #t\n(file/is-directory? \"src/\")          ; =\u003e #t\n(file/is-symlink? \"link\")            ; =\u003e #t/#f\n(file/list \"src/\")                   ; =\u003e list of entries\n(file/mkdir \"new-dir\")               ; create directory\n(file/info \"data.txt\")               ; =\u003e {:size N :modified N ...}\n\n;; Paths\n(path/join \"src\" \"main.rs\")          ; =\u003e \"src/main.rs\"\n(path/dirname \"/a/b/c.txt\")         ; =\u003e \"/a/b\"\n(path/basename \"/a/b/c.txt\")        ; =\u003e \"c.txt\"\n(path/extension \"file.rs\")          ; =\u003e \"rs\"\n(path/absolute \"file.txt\")          ; =\u003e \"/full/path/file.txt\"\n```\n\n#### HTTP\n\n```scheme\n(http/get \"https://httpbin.org/get\")\n; =\u003e {:status 200 :headers {...} :body \"...\"}\n\n(http/post \"https://httpbin.org/post\"\n  {:body {:key \"value\"}              ; maps auto-serialize as JSON\n   :headers {\"Authorization\" \"Bearer ...\"}})\n\n(http/put url {:body \"data\"})\n(http/delete url)\n(http/request {:method \"PATCH\" :url url :body \"data\"})\n```\n\n#### JSON\n\n```scheme\n(json/encode {:name \"Ada\" :age 36})       ; =\u003e \"{\\\"age\\\":36,\\\"name\\\":\\\"Ada\\\"}\"\n(json/encode-pretty {:a 1 :b [2 3]})      ; =\u003e formatted JSON\n(json/decode \"{\\\"name\\\":\\\"Ada\\\"}\")         ; =\u003e {:name \"Ada\"}\n```\n\n#### Regex\n\n```scheme\n(regex/match? \"\\\\d+\" \"abc123\")             ; =\u003e #t\n(regex/match \"^(\\\\w+)@(\\\\w+)\" \"user@host\")  ; =\u003e (\"user@host\" \"user\" \"host\")\n(regex/find-all \"\\\\d+\" \"a1b2c3\")           ; =\u003e (\"1\" \"2\" \"3\")\n(regex/replace \"\\\\d\" \"a1b2\" \"X\")           ; =\u003e \"aXb2\" (first match)\n(regex/replace-all \"\\\\d\" \"a1b2\" \"X\")       ; =\u003e \"aXbX\"\n(regex/split \",\" \"a,b,c\")                  ; =\u003e (\"a\" \"b\" \"c\")\n```\n\n#### CSV\n\n```scheme\n(csv/parse \"a,b\\n1,2\\n3,4\")               ; =\u003e ((\"a\" \"b\") (\"1\" \"2\") (\"3\" \"4\"))\n(csv/parse-maps \"name,age\\nAda,36\")       ; =\u003e ({:age \"36\" :name \"Ada\"})\n(csv/encode '((\"a\" \"b\") (\"1\" \"2\")))       ; =\u003e \"a,b\\n1,2\\n\"\n```\n\n#### Crypto \u0026 Encoding\n\n```scheme\n(uuid/v4)                                  ; =\u003e \"550e8400-e29b-41d4-...\"\n(base64/encode \"hello\")                    ; =\u003e \"aGVsbG8=\"\n(base64/decode \"aGVsbG8=\")                ; =\u003e \"hello\"\n(hash/sha256 \"hello\")                     ; =\u003e \"2cf24dba...\"\n```\n\n#### Date \u0026 Time\n\n```scheme\n(time/now)                                ; =\u003e 1707955200.123 (unix secs)\n(time-ms)                                 ; =\u003e 1707955200123  (unix ms)\n(time/format (time/now) \"%Y-%m-%d\")       ; =\u003e \"2025-02-15\"\n(time/parse \"2025-01-15\" \"%Y-%m-%d\")      ; =\u003e 1736899200.0\n(time/date-parts (time/now))              ; =\u003e {:year 2025 :month 2 :day 15 ...}\n(time/add (time/now) 86400)               ; add seconds\n(time/diff t1 t2)                         ; difference in seconds\n(sleep 1000)                              ; sleep N milliseconds\n```\n\n#### System\n\n```scheme\n(env \"HOME\")                              ; =\u003e \"/Users/ada\"\n(sys/args)                                ; =\u003e (\"sema\" \"script.sema\" ...)\n(sys/cwd)                                 ; =\u003e \"/current/dir\"\n(sys/platform)                            ; =\u003e \"macos\" / \"linux\" / \"windows\"\n(sys/env-all)                             ; =\u003e {:HOME \"...\" :PATH \"...\" ...}\n(sys/set-env \"KEY\" \"value\")               ; set env var\n(sys/pid)                                 ; =\u003e 12345 (process ID)\n(sys/os)                                  ; =\u003e \"macos\" (OS name)\n(sys/arch)                                ; =\u003e \"aarch64\" (CPU architecture)\n(sys/tty)                                 ; =\u003e \"/dev/ttys003\" or nil\n(sys/which \"cargo\")                       ; =\u003e \"/Users/.../.cargo/bin/cargo\" or nil\n(sys/elapsed)                             ; =\u003e 482937100 (nanoseconds since start)\n(sys/interactive?)                        ; =\u003e #t if stdin is a TTY\n(sys/hostname)                            ; =\u003e \"my-machine\"\n(sys/user)                                ; =\u003e \"ada\"\n(sys/home-dir)                            ; =\u003e \"/Users/ada\"\n(sys/temp-dir)                            ; =\u003e \"/tmp\"\n(shell \"ls -la\")                          ; run shell command, return stdout\n(exit 0)                                  ; exit with code\n```\n\n---\n\n## LLM Primitives\n\nSema's differentiating feature: LLM operations are first-class language primitives with prompts, conversations, tools, and agents as native data types.\n\n### Setup\n\nSet one or more API keys as environment variables:\n\n```bash\nexport ANTHROPIC_API_KEY=sk-ant-...\nexport OPENAI_API_KEY=sk-...\n# or any other supported provider (see table below)\n```\n\nSema auto-detects and configures all available providers on startup. Use `--no-llm` to skip auto-configuration.\n\n### Completion\n\n```scheme\n;; Simple completion\n(llm/complete \"Say hello in 5 words\" {:max-tokens 50})\n\n;; With options\n(llm/complete \"Explain monads\"\n  {:model \"claude-haiku-4-5-20251001\"\n   :max-tokens 200\n   :temperature 0.3\n   :system \"You are a Haskell expert.\"})\n\n;; Streaming (prints chunks as they arrive)\n(llm/stream \"Tell me a story\" {:max-tokens 200})\n\n;; Streaming with a callback\n(llm/stream \"Tell me a story\"\n  (fn (chunk) (display chunk))\n  {:max-tokens 200})\n```\n\n### Messages \u0026 Chat\n\n```scheme\n;; Chat with message list\n(llm/chat\n  [(message :system \"You are a helpful assistant.\")\n   (message :user \"What is Lisp? One sentence.\")]\n  {:max-tokens 100})\n\n;; Prompts as composable data structures\n(define review-prompt\n  (prompt\n    (system \"You are a code reviewer. Be concise.\")\n    (user \"Review this function.\")))\n\n(llm/send review-prompt {:max-tokens 200})\n\n;; Compose prompts\n(define base (prompt (system \"You are helpful.\")))\n(define question (prompt (user \"What is 2+2?\")))\n(llm/send (prompt/append base question))\n\n;; Inspect prompts and messages\n(prompt/messages my-prompt)             ; =\u003e list of messages\n(prompt/set-system my-prompt \"new sys\") ; =\u003e new prompt with replaced system msg\n(message/role (message :user \"hi\"))     ; =\u003e :user\n(message/content (message :user \"hi\"))  ; =\u003e \"hi\"\n```\n\n### Conversations\n\nPersistent, immutable conversation state with automatic LLM round-trips:\n\n```scheme\n(define conv (conversation/new {:model \"claude-haiku-4-5-20251001\"}))\n(define conv (conversation/say conv \"Remember: the secret number is 7\"))\n(define conv (conversation/say conv \"What is the secret number?\"))\n(conversation/last-reply conv)          ; =\u003e \"The secret number is 7.\"\n\n;; With options\n(define conv (conversation/say conv \"Explain more\"\n  {:temperature 0.5 :max-tokens 500}))\n\n;; Inspect\n(conversation/messages conv)            ; =\u003e list of message values\n(conversation/model conv)              ; =\u003e \"claude-haiku-4-5-20251001\"\n\n;; Build manually\n(define c (conversation/new {}))\n(define c (conversation/add-message c :user \"hello\"))\n(define c (conversation/add-message c :assistant \"hi there\"))\n```\n\n### Structured Extraction\n\n```scheme\n(llm/extract\n  {:vendor {:type :string}\n   :amount {:type :number}\n   :date   {:type :string}}\n  \"I bought coffee for $4.50 at Blue Bottle on Jan 15, 2025\")\n; =\u003e {:amount 4.5 :date \"2025-01-15\" :vendor \"Blue Bottle\"}\n\n(llm/classify [:positive :negative :neutral]\n              \"This product is amazing!\")\n; =\u003e :positive\n```\n\n### Tools \u0026 Function Calling\n\nDefine tools that the LLM can invoke during a conversation:\n\n```scheme\n(deftool lookup-capital\n  \"Look up the capital of a country\"\n  {:country {:type :string :description \"Country name\"}}\n  (lambda (country)\n    (cond\n      ((= country \"Norway\") \"Oslo\")\n      ((= country \"France\") \"Paris\")\n      (else \"Unknown\"))))\n\n;; The LLM will call the tool automatically\n(llm/chat\n  [(message :user \"What is the capital of Norway?\")]\n  {:tools [lookup-capital] :max-tokens 100})\n\n;; Inspect tools\n(tool/name lookup-capital)              ; =\u003e \"lookup-capital\"\n(tool/description lookup-capital)       ; =\u003e \"Look up the capital...\"\n(tool/parameters lookup-capital)        ; =\u003e {:country {:type :string ...}}\n```\n\n### Agents\n\nAgents combine a system prompt, tools, and a multi-turn loop:\n\n```scheme\n(deftool get-weather\n  \"Get weather for a city\"\n  {:city {:type :string}}\n  (lambda (city)\n    (format \"~a: 22°C, sunny\" city)))\n\n(defagent weather-bot\n  {:system \"You are a weather assistant. Use the get-weather tool.\"\n   :tools [get-weather]\n   :model \"claude-haiku-4-5-20251001\"\n   :max-turns 3})\n\n(agent/run weather-bot \"What's the weather in Tokyo?\")\n\n;; Inspect agents\n(agent/name weather-bot)                ; =\u003e \"weather-bot\"\n(agent/system weather-bot)              ; =\u003e \"You are a weather assistant...\"\n(agent/tools weather-bot)               ; =\u003e list of tool values\n(agent/model weather-bot)               ; =\u003e \"claude-haiku-4-5-20251001\"\n(agent/max-turns weather-bot)           ; =\u003e 3\n```\n\n### Embeddings \u0026 Similarity\n\n```scheme\n;; Configure an embedding provider\n;; (auto-configured from JINA_API_KEY, VOYAGE_API_KEY, or COHERE_API_KEY)\n\n(define v1 (llm/embed \"hello world\"))\n(define v2 (llm/embed \"hi there\"))\n(llm/similarity v1 v2)                 ; =\u003e 0.87 (cosine similarity)\n\n;; Batch embeddings\n(llm/embed [\"cat\" \"dog\" \"fish\"])       ; =\u003e list of vectors\n```\n\n### Provider Management\n\n```scheme\n(llm/auto-configure)                   ; auto-detect from env vars\n(llm/configure :anthropic {:api-key \"sk-...\"})  ; manual setup\n\n;; Runtime provider switching\n(llm/list-providers)                   ; =\u003e (:anthropic :gemini :openai ...)\n(llm/current-provider)                 ; =\u003e {:name :anthropic :model \"claude-sonnet-4-20250514\"}\n(llm/set-default :openai)              ; switch active provider\n\n;; Explicit provider config with options\n(llm/configure :ollama {:host \"http://localhost:11434\"\n                         :default-model \"llama3\"})\n```\n\n### Cost Tracking \u0026 Budgets\n\n```scheme\n(llm/last-usage)                       ; =\u003e {:prompt-tokens 42 :completion-tokens 15 ...}\n(llm/session-usage)                    ; =\u003e cumulative usage across all calls\n(llm/reset-usage)                      ; reset session counters\n\n;; Budget enforcement\n(llm/set-budget 1.00)                  ; set $1.00 spending limit\n(llm/budget-remaining)                 ; =\u003e {:limit 1.0 :spent 0.05 :remaining 0.95}\n(llm/clear-budget)                     ; remove limit\n\n;; Custom pricing for unlisted models\n(llm/set-pricing \"my-model\" 1.0 3.0)  ; input/output per million tokens\n```\n\n### Batch \u0026 Parallel\n\n```scheme\n;; Send multiple prompts concurrently\n(llm/batch [\"Translate 'hello' to French\"\n            \"Translate 'hello' to Spanish\"\n            \"Translate 'hello' to German\"])\n\n;; Map a function over items, sending all prompts in parallel\n(llm/pmap\n  (fn (word) (format \"Define: ~a\" word))\n  '(\"serendipity\" \"ephemeral\" \"ubiquitous\")\n  {:max-tokens 50})\n```\n\n### Supported Providers\n\nAll providers are auto-configured from environment variables. Use `(llm/configure :provider {...})` for manual setup.\n\n| Provider          | Type           | Chat | Stream | Tools | Embeddings |\n| ----------------- | -------------- | ---- | ------ | ----- | ---------- |\n| **Anthropic**     | Native         | ✅   | ✅     | ✅    | —          |\n| **OpenAI**        | Native         | ✅   | ✅     | ✅    | ✅         |\n| **Google Gemini** | Native         | ✅   | ✅     | ✅    | —          |\n| **Ollama**        | Native (local) | ✅   | ✅     | ✅    | —          |\n| **Groq**          | OpenAI-compat  | ✅   | ✅     | ✅    | —          |\n| **xAI**           | OpenAI-compat  | ✅   | ✅     | ✅    | —          |\n| **Mistral**       | OpenAI-compat  | ✅   | ✅     | ✅    | —          |\n| **Moonshot**      | OpenAI-compat  | ✅   | ✅     | ✅    | —          |\n| **Jina**          | Embedding-only | —    | —      | —     | ✅         |\n| **Voyage**        | Embedding-only | —    | —      | —     | ✅         |\n| **Cohere**        | Embedding-only | —    | —      | —     | ✅         |\n\n---\n\n## CLI Reference\n\n```\nsema [OPTIONS] [FILE] [-- SCRIPT_ARGS...]\n```\n\n| Flag                 | Description                                  |\n| -------------------- | -------------------------------------------- |\n| `-e, --eval \u003cEXPR\u003e`  | Evaluate expression, print result if non-nil |\n| `-p, --print \u003cEXPR\u003e` | Evaluate expression, always print result     |\n| `-l, --load \u003cFILE\u003e`  | Load file(s) before executing (repeatable)   |\n| `-q, --quiet`        | Suppress REPL banner                         |\n| `-i, --interactive`  | Enter REPL after running file or eval        |\n| `--no-init`          | Skip LLM auto-configuration                  |\n| `--no-llm`           | Disable LLM features (same as `--no-init`)   |\n| `--model \u003cNAME\u003e`     | Set default LLM model                        |\n| `--provider \u003cNAME\u003e`  | Set LLM provider                             |\n| `-V, --version`      | Print version                                |\n| `-h, --help`         | Print help                                   |\n\n### Subcommands\n\n```\nsema ast [OPTIONS] [FILE]\n```\n\n| Flag                | Description                      |\n| ------------------- | -------------------------------- |\n| `-e, --eval \u003cEXPR\u003e` | Parse expression instead of file |\n| `--json`            | Output AST as JSON               |\n\n### Examples\n\n```bash\n# Parse a file into an AST tree\nsema ast script.sema\n\n# Parse an expression into JSON AST\nsema ast -e '(+ 1 2)' --json\n\n# Load a prelude before starting the REPL\nsema -l prelude.sema\n\n# Load helpers, then run a script\nsema -l helpers.sema script.sema\n\n# Run a script and drop into REPL to inspect state\nsema -i script.sema\n\n# Quick one-liner for shell pipelines\nsema -p '(string/join (map str (range 10)) \",\")'\n\n# Run without LLM features (faster startup)\nsema --no-llm script.sema\n\n# Use a specific model\nsema --model claude-haiku-4-5-20251001 -e '(llm/complete \"Hello!\")'\n\n# Shebang support in scripts\n#!/usr/bin/env sema\n```\n\n### Environment Variables\n\n| Variable             | Description                                           |\n| -------------------- | ----------------------------------------------------- |\n| `ANTHROPIC_API_KEY`  | Anthropic API key (auto-detected)                     |\n| `OPENAI_API_KEY`     | OpenAI API key (auto-detected)                        |\n| `GROQ_API_KEY`       | Groq API key (auto-detected)                          |\n| `XAI_API_KEY`        | xAI/Grok API key (auto-detected)                      |\n| `MISTRAL_API_KEY`    | Mistral API key (auto-detected)                       |\n| `MOONSHOT_API_KEY`   | Moonshot API key (auto-detected)                      |\n| `GOOGLE_API_KEY`     | Google Gemini API key (auto-detected)                 |\n| `OLLAMA_HOST`        | Ollama server URL (default: `http://localhost:11434`) |\n| `JINA_API_KEY`       | Jina embeddings API key (auto-detected)               |\n| `VOYAGE_API_KEY`     | Voyage embeddings API key (auto-detected)             |\n| `COHERE_API_KEY`     | Cohere embeddings API key (auto-detected)             |\n| `SEMA_DEFAULT_MODEL` | Default model name                                    |\n| `SEMA_LLM_PROVIDER`  | Preferred provider                                    |\n\n### REPL Commands\n\n| Command        | Description                |\n| -------------- | -------------------------- |\n| `,quit` / `,q` | Exit the REPL              |\n| `,help` / `,h` | Show help                  |\n| `,env`         | Show user-defined bindings |\n\n---\n\n## Crate Structure\n\n```\ncrates/\n  sema-core/     Value types, errors, environment (Rc, BTreeMap, lasso, hashbrown, thiserror)\n  sema-reader/   Hand-written lexer and s-expression parser\n  sema-eval/     Trampoline-based evaluator, special forms, module system\n  sema-stdlib/   Standard library builtins (memchr, hashbrown)\n  sema-llm/      LLM provider trait + multi-provider API clients\n  sema/          Binary: REPL (rustyline) and file runner (clap)\n```\n\nDependency flow: `sema-core` ← `sema-reader` ← `sema-eval` ← `sema-stdlib` / `sema-llm` ← `sema`\n\n## License\n\nMIT — see [LICENSE](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhelgesverre%2Fsema","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhelgesverre%2Fsema","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhelgesverre%2Fsema/lists"}