The SCMC Programming Lagnuage

1 Introduction

The SCMC programming language is a scheme like lisp dialect of which the core part can be viewed as the "S-expression" version of the C programming language. So in this document, we will mainly introduce how the SCMC compiler compiles the SCMC code into C. Reader should be familiar with the C and the scheme programming languages. Books such as The C Programming Language , The Little Schemer , Structure and Interpretation of Computer Programs , The Scheme Programming Language and ANSI Common Lisp are suggested for reference.

1.1 The first program: Hellow world

In SCMC, one possible program to print "Hellow world" is

(begin
  (include< "stdio.h")
  
(defun main int ((int argc) (char** argv))
  (printf "Hellow world\n")
  (return 0))
)

Save the above code as hellow.scmc . Then use the following command to compile it to C

$ scmc_compile_passes hellow.scmc

If everything is right, a C file with name hellow.c should be generated. Its content should be something like:

$ cat hellow.c
#include <stdio.h>
int main (int argc ,char * * argv ){
printf ( "Hellow world\n" );
return 0 ;}
$

We can see that it is just a C version of a Hellow world program. So a C compiler can be used to compile it into executable file and then we may run it.

$ cc hellow.c
$ ./a.out
Hellow world
$

As we stated, the core part of SCMC programming language can be viewed as the "S-expression" version of the C programming language.

The code of hellow.scmc is just one S-expression of which the first symbol is begin . The begin plays the role just like the role it plays in the scheme programming language. It tells the compiler to compile the rest of that S-expression in sequence.

The following code

(include< "stdio.h")

is compiled to

#include <stdio.h>

in C which is used to include information about the standard input/output library.

The main body of this code is a main function definition, its syntax can be stated as follows:

(defun function_name return_type ((type_1 arg_1) (type_2 arg_2) ...)
  body1
  body2
  ...)
will be compiled to
return_type function_name (type_1 arg_1, type_2 arg_2, ...){
body1;
body2;
...
}

in the C code.

Function calls will be converted correspondingly, for example:

(function_name arg_1 arg_2 ...)

will be compiled to

function_name (arg_1, arg_2, ...)

As we can see, this simple Hellow world program is just an "S-expression" version of a Hellow world program in the C programming language. We will continue to introduce the key idea of writing an SCMC code and its structures and syntaxes.

1.2 SCMC codes

The core part of an SCMC code is the "S-expression" version of C code, so why don't we just write the C code other than the SCMC code? The reason is simple, S-expressiones can be easily manipulated and reused through macros and procedures in scheme codes. So the rest of this document will be divided into two chapters, of which the first one introduces all counterparts of C codes in SCMC, and the second one introduces the macro system and the built-in standard libs for manipulate S-expressiones in SCMC.

2 "S-expression" version of C

In this section, we will introduce all counterparts of C codes in SCMC. Note that in the C programming language, statements, declarations, statements and declarations in expressions (A GNU C extension that can be found in here ), and expressions are very different and the C compiler can not automatically distinguish most of them. However in the scheme programming language they can be automatically decided. So in this chapter we will not distinguish the appearance of statements, declarations and expressions in the C code.

2.1 Include directive

Include files in implementation-defined places

Codes in SCMC:

(include< file_name)

Conterpart in C:

#include <file_name>

where file_name should be a string or symbol

Example:

(include< "stdlib.h")

Include files firstly in the location of source path

Codes in SCMC:

(include- file_name)

Conterpart in C:

#include "file_name"

where file_name should be a string or symbol .

Example:

(include- "local_headers.h")

2.2 Declarations and definitions

Declarations

Codes in SCMC:

(declare type var_name_1 var_name_2 ...)

Conterpart in C:

type var_name_1, var_name_2, ...

Note that declare type can be written as declare-type.

Example:

(begin
  (declare int var1 var2 var3)
  (declare-int var4 var5))

Declarations with initializing

Codes in SCMC:

(declare type (var_name_1 init_1) (var_name_2 init_2) ...)

Conterpart in C:

type var_name_1, var_name_2, ...;
var_name_1=init_1;
var_name_2=init_2;
...

Note that declare type can be written as declare-type.

Example:

