{"id":13442758,"url":"https://github.com/eudoxia0/cmacro","last_synced_at":"2025-12-17T03:04:45.410Z","repository":{"id":16279641,"uuid":"19028064","full_name":"eudoxia0/cmacro","owner":"eudoxia0","description":"Lisp macros for C","archived":false,"fork":false,"pushed_at":"2024-02-05T20:22:09.000Z","size":203,"stargazers_count":887,"open_issues_count":5,"forks_count":29,"subscribers_count":44,"default_branch":"master","last_synced_at":"2024-12-23T05:12:02.659Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Common Lisp","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/eudoxia0.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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":"2014-04-22T12:03:25.000Z","updated_at":"2024-11-26T05:09:59.000Z","dependencies_parsed_at":"2024-10-28T04:00:51.356Z","dependency_job_id":"9d22d250-c617-4612-be49-a7cfa8d4ab33","html_url":"https://github.com/eudoxia0/cmacro","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eudoxia0%2Fcmacro","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eudoxia0%2Fcmacro/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eudoxia0%2Fcmacro/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eudoxia0%2Fcmacro/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/eudoxia0","download_url":"https://codeload.github.com/eudoxia0/cmacro/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":238950457,"owners_count":19557533,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":"2024-07-31T03:01:50.223Z","updated_at":"2025-10-30T10:30:21.436Z","avatar_url":"https://github.com/eudoxia0.png","language":"Common Lisp","funding_links":[],"categories":["Common Lisp","Other projects"],"sub_categories":[],"readme":"# cmacro: Lisp macros for C\n\n[![Build Status](https://travis-ci.org/eudoxia0/cmacro.svg)](https://travis-ci.org/eudoxia0/cmacro)\n\n**For a collection of useful macros, see the associated [Magma](https://github.com/eudoxia0/magma) project**\n\n# Usage\n\nMacros are written directly in the source, and the `cmc` program is used to\nprocess a file with macros to a macroexpanded file.\n\n```bash\ncmc code_with_macros.c -o macroexpanded_code.c\n```\n\n# Installing\n\nIf you're running Arch or a similarly bleeding-edge distro, just install `sbcl`\nfrom Pacman and skip to step 5. Otherwise, you need to manually download the\nlatest SBCL[1].\n\n1. [Download SBCL](http://www.sbcl.org/platform-table.html)\n2. Unpack it, for example, through `bzip2 -cd sbcl-1.1.17-x86-linux-binary.tar.bz2 | tar xvf -`\n3. Install git, curl and flex through your favorite package manager.\n4. Build SBCL: `cd \u003csbcl dir\u003e; sudo sh install.sh`\n5. Build cmacro: `make`, `sudo make install`\n\n[1]: [Buildapp](http://www.xach.com/lisp/buildapp/) doesn't work on older\nversions of SBCL, and it is required to build the executable.\n\n# What?\n\nA macro is a function that operates on your code's abstract syntax tree rather\nthan values. Macros in cmacro have nothing to do with the C preprocessor except\nthey happen at compile time, and have no knowledge of run-time values.\n\nIn cmacro, a macro maps patterns in the code to templates. A macro may have\nmultiple cases, each matching multiple patterns, but each producing code through\nthe same template.\n\nMacros are not primarily about safety and performance: They are about the\nprogrammer. Macros give you automation, plain and simple. They allow you to\nabstract away and remove repetition in places where a functional or\nobject-oriented approach can't. For example, Common Lisp's\n[WITH-OPEN-FILE](http://clhs.lisp.se/Body/m_w_open.htm) macro helps with the\ncommon pattern of 'acquire a resource, apply something to it, and close\nit'. While this can be done in languages that support (And have simple syntax\nfor) anonymous functions, macros help reduce this syntactic overhead.\n\ncmacro has a very lenient notion of C syntax, which means you can write macros\nto implement DSLs with any syntax you like. You could implement Lisp-like prefix\nnotation, or a DSL for routing URLs, or the decorator pattern, for example.\n\nFor a very simple example, this macro matches anything of the form `unless\n\u003ccond\u003e`, where `\u003ccond\u003e` is any arbitrary expression, and performs a simple\ntransformation:\n\n```c\nmacro unless {\n  case {\n    match {\n      $(cond)\n    }\n    template {\n      if(!$(cond))\n    }\n  }\n}\n```\n\nWith this definition, code like `unless(buffer.empty)` becomes `if(!(buffer.empty))`.\n\nA more complicated macro can match multiple patterns, like the `route` macro\nwhich implements a DSL for defining routes in a hypothetical C web framework.\n\n```c\nmacro route {\n  /* Route all requests to 'url' to 'route'. Optionally discriminate by HTTP\n  method (GET by default). */\n  case {\n    match {\n      $(url) =\u003e $(route)\n    }\n    template {\n      register_route($(url), $(route), HTTP_GET);\n    }\n  }\n  case {\n    match {\n      $(url) [$(method)] =\u003e $(route)\n    }\n    template {\n      register_route($(url), $(route), $(method));\n    }\n  }\n}\n\n// Usage with the lambda macro (See below)\nroute \"/profile/\u003cuser\u003e\" =\u003e\n  lambda(Req* request) -\u003e Resp { return Authenticate(request.user); }\n```\n\n# Why?\n\nBecause a language without macros is a tool: You write applications with it. A\nlanguage with macros is building material: You shape it and grow it *into* your\napplication.\n\nThere is a sweet spot between low-level performance and control and high-level\nmetaprogramming that is not yet occupied by any language: Metaprogramming, being\nan inherently compile-time thing, can be done in the absence of automatic memory\nmanagement or dynamic typing. [Rust](http://www.rust-lang.org/) seems to want to\nfill this spot, and I also approached this problem with\n[Corvus](https://github.com/eudoxia0/corvus), but I feel this approach of adding\nmetaprogramming to C - A simple language, with a long history, that runs truly\neverywhere - can become useful.\n\n# Examples\n\n## `lambda`\n\n```c\nmacro lambda {\n  case {\n    match {\n      $(args) -\u003e $(ret) $(body)\n    }\n    template {\n      $(@getsym lambda 0)\n    }\n    toplevel {\n      $(ret) $(@gensym lambda) $(args) $(body)\n    }\n  }\n}\n```\n\nUsage:\n\n```c\n/* Input */\nfn = lambda (int x, int y) -\u003e int { return x + y; };\n\n/* After macroexpansion */\nint cmacro_lambda_0(int x, int y) { return x + y; }\n\nfn = cmacro_lambda_0;\n```\n\nA more complicated example, using the `qsort` function:\n\n```c\nint main() {\n  int array[] = {423, 61, 957, 133, 969,\n                 829, 821, 390, 704, 596};\n\n  qsort(array, 10, sizeof(int),\n        lambda (const void* a, const void* b) -\u003e int\n        { return *(int*)a - *(int*)b; });\n  for(size_t i = 0; i \u003c 10; i++){\n    printf(\"%i \", array[i]);\n  }\n  return 0;\n}\n```\n\n## Anaphoric `if`\n\nThis stores the result of the condition in the variable `it`. See\n[Anaphora](http://common-lisp.net/project/anaphora/) for a collection of similar\nanaphoric macros.\n\n```c\nmacro aif {\n  case {\n    match {\n      $(cond)\n    }\n    template {\n      typeof($(cond)) it = $(cond);\n      if(it)\n    }\n  }\n}\n```\n\nUsage:\n\n```c\n/* Input*/\naif(get_buffer(a,b,c)) {\n  write_string(it, text);\n}\n\n/* After macroexpansion */\ntypeof(get_buffer(a,b,c)) it = get_buffer(a,b,c);\nif(it) {\n  write_string(it, text);\n}\n```\n\n## `forEach`\n\n```c\nmacro forEach {\n  case {\n    match {\n      ($(item), $(collection)) $(body)\n    }\n    template {\n      {\n        size_t index;\n        typeof($(collection)[0]) $(item);\n        for(index = 0, item = nth($(collection), 0);\n            index \u003c length($(collection));\n            index++)\n        $(body)\n      }\n    }\n  }\n}\n```\n\n# Variables\n\nThe syntax for variables is just a name followed by an optional,\nspace-separated list of *qualifiers*, enclosed in the `$()` operator, eg:\n`$(var)`, `$(body ident)`, `$(arg const)`.\n\n## Qualifiers\n\n- None: The variable matches any expression.\n- `rest`: Match multiple expressions (Like C's `...`).\n- `ident`: Matches Identifiers.\n- `int`: Integers.\n- `float`: Floats.\n- `num`: Integers and floats.\n- `string`: String literals.\n- `const`: The equivalent of `(or int float string)`.\n- `op`: Operators.\n- `list`, `array`, `block`: Matches expressions of the form `(...)`, `[...]`,\n  `{...}`.\n\n# Template operations\n\nThese use regular variable syntax but the text starts with a '@'.\n\n- `gensym \u003clabel\u003e`: Generates a unique identifier associated with `label`.\n- `getsym \u003clabel\u003e [n]`: Gets the latest identifier associated with `label`, or\n  optionally the `n`-th last identifier.\n- `to-string \u003cvar\u003e`: Since string literals in C can contain variable notation,\n  you have to explicity use this to stringify a variable. Note, also, that C\n  concatenates string that appear next to each other in the source.\n- `splice \u003cvar\u003e`: If `var` is a block (ie `(...)`, `[...]`, `{...}`) this\n  expression removes the block separators, leaving just the block body.\n\n# Acknowledgments\n\nThe [lex](http://www.quut.com/c/ANSI-C-grammar-l-2011.html) and\n[yacc](http://www.quut.com/c/ANSI-C-grammar-y.html) grammars were originally\nposted by Jeff Lee in 1985, and rescued and updated to the recent standards by\n[Jutta Degener](mailto:jutta@pobox.com).\n\nThe syntax for macro definition was inspired by Mozilla's great\n[sweet.js](http://sweetjs.org/) library. Originally I considered multiple\ndifferent ways of defining them, including external YAML files or just piping\nthe AST to an XSLT or similar program, but this seemed like the best way.\n\nThe Makefile is largely based on that of\n[Dimitri Fontaine](http://tapoueh.org/)'s\n[pgloader](https://github.com/dimitri/pgloader) utility.\n\nPeter Norvig's **Paradigms of Artificial Intelligence Programming** chapter on\nEliza was used as a reference for the pattern-matching engine.\n\n# License\n\nCopyright (c) 2014-2015 Fernando Borretti (eudoxiahp@gmail.com)\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software is furnished to do so,\nsubject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\nFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\nIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feudoxia0%2Fcmacro","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Feudoxia0%2Fcmacro","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feudoxia0%2Fcmacro/lists"}