Code Monkey home page Code Monkey logo

c-mera's Introduction

C-Mera

C-Mera is a very simple source-to-source compiler that utilizes Lisp's macro system for meta programming of C-like languages. One of its main goals is to be easily extensible to other C-like languages and the different versions based on C-Mera's core illustrate that this is a simple process.

Please note: C-Mera is in a good place and works for the things we use it for. There may be no commits for some stretches and it definitely is not our full-time job, but it is alive and well :) So please consier it slow, not dead. Set up an issue if you have problems, questions or feature-requests :)

Contents

  1. Overview
  2. License
  3. Usage
    1. Build Instructions
    2. Emacs Integration
    3. Vim Integration
    4. Examples
    5. Compilation Process
    6. Programming Guide

C-Mera

The C-Mera system is a set of very simple compilers that transform a notation based on S-Expressions (sexp) for C-like languages to the native syntax of that language, e.g. from sexp-C to C, and from sexp-CUDA to CUDA. The semantics of the sexp-based notation is identical to that of the native language, i.e. no inherent abstraction or layering is introduced.

There are a number of different code generators available, all based on C-Mera with a few syntactic extensions.

  • cm-c is the default C code generator
  • cm-cxx is an extension supporting a subset of C++
  • cm-cuda is an extension featuring cuda kernel definition and call syntax
  • cm-glsl is an extension of cgen that can generate opengl shader code
  • cm-ocl (or cm-opencl) is an extension that can generate opencl code (currently not actively maintained and tested, though)

The recommended way to select a generator is by using the front-end program cm:

$ cm c ...
$ cm c++ ...
$ ...

The code for C-Mera and the C-backend is found in src/{c-mera,c,usr} and is rather comprehensive while the other generators (each in their own subdirectory) are quite concise. Browse the files of the derived generators to see how far the respective language support has grown.

License

The C-Mera system (which is the collective term for the code in the repository) is provided under the conditions of the GNU GPL version 3 or later, see the file COPYING.

Usage

To generate a C source file choose the appropriate generator and simply add the input and output file:

$ cm c input.lisp -o test.c
$ cm c++ input.lisp -o test.cpp

For more details see Compilation Process

Please note that, as implied above, the system primarily implements a simple transformation and thus does not rewrite lisp code to, for example, either C or C++, but compiles C code written in Sexps to plain C, and C++ code written in Sexps to plain C++.

However, the system can be leveraged to provide very high level programming paradigms by the use of Common Lisp macros, see our papers.

Build Instructions

We recommend CCL due to long code-transformation times with SBCL.

  • Install SBCL or CCL
  • Install Clon (See reference there)
  • (Alternatively, install Clon with Quicklisp)
    • Install Quicklisp (See the example there)
    • sbcl --eval "(ql:quickload :net.didierverna.clon.core)" or
    • ccl --eval "(ql:quickload :net.didierverna.clon.core)"
  • Build C-Mera
    • autoreconf -if (when building from a fresh repo)
    • ./configure --with-sbcl (or --with-ccl)
    • make
    • make install

Emacs Integration

The easiest way to configure your Lisp to load C-Mera is by adding it to quicklisp, as follows

$ ln -s <path-to-cmera> ~/quicklisp/local-projects/c-mera

Slime

With this setup it is possible to use Slime for the development process. The relevant C-Mera modules can be loaded by

(asdf:load-system :c-mera)
(asdf:load-system :cmu-c)    ; or :cmu-c++, cmu-cuda, etc. 
(in-package :cmu-c)          ; cl-user equivalent with c-mera environment for c
(cm-reader)                  ; switch to c-mera reader; optional for prototyping
                             ; switch back with (cl-reader)

After that you can enter Lisp expressions that print valid C Code to the REPL.

(simple-print 
  (function main () -> int 
    (return 0)))

Emacs Minor Mode (cm-mode)

To support proper indentation and highlighting of keywords, especially when your forms are not known to a SLIME session, we provide a simple minor mode for Emacs. You can set it up by

$ cp <path-to-cmera>/util/emacs/cm-mode.el <load-path>/cm-mode.el
$ cp <path-to-cmera>/util/emacs/cm.indent ~/.emacs.d/cm.indent