(begin
  (declare double (var1 init_1) (var2 init_2) var3 var4)
  (declare-float var5 (var6 init_6) var7))

Definition

Codes in SCMC:

(define-type var_name init)

Conterpart in C:

type var_name;
var_name=init;

Note that the init can be empty.

Example:

(begin
  (define-double arg0)
  (define-double arg1 (cos (+ arg0 1)))
  (define-int* arg2 NULL))

Definition with type inference

Codes in SCMC:

(define var_name init)

Conterpart in C:

typeof(init) var_name;
var_name=init;

Note that the typeof(init) will use the type inferenc feature in SCMC compiler to decide the type of init . Some times it may failed when init is complex.

Example:

(begin
  (define arg0 4.00000000000000022e-01)
  (define arg1 (cos (+ arg0 1)))
  (define arg2 (- arg1 7)))

2.3 Set

Set a value to a variable

Codes in SCMC:

(set! var val)

Conterpart in C:

var=val;

Example:

(begin
  (define a 2.00000000000000011e-01)
  (set! a 1.50000000000000000e+00))

2.4 Functions

Function definition

Codes in SCMC:

(defun function_name return_type ((type_1 arg_1) (type_2 arg_2) ...)
  body1
  body2
  ...)

Conterpart in C:

return_type function_name (type_1 arg_1, type_2 arg_2, ...){
body1;
body2;
...
}

Example:

(begin
(defun fun1 double ((double arg0))
  (return (+ arg0 4)))
)

Function declaration

Codes in SCMC:

(dec-fun function_name return_type ((type_1 arg_1) (type_2 arg_2) ...))

Conterpart in C:

return_type function_name (type_1 arg_1, type_2 arg_2, ...);

Example:

(begin
(defun fun1 double ((double arg0)))
)

Function pointer variable declaration

Codes in SCMC:

(declare (function-pointer return_type type_1 type_2 ...) function_name)

Conterpart in C:

typedef return_type (* function_name) (type_1, type_2, ...);

Example:

(begin
  (declare (function-pointer double double) sin)
  (define-double m (sin 2.00000000000000011e-01)))

Function call

Codes in SCMC:

(function_name arg_1 arg_2 ...)

Conterpart in C:

function_name (arg_1, arg_2, ...)

Example:

(begin
  (function_name_1 arg_1 arg_2 3.00000000000000000e+00 "text")
  (define-int rt1 (function_name_2)))

Return statement in function

Codes in SCMC:

(return retvar)

Conterpart in C:

return retvar;

Example:

(begin
(defun fun1 double ((double arg0))
  (return (+ arg0 4)))
)

2.5 Arithmetic operations

All arithmetic operations are treated as functions in SCMC, for example:

(begin
  (+ 3 4)
  (* 4.20000000000000018e+00 (sin 2.99999999999999989e-01))
  (b-and 1 0))

2.6 Type Conversion

Convet a type

Codes in SCMC:

(type-convert to_type expr)

Conterpart in C:

((to_type)expr)

Example:

(begin
  (define-double a 0.00000000000000000e+00)
  (define-int* p_a (type-convert double* ("&" a))))

2.7 Pointers

Get memory address of a variable

Codes in SCMC:

("&" var)

Conterpart in C:

&var

Example:

(begin
  (define-int a 0)
  (define-int* p_a ("&" a)))

2.8 Control flow

If statements and expressions

Codes in SCMC:

(if p
  v_1
  v_2)

Conterpart in C:

if(p){
v_1;
}else{
v_2
}

Example:

(begin
  
(if (< i 4)
  i
  0)

  
(if (> g 3)
  
(begin
  (define g1 (+ g 4.50000000000000000e+00))
  (* g1 g1))

  (- g 4))

  
(if (< (+ 4 g) 0)
  (set! g 0))
)

Cond structure

Codes in SCMC:

(cond (p1 v1p1 v2p1 ...) (p2 v1p2 v2p2 ...) ... (else velse1 velse2 ...))

Conterpart in C:

if(p1){
v1p1;
v2p1;
...
}else if(p2){
v1p2;
v2p2;
...
}else if ...
else{
velse1;
velse2;
...
}

Example:

