• Stars
    star
    885
  • Rank 51,582 (Top 2 %)
  • Language
    Common Lisp
  • Created over 10 years ago
  • Updated 9 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Lisp macros for C

cmacro: Lisp macros for C

Build Status

For a collection of useful macros, see the associated Magma project

Usage

Macros are written directly in the source, and the cmc program is used to process a file with macros to a macroexpanded file.

cmc code_with_macros.c -o macroexpanded_code.c

Installing

If you're running Arch or a similarly bleeding-edge distro, just install sbcl from Pacman and skip to step 5. Otherwise, you need to manually download the latest SBCL[1].

  1. Download SBCL
  2. Unpack it, for example, through bzip2 -cd sbcl-1.1.17-x86-linux-binary.tar.bz2 | tar xvf -
  3. Install git, curl and flex through your favorite package manager.
  4. Build SBCL: cd <sbcl dir>; sudo sh install.sh
  5. Build cmacro: make, sudo make install

[1]: Buildapp doesn't work on older versions of SBCL, and it is required to build the executable.

What?

A macro is a function that operates on your code's abstract syntax tree rather than values. Macros in cmacro have nothing to do with the C preprocessor except they happen at compile time, and have no knowledge of run-time values.

In cmacro, a macro maps patterns in the code to templates. A macro may have multiple cases, each matching multiple patterns, but each producing code through the same template.

Macros are not primarily about safety and performance: They are about the programmer. Macros give you automation, plain and simple. They allow you to abstract away and remove repetition in places where a functional or object-oriented approach can't. For example, Common Lisp's WITH-OPEN-FILE macro helps with the common pattern of 'acquire a resource, apply something to it, and close it'. While this can be done in languages that support (And have simple syntax for) anonymous functions, macros help reduce this syntactic overhead.

cmacro has a very lenient notion of C syntax, which means you can write macros to implement DSLs with any syntax you like. You could implement Lisp-like prefix notation, or a DSL for routing URLs, or the decorator pattern, for example.

For a very simple example, this macro matches anything of the form unless <cond>, where <cond> is any arbitrary expression, and performs a simple transformation:

macro unless {
  case {
    match {
      $(cond)
    }
    template {
      if(!$(cond))
    }
  }
}

With this definition, code like unless(buffer.empty) becomes if(!(buffer.empty)).

A more complicated macro can match multiple patterns, like the route macro which implements a DSL for defining routes in a hypothetical C web framework.

macro route {
  /* Route all requests to 'url' to 'route'. Optionally discriminate by HTTP
  method (GET by default). */
  case {
    match {
      $(url) => $(route)
    }
    template {
      register_route($(url), $(route), HTTP_GET);
    }
  }
  case {
    match {
      $(url) [$(method)] => $(route)
    }
    template {
      register_route($(url), $(route), $(method));
    }
  }
}

// Usage with the lambda macro (See below)
route "/profile/<user>" =>
  lambda(Req* request) -> Resp { return Authenticate(request.user); }

Why?

Because a language without macros is a tool: You write applications with it. A language with macros is building material: You shape it and grow it into your application.

There is a sweet spot between low-level performance and control and high-level metaprogramming that is not yet occupied by any language: Metaprogramming, being an inherently compile-time thing, can be done in the absence of automatic memory management or dynamic typing. Rust seems to want to fill this spot, and I also approached this problem with Corvus, but I feel this approach of adding metaprogramming to C - A simple language, with a long history, that runs truly everywhere - can become useful.

Examples

lambda

macro lambda {
  case {
    match {
      $(args) -> $(ret) $(body)
    }
    template {
      $(@getsym lambda 0)
    }
    toplevel {
      $(ret) $(@gensym lambda) $(args) $(body)
    }
  }
}

Usage:

/* Input */
fn = lambda (int x, int y) -> int { return x + y; };

/* After macroexpansion */
int cmacro_lambda_0(int x, int y) { return x + y; }

fn = cmacro_lambda_0;

A more complicated example, using the qsort function:

int main() {
  int array[] = {423, 61, 957, 133, 969,
                 829, 821, 390, 704, 596};

  qsort(array, 10, sizeof(int),
        lambda (const void* a, const void* b) -> int
        { return *(int*)a - *(int*)b; });
  for(size_t i = 0; i < 10; i++){
    printf("%i ", array[i]);
  }
  return 0;
}

Anaphoric if

This stores the result of the condition in the variable it. See Anaphora for a collection of similar anaphoric macros.

macro aif {
  case {
    match {
      $(cond)
    }
    template {
      typeof($(cond)) it = $(cond);
      if(it)
    }
  }
}

Usage:

/* Input*/
aif(get_buffer(a,b,c)) {
  write_string(it, text);
}

/* After macroexpansion */
typeof(get_buffer(a,b,c)) it = get_buffer(a,b,c);
if(it) {
  write_string(it, text);
}

forEach

macro forEach {
  case {
    match {
      ($(item), $(collection)) $(body)
    }
    template {
      {
        size_t index;
        typeof($(collection)[0]) $(item);
        for(index = 0, item = nth($(collection), 0);
            index < length($(collection));
            index++)
        $(body)
      }
    }
  }
}

Variables

The syntax for variables is just a name followed by an optional, space-separated list of qualifiers, enclosed in the $() operator, eg: $(var), $(body ident), $(arg const).