You can then add (require 'cm-mode) to your .emacs file and load it using M-x cm-mode. To load it automatically you can add a mode specification to the top of your file:

; -*- mode: Lisp; eval: (cm-mode 1); -*-

You can extend the indentation and keyword information by having an additional file called cm.indent along your source files, see the provided cm.indent for the layout.

Our cm-mode ist still rather basic and we are open for extensions (e.g. better syntax matching).

Vim Integration

With Vim 8 asyc processes spawned Vlime, a project that strives to provide a Slime-like worlflow for Vim. We use is (via a small plugin) to drive indentation of C-Mera code. With Vim set up for Vlime you only have to drop the plugin in the appropriate place:

$ ln -s <path-to-cmera>/util/vim/lisp_cmera.vim ~/.vim/ftplugin/

To get the default behavior (see Emacs integraion) it still has to be told where to look for the cm.indent file. This can be set in your ~/.vimrc

let g:cmera_base_indent_file = '/home/kai/.emacs.d/cm.indent'

Publications

Examples

In the following we show a few examples of how to use C-Mera. Note that we give also give it thorough treatment in our first ELS paper.

Implementation of strcmp(3)

This example illustrates the basic function definition syntax. It's a straightforward transcription of the example in the K&R book.

(function strcmp ((char *p) (char *q)) -> int
  (decl ((int i = 0))
    (for (() (== p[i] q[i]) i++)
      (if (== p[i] #\null)
          (return 0)))
    (return (- p[i] q[i]))))

Implementation of strcat(3)

Here we add arrays to the mix. It, too, is a straightforward transcription of the example in the K&R book.

(function strcat ((char p[]) (char q[])) -> void
  (decl ((int i = 0) (int j = 0))
    (while (!= p[i] #\null)
      i++)
    (while (!= (set p[i++] q[j++]) #\null))))

Implementation of wc -l

This example shows a main function and how to forward-declare externally defined symbols originating from C libraries. There is also use-functions to explicitly declare externally defined functions. In most cases, these forms are not required. C-mera checks if the symbols used are already defined and interprets them as function calls otherwise.

(include <stdio.h>)

(function main () -> int
  (decl ((int c)
         (int nl = 0))
    (while (!= (set c (getchar)) EOF)
      (if (== c #\newline)
          ++nl))
    (printf "%d\\n" nl)
    (return 0)))

Implementation of Shellsort

Lots of loops:

(function shellsort ((int *v) (int n)) -> void
  (decl ((int temp))
    (for ((int gap = (/ n 2)) (> gap 0) (/= gap 2))
      (for ((int i = gap) (< i n) i++)
        (for ((int j = (- i gap)) (&& (>= j 0) (> v[j] (aref v (+ j gap)))) (-= j gap))
          (set temp v[j]
               v[j] (aref v (+ j gap))
               (aref v (+ j gap)) temp))))))

Compilation Process

Suppose the file wc-l.lisp contains the code of the line counting example shown above. Here is a cmdline session:

$ ls
wc-l.lisp
$ cm c wc-l.lisp
#include <stdio.h>

int main(void)
{
        int c;
        int nl = 0;
        while ((c = getchar()) != EOF) {
                if (c == '\n') 
                        ++nl;
        }
        printf("%d\n", nl);
}
$ cm c wc-l.lisp -o wc-l.c
$ ls
wc-l.c wc-l.lisp
$ gcc -std=c99 wc-l.c -o wc-l

Programming Guide

This section describes how some aspects of the system work. We only describe what we believe may be noteworthy for either the seasoned Lisp or the seasoned C programmer. This part will be in motion as we add information that some of our users would have liked to have :) So please get back to us with your experience what might be helpful to mention.

Changes from c-mera-2015

For the old version see its branch. Here we only shortly list the major differences.

  • decl and for forms now require the use of = to distinguish the declarator from the initializer. Earlier we had elaborate guesses in place that worked most of the time, but not every time.

  • For C++ you can also use (decl ((int v[] { 1 2 3 })) ...) instead of (decl ((int v[] = (clist 1 2 3))) ...). This change is required to be able to distinguish between regular initialization and initializer lists. The differences is easily illustrated by printing the values of the follwing vectors:

     (typedef (instantiate #:std:vector (int)) T)
     (decl ((T vec1 = (T 10 20))
            (T vec2 { 10 20 })))
    
  • You almost never have to use use-variables and use-functions anymore.

Simple Syntax

Conditionals

if statements have exactly two or three subforms. The third subform represents the else part and is optional. Thus, the following example is not correct:

(if (!= a 0)
    (printf "all is safe")
    (return (/ b a)))

You can use progn to group multiple sub-forms

(if (!= a 0)
    (progn
      (printf "all is safe")
      (return (/ b a))))

or, equivalently, when

(when (!= a 0)
   (printf "all is safe")
   (return (/ b a)))

which expands to the previous form using progn, which, in turn, expands to:

if (a != 0) {
    ...
}

In contrast, the first example expands to

if (a != 0) {
    printf(...);
else
    return ...;

We also support cond.

Open Issues

We currently don't have unless.

Loops

A for loop is written with the loop-head grouped:

(for ((int i = 0) (< i n) (+= i 1))
  ...)

Note that C-Mera supports C-like increments and decrements for simple expressions:

(for ((int i = 0) (< i n) ++i)
  ...)

while is straighforward

(while (< a b)
   ...
   ...)
Open Issues

do-while is not implemented at the moment.

Declarations

A set of declarations is introduced with

(decl ((T name [= init])
       ...)
  ...)

or (for C++ based languages)

(decl ((T name [{ init }])
       ...)
  ...)

the initializer is optional and C-Mera collects as many symbols to be part of the type as possible, e.g.

(decl ((const unsigned long int x = 0)) ...)

is correctly identified.

As mentioned above, typenames are not checked.

In declarations (such as decl, in function parameters and (sizeof ...)) the type does not have to be enclosed in parens (and must not be). There are places, however, where for the sake of simplicity type names must be grouped, as e.g. in function return values:

(function foo ((const int *i) ...) -> (unsigned int)
  ...)

As shown in this example C-Mera also supports some C-style decorations, i.e.

(decl ((int *i 0)) ...)
(decl ((int* i 0)) ...)

are both recognized.

Namespace (Lisp vs C-Mera)

Some C-Mera symbols are also defined in Common Lisp. Initially, C-Mera starts out in the cmu-<generator> (user package, depending on the code generator used, e.g. cmu-c) which imports all cl symbols that do not conflict to provide metaprogramming as seamlessly as possible.

Especially with symbols like if etc care has to be taken to use the right one. This can be done by explicitly naming the symbol cl:if, but to define lisp functions or lisp-heavy parts of the meta code it is often more convenient to use the lisp form, such as in the example from our ELS'14 presentation:

(defmacro match (expression &rest clauses)
  `(macrolet 
     ((match-int (expression &rest clauses)
        `(progn 
           (set reg_err 
                (regcomp &reg ,(caar clauses) REG_EXTENDED))
           (if (regexec &reg ,expression 0 0 0)
               (progn ,@(cdar clauses))
               ,(lisp (if (cdr clauses)      
                          `(match-int 
                             ,expression 
                             ,@(cdr clauses))))))))
     (decl ((regex_t reg)
            (int reg_err))
       (match-int ,expression ,@clauses))))

Here we define a recursively expanding macrolet, match-int, that inserts conditional clauses (as in (if (regexec ....)) and also checks to terminate the iteration (with ,(lisp (if ...))).

Codestrings

tbd.

c-mera's People

Contributors

daveloyall avatar jandoerntlein avatar kiselgra avatar lispbub avatar pauek avatar snmsts avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

c-mera's Issues

arrays/clist c vs c++

Hi,

I just noticed that the C++ version merges initializers in braces.

Example: The following code allocates and initializes a 2 dimensional array:

$ cat /tmp/cm.lisp 
(decl ((int arr[3][3] (clist (clist 1 2 1) 
                             (clist 2 4 2) 
                             (clist 1 2 1)))))

Using cgen the result is as expected:

$ cm c /tmp/cm.lisp 
int arr[3][3] = { { 1, 2, 1 }, { 2, 4, 2 }, { 1, 2, 1 } };

With cxxgen the = ... becomes {...}, but the nested structure is lost.

$ cm c++ /tmp/cm.lisp 
int arr[3][3] { 1, 2, 1, 2, 4, 2, 1, 2, 1 };

Note that it is correct to loose one level of clists as this would (I think) not be right:

int arr[3][3] { { { 1, 2, 1 }, { 2, 4, 2 }, { 1, 2, 1 } } };   // not correct

Therefore I think the C++ version is correct in adding braces but should omit them if the value specified is already a clist, however while avoiding to collaps all successive clists.

C-Mera frontend

A while back we discussed adding a unified frontend that reflects the project's name.

I'd like to have something like

$ cmera -c infile -o outfile
$ cmera --[cuda,c++,glsl]

We could still keep the other binaries accessible for a more direct invocation, but I think having a frontend might reduce confusion.

Any comments?

Support arbitrary command line flags like gcc's -D...

It would be nice to define variables at compile-time via

./cxxgen bla.cm -DDEBUG=1 -o bla

which then can be accessed in the source

(format t "~a~%" *debug*) ;; or DEBUG, *DEBUG*, ... 

similiar to gcc's -D... flag

cxxgen using-namespace should support from-namespace

I want to write code like this:

using namespace core::log;

I would expect both of these expressions to expand to the same string:

(using-namespace |CORE::LOG|)
(using-namespace (from-namespace 'core 'log))

the first line looks awkward, the latter doesn't expand to the correct thing:

using namespace core::log;
using namespace (from-namespace 'core 'log);

Member variables with the same name in different classes

Consider this example:

(class A ()
        (decl ((int value 0))
              (function A () -> ()
                        (set value 4711))))

(class B ()
        (decl ((int value 0))
              (function B () -> ()
                        (set value 42))))

The generated output nests the second occurance of 'value' in class 'B' into a seperate block. This behaviour can be bypassed by renaming the variable.

Indentation affects simple-print

Hi,
I evaluate the following code with C-M-x in SLIME.

(ql:quickload :cgen)     
(in-package :cg-user)      
(switch-reader)



(defmacro cl-funcall (&rest rest)
  "Check the return error of an OpenCL function."
  `(progn
     (decl ((int err 0))
       (set err (funcall ,@rest))
       (if (!= 0 err)
       (funcall printf (format nil "error: ~a returned %d\\n" ',(first rest)) err)))))



(simple-print
 (function main () -> int
      (decl ((cl_platform_id platform 0)
         (cl_device_id device 0))

        (cl-funcall clGetPlatformIDs 1 &platform 0)
        (cl-funcall clGetDeviceIDs platform
            2 1
            &device 0))))

(simple-print
 (function main () -> int
   (decl ((cl_platform_id platform 0)
      (cl_device_id device 0))

     (cl-funcall clGetPlatformIDs 1 &platform 0)
     (cl-funcall clGetDeviceIDs platform
         2 1
         &device 0))))

The first simple-print gives the following error.
The second simple-print only differs in the indentation and works as expected.

The variable &device is unbound.
   [Condition of type unbound-variable]

Restarts:
 0: [retry] Retry SLIME interactive evaluation request.
 1: [*abort] Return to SLIME's top level.
 2: [abort] abort thread (#<thread "worker" running {100674D063}>)

Return-type can loose namespace

Using (from-ns) in return types can cause the system to drop the qualified name. I suppose this is due to the magic required for supporting different kinds of lists (eg, -> (const long int) and -> (from-namespace 'a 'b).

E.g.:

(function foo () -> (from-namespace 'foo 'bar))
(function foo () -> ((from-namespace 'foo 'bar)))
(function foo () -> (const (from-namespace 'foo 'bar)))

turns into this:

foo::bar foo();
bar foo();
const bar foo();

Update Readme

The readme file still describes c-mera-2015, I'll go over it to make sure the details are all up to date.

Multiple arguments for comparison

When the arguments are constant. It is straightforward to write a macro that handles a comparison with multiple arguments:

(defmacro m<= (&rest rest)
  `(&& ,@(loop for old-e = nil then e
        for e in rest
        when old-e
        collect `(<= ,old-e ,e))))

example:
(simple-print (decl ((float x 0.5))
        (m<= 0.0 x 1.0)))

expanded:
float x = 0.50000;
(0.00000 <= x) && (x <= 1.00000);

If the arguments contain a funcall (with potential side effects) care should be taken to store its value in a temporary variable . If there are multiple arguments with funcall they should be called in a sequence beginning with the first (I guess).

Add new test cases

We have a set of test cases that is not included yet. I'll fix that in the next couple of days.

Typedef doesn't work

The following both exressions

(typedef (complex float) complex_float)
(typedef complex float complex_float)

expands into:

itypedef complex floatcomplex_float;

I believe the "i" is a typo. There is a whitespace missing between the type and the alias.

typedef and namespaces

It seems you cannot typedef qualified names:

$ cat /tmp/cm.lisp 
(typedef (from-namespace 'a 'b) c)

$ cm c++ /tmp/cm.lisp 
warning: handler for 'quote' not found, assuming function-call
Unhandled undefined-function in thread ...:
  The function common-lisp:nil is undefined.

The first argument is probably also taken to be simply a symbol.

Array initialization using braces

According to the example in 5.1 of the paper I assumed this would work:

(decl ((int a[] (clist 1 2 3))))

unfortunately it doesn't emit braces:

int a[] = 1, 2, 3;

Reader-issue with base-class names

It seems that for base-class names C-Mera does not apply identifier conversion, that is

(class foo-bar ())
(class bar-foo ((public foo-bar)))

turns into

class foo_bar;

class bar_foo: public foo-bar;

Also, when only emitting forward-declarations base-class specifications should also be dropped.

Escapes

The example in the Readme (wc -l) shows a bug: The backslash in \n is not taken to the resulting C code.

saner namespace syntax

I was just writing some C++ code for header files and it was not really nice to have all those
(from-namespace 'x 'y) forms cluttering up the code. So I went ahead and build a read-macro for a more concise syntax. It supports code such as this:

(function foo ((const #:std::string &foo)) -> void
    ...)

and generates code as expected.

@lispbub What do you think about it? Should we add this (or something to that end) to our system?

(lisp
 (defun sharp-colon-reader (stream c1 c2)
   (declare (ignore c1 c2))
   (flet ((valid-id-char (c)
            (or (alphanumericp c) (char= #\: c))))
     ;; accumulation target
     (let ((str (make-array 0 :element-type 'character
                            :fill-pointer 0
                            :adjustable t)))
       ;; read char-by-char, unread terminating char
       (loop 
          for c = (read-char-no-hang stream)
          then    (read-char-no-hang stream)
          while   (valid-id-char c)
          do      (vector-push-extend c str)
          finally (unread-char c stream))
       ;; build fn-form by parsing the read string
       (let ((x `(from-namespace
                  ,@(loop for s in (loop for i = 0 then (1+ j)
                                      as j = (position #\: str :start i)
                                      collect (subseq str i j)
                                      while j)
                       if (string/= s "")
                       collect `(quote ,(cintern s))))))
         ;(format t "--> got ~s.~%" x)
         x))))

 (set-dispatch-macro-character #\# #\: #'sharp-colon-reader))

By the way, the formatting of the output of (from-namespace ...) contains a redundant space character.

cxxgen with C++11 and OpenMP support

For convenience reasons cxxgen uses the C++11 syntax to declare variables e.g.

(for ((int i 0) (< i 10) (set i (+ i 1))
   ...)

becomes

for( int i { 0 }; i < 10; i = i + 1 ) 
{...}

which is the correct and desired behaviour. But OpenMP does not support this feature - thus when you add "#pragma omp parallel for" prior to the for loop you run into an error like:

error: expected ';' in 'for' statement specifier
    for( int i { 0 }; i < 10; i = i + 1 )

Therefore OpenMP is currently not supported at all.

Enhancement: Use glsl-spec for GLSL

gl.xml defines all the cpu side functions for the OpenGL spec but non of the glsl ones. This repo https://github.com/cbaggers/glsl-spec has the glsl functions and variables as either sexpressions or json.

It is also in quicklisp so should be very easy to consume.

I use this as the primary source of functions/variables for my glsl compiler and it seems to work pretty well.

Feel free to ping me if anything in the repo seems vague and needs clarification

C++ constructor initialization list

Currently (constructor (params) body) is simply a class-internal macrolet that maps to a function node, however, with that setup we can't specify an initialization list.

I would suggest to make constructor an extension of the function node and only make it accessible by the macrolet as already present in the definition of class, maybe supporting this syntax:

(class
  (decl ((int x) (int y))
    (constructor ((int x) (int y))
        (x x y y)     ; or ((x x) (y y)) or (:x x :y y) ?
      body)))

@lispbub What do you think?

Selecting wrong asd files during build

In the build process we (require...) our systems with relative path information. This might load installed and registered versions, not the source version.

Parameter list formatting

Maybe we should drop (void) for empty parameter lists in cxxgen and derived targets. Plain () is more common in those.

Heap overflow

C-Mera runs out of memory even if there is plenty of free memory available. Seems like the default heap size of SBCL is predefined. To increase the heap size the SBCL command line argument --dynamic-space-size megabytes can be used. The documentation says:

Size of the dynamic space reserved on startup in megabytes. Default value is platform dependent.

Would it be possible to increase the heap and stack size of C-Mera?

operator new in declarations

I was expecting that new in decl would work, however it gets parsed quite differently.

$ cat /tmp/cm.lisp 
(decl ((int *x (new 'int[10])))
  (set x (new 'int[10]))
  (set x (new (aref 'int 10))))

This is the output:

$ cm c++ /tmp/cm.lisp 
int *x { new int_10_ };
x = new int_10_;
x = new int[10];

@lispbub Do you think it would work to always parse the contents of new as decl-item?
Maybe we could also do something like with the names of decl-items to get the size of the array (if):
(new unsigned long int 10), that would get us around the awkward aref syntax ;)

Document how to create GLSL shader

I have a hard time figuring out how to declare a GLSL compute shader:

I want the code to expand into this:

#version 430 core
layout (local_size_x=32, local_size_y=32) in;
layout (binding=0,r32f) uniform image2D data; 
layout (binding=2,r32ui) uniform uimage2D lut; 
const int imw=380, imh=241;
const int ng=256;
void main(void)
{ 

All I came up with up to now is this:

(simple-print
 (progn
   (comment (format nil "#version 430 core~%") :prefix "")
   (layout (local-size-x 32) (local-size-y 32) ) (comment "in;" :prefix "")
   (layout (binding 0) (nil 'r32f))
   (function main () -> void )))

I don't know how to make the r32f qualifier work and it is not clear to me how to declare the images or swizzle access like this:

texture(samp2,texcoord.xy).r

Parallel installation of SBCL and CCL versions

It would be nice if we could install both versions at the same time. Selection could either occur via names:

$ cm-ccl c ...

or via options

$ cm --ccl c ...

In both cases the actual generators (i.e. cm-c, etc) would have to be renamed or put in subdirectories (e.g. $prefix/bin/cm-ccl).

@lispbub Which one do you think would be better?

For both versions it should be possible to specify a default implementation and thus we would also have to be abel to query which was selected, e.g. for the use in scripts, by something like

$ cm impl
sbcl

Implement macros for constructors/destructors

Currently there is just one way to create class ctors: by declaring the return value as 'nil'.

(class A ()
(public
(function A () -> ()
;; ....
)))

It would be nice if there were alias macros to define ctors and dtors like this:

(class A ()
(public
(ctor () ; or maybe "constructor" etc
;; ....
)
(dtor ()
;; ....
)))

using-namespace seems to be broken

Hi again,

I'm certain that using-namespace has worked for ages but somehow it looks broken to me atm.
Both

(using-namespace std)
(using-namespace 'std)

yield

using namespace ;
using namespace ;

Saner template syntax

Similar to #32 it would be cool to have

(decl ((#:std:tuple<int *, #:std:string> foo)) ...)

or maybe

(decl ((#:std:tuple<(int) (#:std:string) (const char *)> foo)) ...)

fixed: CCL conditions can hang C-Mera

When using CCL triggering the condition handler hangs C-Mera. In the following example this is triggered by a bug (see #50), but the general behaviour can be oberserved in other situations, too. Restarts can be selected, but after the handler is done the system hangs. Hitting C-c does not help, so the best exit is to put the job into background-mode and kill it.

$ cm -V
> Error: Undefined function c-mera::print-version called with arguments () .
> While executing: cm-c::c-processor, in process toplevel(2).
> Type :GO to continue, :POP to abort, :R for a list of available restarts.
> If continued: Retry applying c-mera::print-version to nil.
> Type :? for other options.
1 > :pop
^C^C^C^C^Z
[1]+  Stopped                 cm -V
$ kill cm-c
$ fg
cm -V

Terminated

C++ for loop initialization is broken

This is a weird one :)

(for ((unsigned int f 0) (< f 10) ++f))

turns into

for(unsigned int f = 0 }; f < 10; ++f)

when using cm c++

As expected, using cm c works perfectly well.

Single quote character

The following c-mera expression results in wrong C code:

 (when (== #\' ch)
      (comment "quo")
      (return (cast 'o *quo*)))

The C code should look like this:

    if ('\'' == ch) {
        //quo
        return ((o)3);
    }

but it actually looks like this:

    if (''' == ch) {
        //quo
        return ((o)3);
    }

Currently I use this:

 (when (== (char-code #\') ch)
      (comment "quo")
      (return (cast 'o *quo*)))

but that makes the C code very unreadable:

    if (39 == ch) {
        //quo
        return ((o)3);
    }

literal aref syntax with multiple arrays

The following code works:

(decl ((foo *f)
       (int i) (int j)
       (bar b))
  (set b (aref f[i].blah j)))

However, using literal syntax breaks in this case:

(decl ((foo *f)
       (int i) (int j)
       (bar b))
  (set b f[i].blah[j]))

Stack-overflow with

...
997: (CGEN::SPLIT-AREF I] NIL)
998: (CGEN::SPLIT-AREF I] NIL)
999: (CGEN::SPLIT-AREF I] NIL)

As expected, two adjacent array refs work fine:

(decl ((foo *f)
       (int i) (int j)
       (bar b))
  (set b (aref f[i][j].blah j)))

@lispbub More reader-macro madness? ;)

Documentation should explain compound types

I'm having a hard time writing macros that work with compound types that consist of several words like "unsigned int".
Ideally I would like those types to always be written as a list like
(unsigned int).

Casting the return value of malloc is another thing which I can't figure out the correct expression for.
I came up with

(simple-print
 (decl ((float
     (type-pointer a)
     (cast (pointer float) (funcall malloc (* (funcall sizeof 'float) 3)))))))

the expansion of which missses the pointer *

float *a = ((float)malloc(sizeof(float) * 3));

I would like this

(simple-print
 (decl (((float complex)
     (type-pointer a)
     (cast (pointer (float complex)) (funcall malloc (* (funcall sizeof '(float complex)) 3)))))))

to expand into

float complex*a=((float complex*)malloc(sizeof(float complex)*3));

nested use-variables produces superfluous output

I was trying to declare global variables and was about to suggest the following addition to c-mera:

(defmacro globals (&body decls)
  `(progn
     (use-variables ,@(loop for d in decls collect (cgen::get-declaration-name d)))
     (decl ,decls)))

(globals
 (int i)
 (bool verbose))

However, the generated code contains a suprious identifier (the last one declared):

$ cm c globals.lisp 
verbose
int i;
bool verbose;

@lispbub Do you know what's going wrong here?

OpenCL inline-code support

I would like to emit OpenCL programs into strings. This is an example:

const char *source = 
  "\
enum{N=100};                                \
const sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE|          \
                          CLK_ADDRESS_CLAMP_TO_EDGE|            \
                          CLK_FILTER_LINEAR;                \
__kernel void red(int n, __write_only image2d_t rgba,                   \
                  __read_only image3d_t vol)                \
{                                                                       \
  int x=get_global_id(0), y=get_global_id(1);               \
  float4                                \
    start=(float4)(x,y,0,0),                        \
    target=(float4)(64,64,64,0),                    \
    delta=normalize(target-start),                  \
    val=0.0;                                \
  int i=0;                              \
  for(i=-N;i<N;i++)                         \
    val+=read_imagef(vol,sampler,start+i*2*delta);          \
  write_imagef(rgba,(int2)(x,y),val.xxxx/3);                \
}";

I hacked something together using comment which seems to work. I'm sure it can be improved.

(lisp
 (defun replace-newline-with-backslash-newline (string)
   ;; this is from common lisp cookbook i got it from here:
   ;; http://stackoverflow.com/questions/4366668/str-replace-in-lisp
   ;; i modified it to only search for newlines
   (let ((part #\Newline)
     (replacement "\\
"))
     (with-output-to-string (out)
       (loop
      for old-pos = 0 then (+ pos 1)
      for pos = (position part string
                  :start old-pos
                  :test #'char=)
      do (write-string string out
               :start old-pos
               :end (or pos (cl:length string)))
      when pos do (write-string replacement out)
      while pos)))))
(use-variables CLK_NORMALIZED_COORDS_FALSE
           CLK_ADDRESS_CLAMP_TO_EDGE
           CLK_FILTER_LINEAR)


(loop for e in '(__read_only __write_only __kernel)
     do (cgen:add-qualifier e))

(defmacro make-float4 (x &optional (y 0) (z 0) (w 0))
  ;; FIXME x,y,z,w only works with symbols. not with constants 
  `(cast float4 (comment
         (lisp (format nil "(~{~a~^,~})" (list ,x ,y ,z ,w)))
         :prefix "")))
(defmacro make-int2 (x &optional (y 0))
  `(cast int2 (comment
           (lisp (format nil "(~{~a~^,~})" (list ,x ,y)))
           :prefix "")))
(simple-print
 (comment
  (format nil "const char *source = \"\\~%~a\""
      (replace-newline-with-backslash-newline
       (with-output-to-string (*standard-output*)
         (simple-print
          (function integrate_along_ray ((int n)
                         (__write_only image2d_t rgba)
                         (__read_only image3d_t vol))
          -> |__KERNEL VOID|
        (decl ((const sampler_t sampler (\| CLK_NORMALIZED_COORDS_FALSE
                            CLK_ADDRESS_CLAMP_TO_EDGE
                            CLK_FILTER_LINEAR))
               (int x (funcall get_global_id 0))
               (int y (funcall get_global_id 1))
               (const int n 64)
               (const float val1 0.0))
          (decl ((float4 start (make-float4 x y))
             (float4 val val1)
             (float4 target (make-float4 n n n)))
            (decl ((float4 delta (funcall normalize (- target start))))
              (for ((int i (- 100)) (< i 100) i++)
            (+= val (funcall read_imagef vol sampler
                     (+ start (* 2 i delta)))))
              (funcall write_imagef rgba (make-int2 x y) (/ (oref val xxxx) 3)))))
        ))))) :prefix ""))

This expands into:

const char *source = "\
\
__kernel void integrate_along_ray(int n, __write_only image2d_t rgba, __read_only image3d_t vol)\
{\
    const sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRESS_CLAMP_TO_EDGE | CLK_FILTER_LINEAR;\
    int x = get_global_id(0);\
    int y = get_global_id(1);\
    const int n = 64;\
    const float val1 = 0.00000;\
    float4 start = ((float4)\
    (x,y,0,0));\
    float4 val = val1;\
    float4 target = ((float4)\
    (n,n,n,0));\
    float4 delta = normalize(target - start);\
    for(int i = -100; i < 100; i++){\
        val += read_imagef(vol, sampler, (start + (2 * i * delta)));\
    }\
    write_imagef(rgba, ((int2)\
    (x,y)), val.xxxx / 3);\
}"

Reading floats discards suffix

When I have a float such as 1.0f in my code the suffix is not present in the output: 1.0. Especially with CUDA this can lead to performance degradation.

Missing paranthesis when casting

(simple-print (% (cast int (* 7 255.0)) 255))

expands to

((int)7 * 255.00000) % 255

but should expand to

((int)(7 * 255.00000)) % 255

Note the missing paranthesis.

Emit OpenMP pragmas

I would like to emit lines like this:

 #pragma omp parallel for

I don't think that is currently possible.

Support for "union"

Hi,
I would like to be able to declare unions.
I try to express the ulisp source code http://www.ulisp.com/list?1DZ4 in c-mera and I stumble already at the first typedefinition:

typedef struct sobject {
  union {
    struct {
      sobject *car;
      sobject *cdr;
    };
    struct {
      enum type type;
      union {
        unsigned int name;
        int integer;
      };
    };
  };
} object;

Currently, I define 3 different struct types. But this is quite clunky:

(defmacro deftstruct (name &body body)
  `(progn
     (typedef struct ,name ,name)
     (struct ,name
             ,@body)))

(include <stdint.h>)

(comment "I use integers that have the same size as a pointer")

(typedef uintptr_t uintgr)
(typedef intptr_t intgr)

(comment "C-mera doesn't support unions")

(deftstruct cons_object
  (decl ((cons_object* car)
         (cons_object* cdr))))
(deftstruct cons_symbol
  (decl ((uintgr type)
         (uintgr name))))
(deftstruct cons_number
  (decl ((uintgr type) 
         (intgr integer))))
#include <stdint.h>
//I use integers that have the same size as a pointer
typedef uintptr_t uintgr;
typedef intptr_t intgr;
//C-mera doesn't support unions
typedef struct cons_object cons_object;

struct cons_object
{
    cons_object *car;
    cons_object *cdr;
};
typedef struct cons_symbol cons_symbol;

struct cons_symbol
{
    uintgr type;
    uintgr name;
};
typedef struct cons_number cons_number;

struct cons_number
{
    uintgr type;
    intgr integer;
};

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.