(cond
((< i 4)
  5)
((< i 8)
  (* i 4))
((< i 12)
  (define g 4)
  (cos g))
(else
  (sin i))
)

2.9 Loop

For loop

Codes in SCMC:

(for beg condition iter
  body1
  body2
  ...)

Conterpart in C:

for (beg;condition;iter) {
body1;
body2;
...
}

Example:

(begin
  (define-int i)
  
(for (set! i 0) (< i 10) (incf! i)
  (printf "%d\n" i))
)

Break and Continue

Codes in SCMC:

break continue

Conterpart in C:

break continue

Example:

(begin
  (define-int i)
  
(for (set! i 0) (< i 100) (incf! i)
  
(if (> i 80)
  break)

  
(if (< i 40)
  continue)

  (printf "%d\n" i))
)

2.10 Program structure

Block structure

Codes in SCMC:

(block
  exp1
  exp2
  ...)

Conterpart in C:

{
exp1;
exp2;
...
}

Example:

(block
  (define i 1)
  
(block
  (define i 4)
  (printf "inner block: i=%d\n" i))

  (printf "outter block: i=%d\n" i)
  (begin (define-double a
(block
  (define-double b (cos 5.00000000000000000e-01))
  (+ b 1))
)))

2.11 Goto and Labels

Labels

Codes in SCMC:

(label lab)

Conterpart in C:

lab:

Example:

(begin
  (label l1)
  
(if (< i 4)
  (goto l1))
)

Goto

Codes in SCMC:

(goto lab)

Conterpart in C:

goto lab;

Example:

(begin
  (label l1)
  
(if (< i 4)
  (goto l1))
)

2.12 Arrays

Declare arrays

Codes in SCMC:

(dec-array type name dim_1 dim_2 ...)

Conterpart in C:

type name[dim_1][dim_2]...

Example:

(begin (dec-array double arr1 4 8 3))

Reference array or pointer element

Codes in SCMC:

(vector-ref name i)

Conterpart in C:

name[i]

Example:

(begin
  (dec-array double arr0 8)
  (define m (vector-ref arr0 2)))

Set array element

Codes in SCMC:

(vector-set! name i val)

Conterpart in C:

name[i]=val

Example:

(begin
  (dec-array double arr1 4 8 3)
  (vector-set! (vector-ref (vector-ref arr1 0) 2) 1 4.00000000000000022e-01))

2.13 Struct

Declare a struct

Codes in SCMC:

(typedef-struct name
  (type_1 var_1)
  (type_2 var_2)
  ...)

Conterpart in C:

typedef struct{
type_1 var_1;
type_2 var_2;
...
} name;

Example:

(begin
  
(typedef-struct point_4d
  (double x)
  (double y)
  (double z)
  (double w))

  (define-point_4d p1))

Reference a variable from struct pointer

Codes in SCMC:

(structp-ref name var)

Conterpart in C:

name->var

Example:

(begin
  
(typedef-struct point_4d
  (double x)
  (double y)
  (double z)
  (double w))

  (define-point_4d p1)
  (define-point_4d* pp1 ("&" p1))
  (set! (structp-ref pp1 x) 5.00000000000000000e-01)
  (set! pp1->y 2.00000000000000011e-01))

Reference a variable in struct

Codes in SCMC:

(struct-ref name var)

Conterpart in C:

name.var

Example:

(begin
  
(typedef-struct point_4d
  (double x)
  (double y)
  (double z)
  (double w))

  (define-point_4d p1)
  (set! (struct-ref p1 x) 5.00000000000000000e-01)
  (set! p1.y 2.00000000000000011e-01))

SIMD parallel for

Codes in SCMC:

(paraforn (simd_length align_length type_map var 0 range) body1 body2 ...)

Conterpart in C:

{
long var;
for(var=0;var<range;var++){
body1;
body2;
...
}}

This syntax is designed to convert a parallelable for statement block to SIMD optimized version. Currently not all expressions and statements are supported in this structure, you may try if it is supported.

Example:

(begin (paraforn (4 32 ((double __m256d) (long __m256i) (set-single _mm256_setr_pd))) g 0 len (define-double ag (vector-ref a g)) (define-double bg (vector-ref b g)) (force-simd-ver (set! ag (+ bg (* ag ag 5.00000000000000000e-01)))) (vector-set! a g ag)))

