{"id":13440848,"url":"https://github.com/microsoft/schemy","last_synced_at":"2026-01-14T04:56:31.813Z","repository":{"id":65976156,"uuid":"127793432","full_name":"microsoft/schemy","owner":"microsoft","description":"A lightweight embeddable Scheme-like interpreter for configuration ","archived":true,"fork":false,"pushed_at":"2022-09-30T07:54:32.000Z","size":52,"stargazers_count":306,"open_issues_count":7,"forks_count":43,"subscribers_count":12,"default_branch":"master","last_synced_at":"2025-12-27T07:40:59.440Z","etag":null,"topics":["configuration","csharp","dotnet","interpreter","scheme","scripting-language"],"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/microsoft.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-04-02T18:06:14.000Z","updated_at":"2025-11-25T19:30:54.000Z","dependencies_parsed_at":"2023-02-19T19:31:08.241Z","dependency_job_id":null,"html_url":"https://github.com/microsoft/schemy","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/microsoft/schemy","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/microsoft%2Fschemy","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/microsoft%2Fschemy/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/microsoft%2Fschemy/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/microsoft%2Fschemy/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/microsoft","download_url":"https://codeload.github.com/microsoft/schemy/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/microsoft%2Fschemy/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28409928,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T01:52:23.358Z","status":"online","status_checked_at":"2026-01-14T02:00:06.678Z","response_time":107,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["configuration","csharp","dotnet","interpreter","scheme","scripting-language"],"created_at":"2024-07-31T03:01:26.954Z","updated_at":"2026-01-14T04:56:31.796Z","avatar_url":"https://github.com/microsoft.png","language":"C#","readme":"# Schemy\n\nSchemy is a lightweight Scheme-like scripting language interpreter for\nembedded use in .NET applications. It's built from scratch without any\nexternal dependency.  Its primary goal is to serve as a highly flexible\nconfiguration language.  Example scenarios are to describe computational\ngraph, workflow, or to represent some complex configuration.\n\nIts design goals are:\n\n* easy to embed and extend in .NET\n* extensible in Scheme via macro expansion\n* safe without the need of complicated AppDomain sandboxing. It's safe because\n  IO functions are not exposed by default\n* runs reasonably fast and low memory footprint\n\nNon-goals:\n\n* be highly optimized - it's designed to load configurations and not part of \n  any heavy computation, so being optimized is not the goal - e.g., there's no\n  JIT compiling, etc.\n\n\nSchemy's implementation is inspired by Peter Norvig's [article on Lisp\ninterpreter][lispy], but is heavily adapted to .NET and engineered to be easily\nextensible and embeddable in .NET applications.\n\n\n## Language Features\n\nIt has most features that a language would support:\n\n* number, boolean, string, list types\n* variable, function definition\n* tail call optimization\n* macro definition\n* lexical scoping\n\n\nMany Scheme features are not (yet) supported. Among those are:\n\n* continuation (`call/cc`)\n* use square brackets `[...]` in place of parenthesis `(...)`\n\n\n## Why Schemy\n\nSchemy was originally designed at Microsoft 365 to define complex machine\nlearning model workflows that handle web API requests. Since Schemy scripts\ncan be easier to develop, modify, and deploy than full fledged .NET\napplication or modules, the development and maintenance become more agile, and\nconcerns are better separated - request routing is handled by web server,\nrequest handling logics are defined by Schemy scripts. The\n[`command_server`](src/examples/command_server/) example application captures\nthis design in a very simplified way.\n\nMore generically, for applications that require reading configuration, the\nusual resort is to configuration languages like JSON, XML, YAML, etc. While\nthey are simple and readable, they may not be suitable for more complex\nconfiguration tasks, when dynamic conditioning, modularization, reusability\nare desired. For example, when defining a computational graph whose components\ndepend on some runtime conditions, and when the graph is desirable to be\ncomposed of reusable sub-graphs.\n\nThe other end of the spectrum is to use full fledged scripting languages like\nPython (IronPhython), Lua (NLua), etc. While they are more flexible and\npowerful, footprint for embedding them can be heavy, and they pull in\ndependencies to the host application.\n\nSchemy can be seen as sitting in the middle of the spectrum:\n\n-   It provides more languages features for conditioning,\n    modularization/reusability (functions, scripts), customization (macro),\n    then simple configuration languages.\n\n-   It's simpler in implementation (~1500 lines of code) and doesn't pull in\n    extra dependencies and have a smaller footprint to the host application.\n\n-   It can be safer in the sense that it doesn't provide access to file system\n    by default. Although we run it in fully trusted environment, this could be\n    useful as it doesn't need to be sandboxed. (IO is supported by [virtual\n    file system](#virtualfs).)\n\n\n## Usage\n\nTo reference `schemy.dll`, either install it via [Nuget\n(schemy)][schemy_nuget], or [build it from source](#build).\n\nAlternatively, you can just copy `src/schemy/*.cs` source code to include in\nyour application. Since Schemy code base is small. This approach is very\nfeasible (don't forget to also include the resource file `init.ss`).\n\n\n\u003ca id=\"build\"\u003e\u003c/a\u003e\n## Build\n\nSchemy does not take any external dependency. So building it should be\nstraightfoward: simply run `msbuild` in `src/`, or use your favorite IDE.\n\nThe project structure looks like so:\n\n    ├───doc\n    └───src\n        ├───schemy              // the core schemy interpreter (schemy.dll)\n        ├───examples\n        │   ├───command_server  // loading command handlers from schemy scripts\n        │   └───repl            // an interactive interpreter (REPL)\n        └───test\n\n\n## Embedding and Extending Schemy\n\n\nThe below sections describes how to embed and extend Schemy in .NET\napplications and in Scheme scripts. For a comprehensive example, please refer\nto [`src/examples/command_server`](src/examples/command_server).\n\n\n### Extending Schemy in .NET\n\nSchemy can be extended by feeding the interpreter symbols with predefined\n.NET objects. Variables could be any .NET type. Procedures\nmust implement `ICallable`.\n\nAn example procedure implementation:\n\n```csharp\nnew NativeProcedure(args =\u003e args, \"list\");\n```\n\nThis implements the Scheme procedure `list`, which converts its arguments \ninto a list:\n\n    schemy\u003e (list 1 2 3 4)\n    (1 2 3 4)\n\n\n`NativeProcedure` has convenient factory methods that handles input type\nchecking and conversion, for example, `NativeProcedure.Create\u003cdouble, double, bool\u003e()`\ntakes a `Func\u003cdouble, double bool\u003e` as the implementation, and handles the\nargument count checking (must be 2) and type conversion (`(double, double) -\u003e bool`):\n\n```csharp\nbuiltins[Symbol.FromString(\"=\")] = NativeProcedure.Create\u003cdouble, double, bool\u003e((x, y) =\u003e x == y, \"=\");\n```\n\nTo \"register\" extensions, one can pass them to the `Interpreter`'s\nconstructor:\n\n```csharp\nInterpreter.CreateSymbolTableDelegate extension = itpr =\u003e new Dictionary\u003cSymbol, object\u003e\n{\n    { Symbol.FromString(\"list\"), new NativeProcedure(args =\u003e args, \"list\") },\n};\n\nvar interpreter = new Interpreter(new[] { extension });\n```\n\n\n### Extending Schemy in Scheme\n\nWhen launched, the interpreter tries to locate and load Scheme file `.init.ss`\nin the same directory as the executing assembly. You can extend Schemy by\nputting function, variable, macro definition inside this file.\n\n\n#### Extending with functions\n\nFor example, this function implements the standard Scheme list reversion\nfunction `reverse` (with proper tail call optimization):\n\n```scheme\n(define (reverse ls)\n  (define loop\n    (lambda (ls acc)\n      (if (null? ls) acc\n        (loop (cdr ls) (cons (car ls) acc)))))\n  (loop ls '()))\n```\n\nUse it like so:\n\n```nohighlight\nSchemy\u003e (reverse '(1 2 \"foo\" \"bar\"))\n(\"bar\" \"foo\" 2 1)\n```\n\n\n#### Syntax augmentation in Scheme\n\nFor example, we want to augment Schemy with a new syntax for local variable\ndefinition, [`let`][schemepl]. Here's what we want to achieve:\n\n```nohighlight\nSchemy\u003e (let ((x 1)     ; let x = 1\n              (y 2))    ; let y = 2\n          (+ x y))      ; evaluate x + y\n3\n```\n\nThe following macro implements the `let` form by using lambda invocation:\n\n```scheme\n(define-macro let\n  (lambda args\n    (define specs (car args))  ; ((var1 val1), ...)\n    (define bodies (cdr args)) ; (expr1 ...)\n    (if (null? specs)\n      `((lambda () ,@bodies))\n      (begin\n        (define spec1 (car specs)) ; (var1 val1)\n        (define spec_rest (cdr specs)) ; ((var2 val2) ...)\n        (define inner `((lambda ,(list (car spec1)) ,@bodies) ,(car (cdr spec1))))\n        `(let ,spec_rest ,inner)))))\n```\n\n\n\u003ca id=\"repl\"\u003e\u003c/a\u003e\n## Use Interactively (REPL)\n\nThe interpreter can be run interactively, when given a `TextReader` for input\nand a `TextWriter` for output. The flexibility of this interface means you can\nnot only expose the REPL via stdin/stdout, but also any streamable channels,\ne.g., a socket, or web socket (please consider security!).\n\n```csharp\n/// \u003csummary\u003eStarts the Read-Eval-Print loop\u003c/summary\u003e\n/// \u003cparam name=\"input\"\u003ethe input source\u003c/param\u003e\n/// \u003cparam name=\"output\"\u003ethe output target\u003c/param\u003e\n/// \u003cparam name=\"prompt\"\u003ea string prompt to be printed before each evaluation\u003c/param\u003e\n/// \u003cparam name=\"headers\"\u003ea head text to be printed at the beginning of the REPL\u003c/param\u003e\npublic void REPL(TextReader input, TextWriter output, string prompt = null, string[] headers = null)\n```\n\nThis can be useful for expose a remote \"shell\" for the application, or as\ndebugging purposes (see how `src/examples/command_server/` uses the `--repl`\ncommand line argument).\n\nThere is an example REPL application in\n[`src/examples/repl/`](src/examples/repl/) that can be started as a REPL\ninterpreter:\n\n    $ schemy.repl.exe\n    -----------------------------------------------\n    | Schemy - Scheme as a Configuration Language |\n    | Press Ctrl-C to exit                        |\n    -----------------------------------------------\n\n    Schemy\u003e (define (sum-to n acc)\n               (if (= n 0) \n                  acc \n                  (sum-to (- n 1) (+ acc n))))\n\n    Schemy\u003e (sum-to 100 0)\n    5050\n\n    Schemy\u003e (sum-to 10000 0)  ; proper tail call optimization prevents stack overflow\n    50005000\n\nRun a script:\n\n    $ schemy.repl.exe \u003csome_file\u003e\n\n\n\n\u003ca id=\"virtualfs\"\u003e\u003c/a\u003e\n## Virtual File System\n\nThe interpreter's constructor takes a `IFileSystemAccessor`:\n\n```csharp\npublic interface IFileSystemAccessor\n{\n    /// \u003csummary\u003e\n    /// Opens the path for read\n    /// \u003c/summary\u003e\n    /// \u003cparam name=\"path\"\u003eThe path\u003c/param\u003e\n    /// \u003creturns\u003ethe stream to read\u003c/returns\u003e\n    Stream OpenRead(string path);\n\n    /// \u003csummary\u003e\n    /// Opens the path for write\n    /// \u003c/summary\u003e\n    /// \u003cparam name=\"path\"\u003eThe path\u003c/param\u003e\n    /// \u003creturns\u003ethe stream to write\u003c/returns\u003e\n    Stream OpenWrite(string path);\n}\n```\n\nThere're two builtin implementations: a `DisabledFileSystemAccessor`, which\nblocks read/write, a `ReadOnlyFileSystemAccessor`, which provides readonly to\nlocal file system. The default behavior for an interpreter is\n`DisabledFileSystemAccessor`.\n\nIn addition to them, you can implement your own file system accessors. For\nexample, you could implement it to provide access into a Zip archive, treating\neach zip archive entry as a file in a file system.\n\n\n# Contributing\n\nThis project welcomes contributions and suggestions.  Most contributions require you to agree to a\nContributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us\nthe rights to use your contribution. For details, visit https://cla.microsoft.com.\n\nWhen you submit a pull request, a CLA-bot will automatically determine whether you need to provide\na CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions\nprovided by the bot. You will only need to do this once across all repos using our CLA.\n\nThis project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).\nFor more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or\ncontact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.\n\n\n\n[schemepl]: http://www.scheme.com/tspl4/start.html#./start:h4\n[lispy]: http://norvig.com/lispy2.html\n[schemy_nuget]: https://www.nuget.org/packages/schemy\n\u003c!--- vim: set ft=markdown tw=78: --\u003e\n\n","funding_links":[],"categories":["C# #","C\\#"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmicrosoft%2Fschemy","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmicrosoft%2Fschemy","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmicrosoft%2Fschemy/lists"}