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"))