Force use SIMD intrin in the paraforn

Codes in SCMC:

(force-simd-ver expr)

Conterpart in C:

This syntax is used in the paraforn syntax to generate intrin SIMD codes.

Example:

(begin (paraforn (4 32 ((double __m256d) (long __m256i) (set-single _mm256_setr_pd))) g 0 len (define-double ag (vector-ref a g)) (define-double bg (vector-ref b g)) (force-simd-ver (set! ag (+ bg (* ag ag 5.00000000000000000e-01)))) (vector-set! a g ag)))

3 Macros and the standard library in SCMC

3.1 Macros

In the SCMC language, macros can be defined by the defmacro structure, which behaves like defmacro in the common lisp programming language.

DEFMACRO

Codes in SCMC:

(defmacro macro_name (var1 var2 ...)
  body1
  body2
  ...)

Conterpart in C:

No C counterpart for this structure.

Example:

(begin
  
(defmacro print_double_variables var_list
  `
(begin
  
(begin
  unquote
  (map
(lambda (v)
  `(printf "%e " ,v))
var_list))

  (printf "\n"))
)

  (declare-double (v1 2.00000000000000011e-01) (v2 2.99999999999999989e-01) (v3 1.00000000000000006e-01))
  (print_double_variables v1)
  (print_double_variables v1 v2)
  (print_double_variables v2 v1 v3)
  
(defmacro vrf (vector-name . ref_ids)
  (cond
((null? ref_ids)
  vector-name)
(else
  (define lst (reverse ref_ids))
  (define rem (reverse (cdr lst)))
  (set! lst (car lst))
  `(vector-ref (vrf ,vector-name unquote rem) ,lst))
))

  (dec-array double m 4 3)
  (set! (vrf m 2 1) 0))

3.2 Standard Libs in SCMC

input-scmc

Codes in SCMC:

(input-scmc scmc_source)

Conterpart in C:

No C counterpart for this structure. This operation will evaluate all source put in the file located at scmc_source .

Example:

(input-scmc "local_defs.scmc")

eval-scmc-global

Codes in SCMC:

(eval-scmc-global scmc_expression)

Conterpart in C:

No C counterpart for this structure. This operation will evaluate the S-expression scmc_expression .

Example:

(eval-scmc-global (list 'define-double s 0.00000000000000000e+00))

()

Codes in SCMC:

()

Conterpart in C:

No C counterpart for this structure. The empty list will not be generated in the C code.

Example:

(begin
  (eval-scmc-global
(begin
  (define tmp-type 'double)
  '())
)
  (eval-scmc-global `(declare ,tmp-type var1)))

define-scmc-global

Codes in SCMC:

(define-scmc-global var
  val)

Conterpart in C:

No C counterpart for this structure. This operation will define a global variable named var and set it to val

Example:

(begin
  
(define-scmc-global tmp-type
  'double)

  (eval-scmc-global `(declare ,tmp-type var1)))

begin-map

Codes in SCMC:

(begin-map procedure list)

Conterpart in C:

This is a scheme procedure which is used to generate `(begin . ,(map structure

Example:

(begin (eval-scmc-global (begin-map
(lambda (v)
  (set! v 0.00000000000000000e+00))
'(v1 v2 v3))))

multi-concat

Codes in SCMC:

(multi-concat sym1 sym2 ...)

Conterpart in C:

This is a scheme procedure which is used to concatenate multiple strings or symbols.

Example:

(begin
  
(defmacro def-multi-variables (type . var-init)
  (begin-map
(lambda (x)
  (define var (car x))
  (define val (cadr x))
  `(,(multi-concat 'define- type) ,var ,val))
var-init))

  (def-multi-variables double (var1 init1) (var2 init2)))

pure-text

Codes in SCMC:

(pure-text text)

Conterpart in C:

text

To make sure that some syntaxes in C are not implemented in SCMC, we give a final pure-text structure, which is used to directly put string to the generated C code.

Example:

(begin (pure-text "\n#ifndef var\n#define var\n#endif\n"))