with-c-syntax

2021-08-07

with-c-syntax is a fun package which introduces the C language syntax into Common Lisp.

Upstream URL

github.com/y2q-actionman/with-c-syntax

Author

YOKOTA Yuki <y2q.actionman@gmail.com>

License

WTFPL
README
Provided Systems

1Abstract

with-c-syntax is a fun package which introduces the C languagesyntax into Common Lisp. (Yes, this package is not for practicalcoding, I think.)

At this stage, this package has almost all features of ISO C 90 freestanding implementation. (The lacking part is the full implementation of the C Preprocessor.)

2News

2.12021-5-24

  • C Numeric Literals are added. (Inspired by @akanouras. See PR #7.)Hexadecimal floating number syntax may be a only practicalfeature of this package. (Try #{ 0x1.fffp+1; }#)

2.22019-4-25

  • I added some special handlings around ++, *, etc.The Duff's Device example becomes prettier.
  • I added a new example, C in Lisp in C in Lisp. (unholy mixture..)

3Examples

3.1Hello, World

CL-USER> (with-c-syntax:with-c-syntax ()
    format \( t \, "Hello World!" \) \;
  )

Hello World!
NIL

For suppressing Lisp's syntax, you need many backslash escapes.

#{ and }# reader macro escapes them and wrap its contents into with-c-syntax. You can use it to write simply:

;; enables #{ }# reader macros.
CL-USER> (named-readtables:in-readtable with-c-syntax:with-c-syntax-readtable)
...

CL-USER> #{ format (t, "Hello World!"); }#

Hello World!
NIL

This example shows you can call a Lisp function (cl:format) with C syntax.

3.2Summing from 1 to 100.

  (named-readtables:in-readtable with-c-syntax:with-c-syntax-readtable)

  #{
    int i, sum = 0;
  
    for (i = 0; i <= 100; ++i)
      sum += i;
    return sum;
  }#
  ;; => 5050

3.3Using C syntax inside a Lisp function.

  (named-readtables:in-readtable with-c-syntax:with-c-syntax-readtable)

  (defun array-transpose (arr)
    (destructuring-bind (i-max j-max) (array-dimensions arr)
      #{
        int i,j;
        for (i = 0; i < i-max; i++) {
            for (j = i + 1; j < j-max; j++) {
	        rotatef(arr[i][j], arr[j][i]);
            }
        }
      }#)
    arr)

  (array-transpose (make-array '(3 3)
 		:initial-contents '((0 1 2) (3 4 5) (6 7 8))))
  ; => #2A((0 3 6) (1 4 7) (2 5 8))

3.4Defining a function with C syntax.

  (named-readtables:in-readtable with-c-syntax:with-c-syntax-readtable)

  #{
  int sum-of-list (list) {
    int list-length = length(list);
    int i, ret = 0;

    for (i = 0; i < list-length; ++i) {
       ret += nth(i, list);
    }

    return ret;
  }
  }#

  (sum-of-list '(1 2 3 4 5 6 7 8 9 10)) ; => 55

3.5Duff's Device

  (named-readtables:in-readtable with-c-syntax:with-c-syntax-readtable)

  (defun wcs-duff-device (to-seq from-seq cnt)
      #{
      int *to = &to-seq;
      int *from = &from-seq;
  
      int n = (cnt + 7) / 8;
      n = floor(n);           /* Lisp's CL:/ produces rational */
      switch (cnt % 8) {
      case 0 :    do {    *to++ = *from++;
      case 7 :            *to++ = *from++;
      case 6 :            *to++ = *from++;
      case 5 :            *to++ = *from++;
      case 4 :            *to++ = *from++;
      case 3 :            *to++ = *from++;
      case 2 :            *to++ = *from++;
      case 1 :            *to++ = *from++;
        } while (--n > 0);
      }
      }#
    to-seq)

  (defparameter *array-1*
    (make-array 20 :initial-element 1))

  ;; C syntax can also be used for defining a variable.
  #{
  int *array-2* [] = {2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2};
  }#

  (wcs-duff-device *array-1* *array-2* 10)
  (print *array-1*) ;; => #(2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1)

