{"id":38999603,"url":"https://github.com/codr7/sharpl","last_synced_at":"2026-01-17T17:11:02.114Z","repository":{"id":237019989,"uuid":"793641660","full_name":"codr7/sharpl","owner":"codr7","description":"a custom Lisp","archived":false,"fork":false,"pushed_at":"2024-09-16T23:10:31.000Z","size":728,"stargazers_count":82,"open_issues_count":0,"forks_count":4,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-09-17T11:31:01.054Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"C#","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/codr7.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","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}},"created_at":"2024-04-29T15:47:52.000Z","updated_at":"2024-09-16T23:10:34.000Z","dependencies_parsed_at":"2024-06-27T00:49:17.982Z","dependency_job_id":"d9c7be47-4cb2-4732-9430-371ed474e8ce","html_url":"https://github.com/codr7/sharpl","commit_stats":null,"previous_names":["codr7/sharpl"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/codr7/sharpl","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codr7%2Fsharpl","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codr7%2Fsharpl/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codr7%2Fsharpl/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codr7%2Fsharpl/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/codr7","download_url":"https://codeload.github.com/codr7/sharpl/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codr7%2Fsharpl/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28511976,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-17T13:38:16.342Z","status":"ssl_error","status_checked_at":"2026-01-17T13:37:44.060Z","response_time":85,"last_error":"SSL_read: 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":[],"created_at":"2026-01-17T17:11:02.012Z","updated_at":"2026-01-17T17:11:02.079Z","avatar_url":"https://github.com/codr7.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"﻿# sharpl\n\n```\n$ dotnet run\nsharpl v29\n\n   1 (say 'hello)\n   2 \nhello\n```\n\n## intro\nSharpl is a custom Lisp interpreter implemented in C#.\u003cbr/\u003e\n\u003cbr/\u003e\nIt's trivial to embed and comes with a simple REPL.\u003cbr/\u003e\nThe code base currently hovers around 7 kloc and has no external dependencies.\u003cbr/\u003e\n\u003cbr/\u003e\nAll features described in this document are part of the [test suite](https://github.com/codr7/sharpl/tree/main/tests) and expected to work as described.\n\n## novelties\n\n- [Pairs](https://github.com/codr7/sharpl/tree/main#pairs) (`a:b`) are used everywhere, all the time.\n- [Range](https://github.com/codr7/sharpl?tab=readme-ov-file#ranges) support (`min..max:stride`).\n- [Maps](https://github.com/codr7/sharpl/tree/main#maps) (`{k1:v1...kN:vN}`) are ordered.\n- [Methods](https://github.com/codr7/sharpl/tree/main#methods) may be [composed](https://github.com/codr7/sharpl/tree/main#composition) using `\u0026`.\n- [Varargs](https://github.com/codr7/sharpl/tree/main#varargs) (`foo*`), similar to Python.\n- [Declarative destructuring](https://github.com/codr7/sharpl/tree/main#destructuring) (`(let [_:y 1:2] y)`) of bindings.\n- [Splatting](https://github.com/codr7/sharpl#splat) (`[1 2 3]*`), simlar to Python.\n- [Deferred actions](https://github.com/codr7/sharpl#deferred-actions), similar to Go.\n- Unified, deeply integrated [iterator](https://github.com/codr7/sharpl/tree/main#iterators) protocol.\n- Default decimal type is [fixpoint](https://github.com/codr7/sharpl/tree/main#fixpoints).\n- Nil is written `_`.\n- Both `T` and `F` are defined.\n- Zeros and empty strings, arrays, lists and maps are considered false.\n- `=` compares values deeply, `is` may be used to only compare identity.\n- [Lambdas](https://github.com/codr7/sharpl/tree/main#lambdas) look like anonymous methods.\n- Compile time [eval](https://github.com/codr7/sharpl#evaluation) (`emit`), similar to Zig's comptime.\n- Explicit [tail calls](https://github.com/codr7/sharpl/tree/main#tail-calls) using `return`.\n- Parens are used for calls only.\n- Many things are callable, simlar to Clojure.\n- [Methods](https://github.com/codr7/sharpl/tree/main#methods ) have their own symbol (^).\n- Line numbers are a thing.\n\n## examples\nThe following examples may give you an idea what Sharpl is currently capable of:\n\n- [Advent of Code 2023](https://github.com/codr7/sharpl/tree/main/examples/aoc23)\n- [Fire](https://github.com/codr7/sharpl/tree/main/examples/fire)\n- [Remote REPL](https://github.com/codr7/sharpl/tree/main/examples/remote-repl)\n\n## bindings\nBindings come in two flavors, with lexical or dynamic scope.\n\n### lexical scope\nNew lexically scoped bindings may be created using `let`.\n\n```\n(let [x 1 \n      y (+ x 2)]\n  y)\n```\n`3`\n\n### dynamic scope\nNew dynamically scoped bindings may be created using `var`.\n\n```\n(var foo 35)\n  \n(^bar []\n  foo)\n\n(let [foo (+ foo 7)]\n  (bar))\n```\n`42`\n\n### destructuring\nBindings support declarative destructuring of pairs.\n\n```\n(let [_:r:rr 1:2:3] r:rr)\n```\n`2:3`\n\n### updates\n`set` may be used to update a binding.\n\n```\n(let [foo 1 bar 2]\n  (set foo 3 bar 4)\n  foo:bar)\n```\n`3:4`\n\n## branching\n`if` may be used to conditionally evaluate a block of code.\n\n```\n(if T 'is-true)\n```\n`'is-true`\n\n`else` may be used to specify an else-clause.\n\n```\n(else F 1 2)\n```\n`2`\n\n## methods\nNew methods may be defined using `^`.\n\n```\n(^foo [x]\n  x)\n\n(foo 42)\n```\n`42`\n\n### lambdas\nLeaving out the name creates a lambda.\n\n```\n(let [f (^[x] x)]\n  (f 42))\n```\n`42`\n\n### closures\nExternal bindings are captured at method definition time.\n\n```\n(^make-countdown [max]\n  (let [n max]\n    (^[] (dec n))))\n\n(var foo (make-countdown 42))\n\n(foo)\n```\n`41`\n\n```\n(foo)\n```\n`40`\n\n### tail calls\n`return` may be used to convert any call into a tail call.\n\nThis example will keep going forever without consuming more space:\n```\n(^foo []\n  (return (foo)))\n\n(foo)\n```\n\nWithout `return` it quickly runs out of space:\n```\n(^foo []\n  (foo))\n\n(foo)\n```\n```\nSystem.IndexOutOfRangeException: Index was outside the bounds of the array.\n```\n\n### varargs\n`methods` may be defined as taking a variable number of arguments by suffixing the last parameter with `*`.\nThe name is bound to an array containing all trailing arguments when the method is called.\n\n```\n(^foo [bar*]\n  (say bar*))\n  \n(foo 1 2 3)\n```\n```\n123\n```\n\n### composition\nMethods may be composed using `\u0026`.\n\n```\n(^foo [x]\n  (+ x 1))\n  \n(^bar [x]\n  (* x 2))\n\n(let [f foo \u0026 bar]\n  (say f)\n  (f 20))\n```\n```\n(foo \u0026 bar [values*])\n```\n`42`\n\n## looping\n### loop\n`loop` does exactly what it says, and nothing more.\n\n```\n(^enumerate [n]\n  (let [result (List) i 0]\n    (loop\n      (push result i)\n      (if (is (inc i) n) (return result)))))\n\n(enumerate 3)\n```\n`[0 1 2]`\n\n### for\n`for` may be used to iterate any number of iterables.\n\n```\n(let [result {}]\n  (for [i [1 2 3] \n        j [4 5 6 7]]\n    (result i j))\n  result)\n```\n`{1:4 2:5 3:6}`\n\n## quoting\nExpressions may be quoted by prefixing with `'`.\n\n### unquoting\n`,` may be used to temporarily decrease quoting depth while evaluating the next form.\n\n```\n(let [bar 42]\n  '[foo ,bar baz])\n```\n\n#### splat\nUnquoting may be combined with `*` to expand in place.\n\n```\n(let [bar 42 \n      baz \"abc\"]\n  '[foo ,[bar baz]* qux])\n```\n`['foo 42 \"abc\" 'qux]`\n\n## value and identity\n`=` may be used to compare values deeply, while `is` compares identity.\u003cbr/\u003e\nFor some types they return the same result; integers, strings, pairs, methods etc.\n\nFor others; like arrays and maps; two values may well be equal despite having different identities.\n```\n(= [1 2 3] [1 2 3])\n```\n`T`\n\n```\n(is [1 2 3] [1 2 3])\n```\n`F`\n\n\n## types\n### nil\nThe `Nil` type has one value (`_`), which represents the absence of a value.\n\n### bits\nThe `Bit` type has two values, `T` and `F`.\n\n### symbols\nSymbols may be created by quoting identifiers or using the type constructor.\n\n```\n(= (Sym 'foo \"bar\") 'foobar)\n```\n`T`\n\n#### unique\n`gensym` may be used to create unique symbols.\n\n```\n  (gensym 'foo)\n\n'7foo\n```\n\n### characters\nCharacter literals may be defined by prefixing with `\\`.\n\n```\n(char/is-digit \\7)\n```\n`T`\n\nSpecial characters require one more escape.\n\n```\n\\\\n\n```\n`\\\\n`\n\nCharacters support ranges.\n\n```\n[\\a..\\z:2*]\n```\n`[\\a \\c \\e \\g \\i \\k \\m \\o \\q \\s \\u \\w \\y]`\n\n### integers\nIntegers support the regular arithmetic operations.\n\n```\n(- (+ 1 4 5) 3 2)\n```\n`5`\n\nNegative integers lack syntax, and must be created by way of subtraction.\n\n```\n(- 42)\n```\n`-42`\n\n\nIntegers support ranges.\n\n```\n[1..10:2*]\n```\n`[1 3 5 7 9]`\n\n`parse-int` may be used to parse integers from strings, it returns the parsed value and the next index in the string.\n\n```\n(parse-int \"42foo\")\n```\n`42:2`\n\n### fixpoints\nDecimal expressions are read as fixpoint values with specified number of decimals.\u003cbr/\u003e\nLike integers, fixpoints support the regular arithmetic operations.\n\n```\n1.234\n```\n`1.234`\n\nLeading zero is optional.\n\n```\n(= 0.123 .123)\n```\n`T`\n\nAlso like integers; negative fixpoints lack syntax, and must be created by way of subtraction.\n\n```\n(- 1.234)\n```\n`-1.234`\n\nFixpoints support ranges.\n\n```\n[1.1..1.4:.1*]\n```\n`[1.1 1.2 1.3]`\n\n## composite types\n\n### pairs\nPairs may be formed by putting a colon between two values.\n\n```\n1:2:3\n```\n`1:2:3`\n\nOr by calling the constructor explicitly.\n\n```\n(Pair 1 2 3)\n```\n`1:2:3`\n\n### arrays\nArrays are fixed size sequences of values.\u003cbr/\u003e\nNew arrays may be created by enclosing a sequence of values in brackets.\n\n```\n[1 2 3]\n```\nOr by calling the constructor explicitly.\n```\n(Array 1 2 3)\n```\n`[1 2 3]`\n\n### strings\nString literals may be defined using double quotes.\n\n`up` and `down` may be used to change case.\n\n```\n(string/up \"Foo\")\n```\n`\"FOO\"`\n\n`split` may be used to split a string on a pattern.\n\n```\n(string/split \"foo bar\" \" \")\n```\n`[\"foo\" \"bar\"]`\n\n`reverse` may be used to reverse a string.\n\n```\n(string/reverse \"foo\")\n```\n`\"oof\"`\n\n`replace` may be used to replace all occurrences of a pattern.\n\n```\n(string/replace \"foo bar baz\" \" ba\" \"\")\n```\n`\"foorz\"`\n\n### lengths\n`#` or `length` may be used to get the length of any composite value.\n\n```\n(= #[1 2 3] (length \"foo\"))\n```\n`T`\n\n### stacks\nPairs, arrays, lists and strings all implement the stack trait.\n\n#### push\n`push` may be used to push a new item to a stack; for pairs it's added to the front, for others to the back.\n\n```\n(let [s 2:3]\n  (push s 1)\n  s)\n```\n`1:2:3`\n\n#### peek\n`peek` may be used to get the top item in a stack.\n\n```\n(peek \"abc\")\n```\n`\\a`\n\n#### pop\n`pop` may be used to remove the top item from a stack.\n\n```\n(let [s [1 2 3]]\n  (pop s):s)\n```\n`3:[1 2]`\n\n### maps\nMaps are ordered mappings from keys to values.\u003cbr/\u003e\nNew maps may be created by enclosing a sequence of pairs in curly braces.\n\n```\n'{foo:1 bar:2 baz:3}\n```\nOr by calling the constructor explicitly.\n```\n(Map 'foo:1 'bar:2 'baz:3)\n```\n`{'bar:2 'baz:3 'foo:1}`\n\n### slicing\nComposite types may be sliced by indexing using a pair.\n\n```\n('{a:1 b:2 c:3 d:4} 'b:'c)\n```\n`{'b:2 'c:3}`\n\n## iterators\n\n### map\n`map` may be used to map a method over one or more iterables, it returns an iterator.\n\n```\n[(map Pair '[foo bar baz] [1 2 3 4])*]\n```\n`['foo:1 'bar:2 'baz:3]`\n\n### filter\n`filter` returns an iterator for items matching the specified predicate.\n\n```\n[(filter (^[x] (\u003e x 2)) [1 2 3 4 5])*]\n```\n`[3 4 5]`\n\n### reduce\n`reduce` may be used to transform any iterable into a single value. \n\n```\n(reduce - [2 3] 0)\n```\n`1`\n\n### sum\n`sum` is provided as a shortcut.\n\n```\n(sum 1 2 3)\n```\n`10`\n\n### zip\n`zip` may be used to braid any number of iterables.\n\n```\n[(zip '[foo bar] '[1 2 3] [T F])*]\n```\n`['foo:1:T 'bar:2:F]`\n\n### enumerate\n`enumerate` may be used to zip any iterable with indexes.\n\n```\n[(enumerate 42 '[foo bar])*]\n```\n`[42:'foo 43:'bar]`\n\n### find\n`find-first` may be used to find the first value in an iterable matching the specified predicate, along with its index; or `_` if not found.\n\n```\n(find-first (^[x] (\u003e x 3)) [1 3 5 7 9])\n```\n`5:2`\n\n## user defined types\n### traits\n`trait` may be used to define abstract types.\u003cbr/\u003e\n\u003cbr/\u003e\nSupers are required to be traits.\n\n```\n(trait Foo)\n(trait Bar Foo)\n(\u003e Bar Foo)\n```\n`T`\n\n### data\n`type` may be used to define new types.\u003cbr/\u003e\n\u003cbr/\u003e\nA default constructor is automatically generated if not specified.\n\n```\n(data Foo Int)\n\n(let [x (Foo 2)]\n  (say x)\n  (say (+ x 3))\n```\n```\n(Foo 2)\n3\n```\n \nSpecifying a constructor allows customizing the instantiation.\n\n```\n(type Bar Map\n  (^[x y z] {x:1 y:2 z:3}))\n\n(let [bar (Bar 'a 'b 'c)]\n  (say bar)\n  (say (bar 'b)))\n```\n```\n(Bar {'a:1 'b:2 'c:3})\n2\n```\n\n## errors\n`fail` may be used to signal an error.\u003cbr/\u003e\n\u003cbr/\u003e\nThe first argument is the error type (default `Error`); remaining arguments are passed to the constructor, which by default concatenates a message.\n\n```\n(fail _ 'bummer)\n```\n```\nSharpl.UserError: repl@1:2 bummer\n   at Sharpl.Libs.Core.\u003c\u003ec.\u003c.ctor\u003eb__27_22(VM vm, List`1 stack, Method target, Int32 arity, Loc loc) in /home/codr7/Code/sharpl/src/Sharpl/Libs/Core.cs:line 433\n   at Sharpl.Method.Call(VM vm, List`1 stack, Int32 arity, Loc loc) in /home/codr7/Code/sharpl/src/Sharpl/Method.cs:line 24\n   at Sharpl.VM.Eval(Int32 startPC, List`1 stack) in /home/codr7/Code/sharpl/src/Sharpl/VM.cs:line 271\n```\n\n### handling\n`try` may be used to register error handlers for a block of code, handlers are checked in specified order when an error occurs.\n\n```\n(try [Any:(^[_] (say 'inside-error-handler))] \n  (say 'before)\n  (fail _ 'bummer)\n  (say 'after))\n\n(say 'done)\n```\n```\nbefore\ninside-error-handler\ndone\n```\n\n### location\n`LOC` may be used to get the error source location inside handlers.\n\n```\n(try [Any:(^[e] LOC)] \n  (fail _))\n```\n`repl@2:4`\n\n### custom errors\nAny type may be used as an error.\n\n```\n(trait Foo)\n\n(data Bar [Pair Foo] \n  (^[x y z] x:y:z))\n\n(try [Foo:(^[e] e)]\n  (fail Bar 3 2 1))\n```\n`(Bar 1:2:3)`\n\n### restarts\n```\n(+ 'foo 1)\n```\n```\nSharpl.NonNumericError: repl@1:2 Expected numeric value: 'foo\n1 use-value\n2 stop\nPick an alternative (1-2) and press ⏎: 1\nEnter new value: 42\n```\n`43`\n\n## deferred actions\nActions may be registered to run unconditionally at scope exit using `defer`.\nDeferred actions are evaluated last in first out.\n\n```\n(do\n  (say 'before)\n  (defer (^[] (say 'defer)))\n  (say 'after))\n```\n```\nbefore\nafter\ndefer\n```\n\n## libraries\n`lib` may be used to define/extend libraries.\n\n```\n(lib foo\n  (var bar 42))\n\nfoo/bar\n```\n`42`\n\nWhen called with one argument, it specifies the current library for the entire file.\n\n```\n(lib foo)\n(var bar 42)\n```\n\nAnd when called without arguments, it returns the current library.\n\n```\n(lib)\n```\n`(Lib user)`\n\n## evaluation\n\n### dynamic\n`eval` may be used to dynamically evaluate code at runtime.\n\n```\n(eval '(+ 1 2))\n```\n`3`\n\n### static\n`emit` may be used to evaluate code once as it is emitted.\n\n```\n(emit '(+ 1 2))\n```\n`3`\n\n## time\nThe time library uses established naming conventions when referring to fields: `Y` for years, `M` for months, `D` for days, `h` for hours, `m` for minutes, `s` for seconds, `ms` for milliseconds and `us` for microseconds.\n\n`time/today` and `time/now` may be used to get the current date/time.\n\n```\n(is (time/trunc (time/now)) (time/today))\n```\n`T`\n\nThe `Timestamp` constructor may be used to create new timestamps manually, pass `_` for default.\n\n```\n  (Timestamp 2024 _ _ 21 22)\n```\n`2024-01-01 21:22:00`\n\n### durations\nSubtracting timestamps results in a duration.\n\n```\n  (- (time/now) (time/today))\n```\n`16:36:27.2404435`\n\nSuffixes may be used as constructors.\n\n```\n(is (time/m 120) (time/h))\n```\n`T`\n\nWhen applied to timestamps or durations they return the value for the specified field.\n\n```\n(time/D (time/W 2))\n```\n`14`\n\nDurations may be added/subtracted to/from timestamps.\n\n```\n(+ (time/today) (time/m 90))\n```\n`2024-09-15 01:30:00`\n\n## ranges\nTimestamps support ranges.\u003cbr/\u003e\nThe following example generates timestamps between `2024-1-1` and the next day, separated by six hours.\n\n```\n(let [t (Timestamp 2024 1 1)]\n  [t..(+ t (time/D 1)):(time/h 6)*])\n```\n`[2024-01-01 00:00:00 2024-01-01 06:00:00 2024-01-01 12:00:00 2024-01-01 18:00:00]`\n\n### months\n`MONTHS` maps indexes to month names.\n\n```\ntime/MONTHS\n```\n`[_ 'jan 'feb 'mar 'apr 'may 'jun 'jul 'aug 'sep 'oct 'nov 'dec]`\n\n### weekdays\n`WEEKDAYS` maps indexes to week day names.\n\n```\ntime/WEEKDAYS\n```\n`['su 'mo 'tu 'we 'th 'fr 'sa]`\n\n### time zones\nBy default all timestamps are local, `time/to-utc` and `time/from-utc` may be used to convert back and forth.\n\n```\n(let [t (time/now)]\n  (is (time/to-local (time/to-utc t)) t))\n```\n`T`\n\n## communication\n### pipes\nPipes are unbounded, thread safe communication channels. Pipes may be called without arguments to read and with to write.\n\n### ports\nPorts are bidirectional communication channels. Like pipes, ports may be called without arguments to read and with to write.\n\n### polling\n`poll` returns the first argument that's ready for reading.\n\n```\n(let [p1 (Pipe) p2 (Pipe)]\n  (p2 42)\n  ((poll [p1 p2])))\n```\n`42`\n\n## threads\n`spawn` may be used to start new threads, each thread runs in a separate VM. A port is created for communication, one side passed as argument and the other returned.\n\n```\n(let [p (spawn [p] (p 42))]\n  (p))\n```\n\n## json\n`json/encode` and `json/decode` may be used to convert values to/from [JSON](https://www.json.org/json-en.html).\n\n```\n(let [dv {'foo:42 'bar:.5 'baz:\"abc\" 'qux:[T F _]}\n      ev (json/encode dv)]\n  ev:(= (json/decode ev) dv))\n```\n`\"{\\\"bar\\\":0.5,\\\"baz\\\":\\\"abc\\\",\\\"foo\\\":42,\\\"qux\\\":[true,false,null]}\":T`\n\n## terminal control\nThe terminal control library is intented as a portable foundation that provides all the bits and pieces needed to build a full TUI.\n\n### keys\nThe following standard keys are defined as constants:\n- `DOWN`\n- `LEFT`\n- `ENTER`\n- `ÈSC`\n- `RIGHT`\n- `SPACE`\n- `UP`\n\n`poll-key` may be used to check if a key is available.\n\n```\n(term/poll-key)\n```\n`F`\n\n`read-key` may be used to read the next key press, echoing is disabled by default but may be turned on by passing `T`.\n\n```\n(term/read-key T)\n```\n`(term/Key Enter)`\n\n`ask` may be used to read a line of input with optional prompt and/or echo (enabled by default).\n\n```\n(ask \"Enter password\" \\*)\n```\n```\nEnter password: ***\n```\n`\"abc\"`\n\n## tests\n`check` fails with an error if the result of evaluating its body isn't equal to the specified value.\n```\n(check 5\n  (+ 2 2))\n```\n```\nSharpl.EvalError: repl@1:2 Check failed: expected 5, actual 4!\n```\n\nWhen called with a single argument, it simply checks if it's true.\n```\n(check 0)\n```\n```\nSharpl.EvalError: repl@1:2 Check failed: expected T, actual 0!\n```\n\nHave a look at the [test suite](https://github.com/codr7/sharpl/blob/main/tests/all-tests.sl) for examples.\n\n## benchmarks\n`bench` may be used to measure the number of milliseconds it takes to repeat a block of code N times with warmup.\n\n```\ndotnet run -c=release benchmarks/fib.sl\n```\n\n## building\n`dotnet publish` may be used to build a standalone executable.\n\n```\n$ dotnet publish\nMSBuild version 17.9.8+b34f75857 for .NET\n  Determining projects to restore...\n  Restored ~/sharpl/sharpl.csproj (in 324 ms).\n  sharpl -\u003e ~/sharpl/bin/Release/net8.0/linux-x64/sharpl.dll\n  Generating native code\n  sharpl -\u003e ~/sharpl/bin/Release/net8.0/linux-x64/publish/\n```\n\n## debugging\n`dmit` may be used to display the VM operations emitted for an expression.\n\n```\n(dmit '(+ 1 2))\n```\n```\n9    Push 1\n10   Push 2\n11   CallMethod (Method + []) 2 False\n```\n\n## contributing\nContributions are very welcome, feel free to submit pull requests.\u003cbr/\u003e\nOr even better, register a GitHub issue describing the change and allow us to make sure we agree it's a good idea first.\n\n- Fork the project.\n- Create a feature branch (`git checkout -b issue-x`).\n- Commit your changes (`git commit -m '...'`).\n- Push to the branch (`git push origin issue-x`).\n- Open a pull request.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcodr7%2Fsharpl","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcodr7%2Fsharpl","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcodr7%2Fsharpl/lists"}