Qualifiers

  • None: The variable matches any expression.
  • rest: Match multiple expressions (Like C's ...).
  • ident: Matches Identifiers.
  • int: Integers.
  • float: Floats.
  • num: Integers and floats.
  • string: String literals.
  • const: The equivalent of (or int float string).
  • op: Operators.
  • list, array, block: Matches expressions of the form (...), [...], {...}.

Template operations

These use regular variable syntax but the text starts with a '@'.

  • gensym <label>: Generates a unique identifier associated with label.
  • getsym <label> [n]: Gets the latest identifier associated with label, or optionally the n-th last identifier.
  • to-string <var>: Since string literals in C can contain variable notation, you have to explicity use this to stringify a variable. Note, also, that C concatenates string that appear next to each other in the source.
  • splice <var>: If var is a block (ie (...), [...], {...}) this expression removes the block separators, leaving just the block body.

Acknowledgments

The lex and yacc grammars were originally posted by Jeff Lee in 1985, and rescued and updated to the recent standards by Jutta Degener.

The syntax for macro definition was inspired by Mozilla's great sweet.js library. Originally I considered multiple different ways of defining them, including external YAML files or just piping the AST to an XSLT or similar program, but this seemed like the best way.

The Makefile is largely based on that of Dimitri Fontaine's pgloader utility.

Peter Norvig's Paradigms of Artificial Intelligence Programming chapter on Eliza was used as a reference for the pattern-matching engine.

License

Copyright (c) 2014-2015 Fernando Borretti ([email protected])

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

More Repositories

1

corvus

Low-level Lisp for LLVM
Common Lisp
502
star
2

magma

Extending C with cmacro
C
328
star
3

interim

Low-level Lisp with compile-time memory management
Standard ML
269
star
4

crane

An ORM for Common Lisp.
Common Lisp
201
star
5

lucerne

A web framework for Common Lisp, built on Clack
Common Lisp
142
star
6

cl-yaml

YAML parser for Common Lisp
Common Lisp
61
star
7

corona

Create and manage virtual machines from Common Lisp
Common Lisp
49
star
8

rock

Asset manager and compiler for Common Lisp web apps
Common Lisp
46
star
9

trivial-ssh

An SSH client library for Common Lisp (Built on libssh2)
Common Lisp
43
star
10

hermetic

Security for Clack-based Common Lisp web applications.
Common Lisp
42
star
11

docparser

Extract documentation from Common Lisp systems
Common Lisp
41
star
12

eco

Fast, flexible, designer-friendly templates for Common Lisp
Common Lisp
39
star
13

trivial-download

Download files from Common Lisp through Drakma.
Common Lisp
38
star
14

asdf-linguist

ASDF extensions.
Common Lisp
34
star
15

spaced-repetition-tools

Scripts for generating flashcards.
Python
33
star
16

swank-protocol

A low-level client for Swank
Common Lisp
31
star
17

cl-pass

Password hashing and verification library
Common Lisp
30
star
18

clack-errors

Error page middleware for Clack.
JavaScript
27
star
19

lime

A client for Swank
Common Lisp
26
star
20

dotfiles

Not guaranteed to work outside of My Machineβ„’
Emacs Lisp
23
star
21

find-port

Programmatically find open ports.
Common Lisp
19
star
22

trivial-open-browser

Open a browser window. From Common Lisp.
Common Lisp
18
star
23

terminal-keypress

Read keyboard events in the terminal from Common Lisp
Common Lisp
15
star
24

parsing-menhir

Code for a tutorial on parsing with Menhir
OCaml
15
star
25

eudoxia0.github.io

Personal website
HTML
14
star
26

lcm

Manage your system configuration in Common Lisp.
Common Lisp
12
star
27

astro-eog581

Astronomical calculations for The Epiphany of Gliese 581.
Common Lisp
11
star
28

terminal-size

Get the size of the terminal from Common Lisp
Common Lisp
11
star
29

parsimony

Parser combinators for Standard ML
Standard ML
10
star
30

clos-fixtures

CLOS fixtures.
Common Lisp
9
star
31

avatar-api

Get avatars from Gravatar and other services.
Common Lisp
9
star
32

postmaster

Email for humans
Common Lisp
8
star
33

arachne

A web crawling framework in Common Lisp.
Common Lisp
8
star
34

cl-virtualbox

Control VirtualBox from Common Lisp
Common Lisp
8
star
35

airloom

A reverse literate programming tool.
Haskell
7
star
36

cl-libyaml

libyaml bindings for Common Lisp
Common Lisp
6
star
37

concordia

A document preparation system
Standard ML
4
star
38

trivial-extract

Extract compressed files painlessly.
Common Lisp
4
star
39

ctbnrle

Continuous Time Bayesian Network Reasoning and Learning Engine (CTBN-RLE)
C
4
star
40

cartesian

My personal exocortex
Python
3
star
41

which

The which command in Common Lisp
Common Lisp
3
star
42

l0

Linear Lisp
Standard ML
3
star
43

cl-base58

An implementation of base58 for Common Lisp
Common Lisp
3
star
44

parse-front-matter

A Jekyll-style front matter parser
Common Lisp
3
star
45

clippings-parser

Export Kindle clippings to JSON, CSV, or Markdown.
Python
2
star
46

NeuriteTracer

An experiment in computer vision.
C++
2
star
47

lass-flexbox

Flexbox for Lass
Common Lisp
2
star
48

mlunit

A test framework for Standard ML
Standard ML
2
star
49

ocaml-nix-starter

OCaml starter project template using Nix for reproducibility.
Nix
2
star
50

git-file-history

View a file's git history, and individual commit info
Common Lisp
2
star
51

diy-clozemaster

Python
2
star
52

sml-sqlite3

SQLite3 bindings for MLton
Standard ML
1
star
53

MNT

Molecular machinery built in NanoEngineer, in .mmp format.
1
star
54

wax

TeX-like markup language experiment thing
Common Lisp
1
star
55

path-parse

Parse the PATH environment variable portably
Common Lisp
1
star
56

benchmarks

Common Lisp vs. the World
Common Lisp
1
star
57

texgen

Lisp DSL for generating TeX
Common Lisp
1
star