This example shows some C operators (++, --, unary * and &) behave as you expected as possible.

(This feature is based on @phoe's suggestion. See Issue #2 .)

3.6C in Lisp in C in Lisp

Sometimes you want to use the Lisp syntax even in with-c-syntax.If you feel so, you can use ` as an escape. Here is an example:
(named-readtables:in-readtable with-c-syntax:with-c-syntax-readtable)

#{
void 99-bottles-of-beer (filename) {
  void * output-path = merge-pathnames (filename, user-homedir-pathname());
  `(with-open-file (*standard-output* output-path :direction :output
				      :if-exists :supersede :if-does-not-exist :create)
     #{
     int b;
     for (b = 99; b >= 0; b--) {
         switch (b) {
         case 0 :
           write-line("No more bottles of beer on the wall, no more bottles of beer.");
           write-line("Go to the store and buy some more, 99 bottles of beer on the wall.");
           break;
         case 1 :
           write-line("1 bottle of beer on the wall, 1 bottle of beer.");
           write-line("Take one down and pass it around, no more bottles of beer on the wall.");
           break;
         default :
           format(t, "~D bottles of beer on the wall, ~D bottles of beer.~%", b, b);      
           format(t, "Take one down and pass it around, ~D ~A of beer on the wall.~%"
                     , b - 1
                     , ((b - 1) > 1)? "bottles" : "bottle");
           break;
         }
     }
     }#);
  return;
  }
}#

(99-bottles-of-beer "99_bottles_of_beer.txt")

(probe-file "~/99_bottles_of_beer.txt") ; => T

This example creates "99_bottles_of_beer.txt" file into your home directory. I used ` for using with-open-file in Lisp syntax.

(You can use any Lisp operators including with-open-file in with-c-syntax style. However it looks very weird; An example exists in my test code.)

4How to load

4.1Loading by quicklisp

This library is quicklisp-ready.

Due to Mathematical Errors, this is removed at June 2021. I'm now working to fix it.

4.2or, Loading manually

4.2.1Libraries depending on

cl-yacc
As a parser for C syntax
alexandria
as utilities.
named-readtables
For exporting '#{' reader syntax.
cl-ppcre
For parsing numeric constants.
trivial-gray-streams
For implementing translation phase 1 and 2 correctly.

4.2.1.1by libc

float-features
For math.h, dealing NaN and Infinities.
floating-point-contractions
For math.h, to implement some functions.

4.2.1.2by test codes

1am
As a testing framework.
trivial-cltl2
For using compiler-let to test NDEBUG.
floating-point
For comparing mathmatical function results.

4.2.2Load with ASDF

(asdf:load-asd "with-c-syntax.asd")
(asdf:load-system :with-c-syntax)

4.2.3Running tests

(asdf:load-asd "with-c-syntax-test.asd")
(asdf:test-system :with-c-syntax)

4.2.4CI

https://github.com/y2q-actionman/with-c-syntax/actions/workflows/linux-testSystem.yml/badge.svg There are Github Actions to run the test above. I wrote current recipes referring the example of CI-Utils.

5API

Please see these docstrings or comments:

6Further Information

What this macro does is only expanding a list of symbols to a Lisp form.

If you are still interested, please see: https://github.com/y2q-actionman/with-c-syntax/wiki

Vacietis is a similer project. It is a "C to Common Lisp" compiler, based on reader macros.

7License

Copyright (c) 2014,2019,2021 YOKOTA Yuki <y2q.actionman@gmail.com>

This program is free software. It comes without any warranty, to the extent permitted by applicable law. You can redistribute it and/or modify it under the terms of the Do What The Fuck You Want To Public License, Version 2, as published by Sam Hocevar. See the COPYING file for more details.

Dependencies (10)

  • 1am
  • alexandria
  • cl-ppcre
  • cl-yacc
  • float-features
  • floating-point
  • floating-point-contractions
  • named-readtables
  • trivial-cltl2
  • trivial-gray-streams

Dependents (0)

    • GitHub
    • Quicklisp