Skip to content

Lisp C Compiler, Lisp-like syntax for writing C code in addition of some forms and pointer managements

License

Notifications You must be signed in to change notification settings

saman-pasha/LCC

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

71 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

lcc

Lisp C Compiler, Lisp-like syntax for writing C code in addition of some forms and pointer managements.

Instruction

  • Install SBCL.
  • lcc as default uses Libtool for compiling and linking C code. If you like it just install it for your platform and put it in the PATH environment variable. Compiler and linker could be set in lcc-config.lisp file.
  • Download and copy lcc folder to ~/common-lisp for enabling ASDF access to lcc package.
  • Write your own lcc code and save it in .lcc or .lisp extension.
  • Copy lcc.lisp file from source folder into your project path.
  • Send your file as an argument to lcc.lisp. sbcl --script lcc.lisp test.lisp
  • If you are using EMACS editor, copy mode.lisp file content into .emacs file for syntax highlighting.

Identifiers

(variable int amount)
(variable double total)
int amount;
double total;

Constants

(variable const int SIDE . 10)
const int SIDE = 10;

Operators

Arithmetic

lcc Operator C Operator
+ +
- -
* *
/ /
% %
(set total (+ total amount))

(let ((int i . 3)
      (int j . 7)
      (int k))
  (set k (+ i j)))
total = total + amount;

{
  int i = 3, j = 7, k;
  k = i + j;
}

Increment and Decrement

lcc Operator C Operator
++ prefix ++
-- prefix --
++# postfix ++
--# postfix --
(target "main.c" 
  ()
  (include <stdio.h>)

  (function main ()
    (let ((int a . 5)
          (int b . 5))
          
      ;; Print them and decrementing each time.
      ;; Use postfix mode for a and prefix mode for b.
      
      (printf "\n%d %d" (--# a) (-- b))
      (printf "\n%d %d" (--# a) (-- b))
      (printf "\n%d %d" (--# a) (-- b))
      (printf "\n%d %d" (--# a) (-- b))
      (printf "\n%d %d" (--# a) (-- b)))))
#include <stdio.h>

void main()
{
  {
    int a = 5, b = 5;
    
    //Print them and decrementing each time.
    //Use postfix mode for a and prefix mode for b.
    
    printf("\n%d %d", a--, --b);
    printf("\n%d %d", a--, --b);
    printf("\n%d %d", a--, --b);
    printf("\n%d %d", a--, --b);
    printf("\n%d %d", a--, --b);
  }
}

Relational

lcc Operator C Operator
== ==
!= !=
> >
< <
>= >=
<= <=

Logical

lcc Operator C Operator
and &&
&& &&
or ||
|| ||
not !
! !

Bitwise

lcc Operator C Operator
<< <<
>> >>
~ ~
bitand &
& &
xor ^
^ ^
bitor |
| |

Assignment

lcc Operator C Operator
set =
= =
+= +=
-= -=
*= *=
/= /=
%= %=
<<= <<=
>>= >>=

Conditional

lcc Operator C Operator
? ?:
(set a (? (== b 2) 20 30))
a = (b == 2) ? 20 : 30;

Special

lcc Operator C Operator
sizeof sizeof()
addressof &
contentof *

Data Types

ANSI C provides three types of data types:

  • Primary(Built-in) Data Types: void, int, char, double and float.
  • Derived Data Types: Array, References, and Pointers.
  • User Defined Data Types: Structure, Union, and Enumeration.

lcc supports declaration and definition of all ANCI C data types.

lcc Data Type C Data Type
void void
bool bool
char char
uchar unsigned char
short short
ushort unsigned short
int int
uint unsigned int
long long
ulong unsigned long
llong long long
ullong unsigned long long
i8 int8_t
u8 uint8_t
i16 int16_t
u16 uint16_t
i32 int32_t
u32 uint32_t
i64 int64_t
u64 uint64_t
i128 __int128
u128 unsigned __int128
float float
double double
real long double

Variable

(let ((double price . 500.4)                         ; atom initialization
      (double price_array [] . '{100.2 230.7 924.8}) ; list initialization
      (double price_calc . #'(calculate_price))))    ; initialization by output of a function
{
  double price = 500.4;
  double price_array [] = {100.2, 230.7, 924.8};
  double price_calc = calculate_price();
}

Free Variable Declaration and Initialization

A free variable can has some attributes or storage class. each attribute enclosed in braces or parentheses.

  • {auto}
  • {register}
  • {static}
  • {extern}
{auto} (variable int width)
{register} (variable int height . 5)
(variable char letter . #\A)
(variable float age)
{extern} (variable float area)
{static} (variable double d)

;; actual initialization
(set width 10)
(set age 26.5)
auto int width; 
register height = 5;
char   letter = 'A';
float  age;
extern float area;
static double d;

/* actual initialization */
width = 10;
age = 26.5;

Scoped Variable Declaration and Initialization

A scoped variable can has some attributes or storage class. each attribute enclosed in braces or parentheses.

  • {auto}
  • {register}
  • {static}
(let ({static} (int width . 3)
      {register} (int height . 4))
  (printf "area: %d" (* width height)))
{
  static int width = 3;
  register int height = 4;
  printf("area: %d", width * height);
}

Assignment

(set width 60)
(set age 35)
width = 60;
age = 31;
(target "main.c"
  ()
  (include <stdio.h>)
  
  (function main ()
    (let ((int age . 33))
      (printf "I am %d years old.\n" age))))
#include <stdio.h>

void main()
{
  {
    int age = 33;
    printf("I am %d years old.\n", age);
  }
}

Type Casting

(target "main.c"
  ()
  (include <stdio.h>)
  (function main ()
    (let ((float a))
      (set a (cast float (/ 15 6)))
      (printf "%f" a))))
#include <stdio.h>
main ()
{
  {
    float a;
    a = (float) 15 / 6;
    printf("%f", a);
  }
}

Program Structure

lcc program involves one or many target form. targets are translating it's content forms to C code. each target must has a target c file, and a list of feature arguments.

Features

All features could be omitted or if available accept #t for default behaviour or #f for do nothing.

  • :std: writes standard libraries inclusion at top of target file.
(target "main.c"
  (:std #t)
  ;; some forms
  )
#include <stdio.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
  • :compile: used for compiling target file. Dafault behaviour is -c target.c. Could be a list of arguments that will send to compiler which has been set in lcc-config.lisp.
  • :link: used for linking and builing target file as library or executable. It has not dfault behaviour. Could be a list of arguments that will send to linker which has been set in lcc-config.lisp.
;; MyMath library declaration
(target "mymath.h"
  (:compile #f)

  (guard __MYMATH_H__
    {declare} (function obj1_does ((int) (int)) (returns int))
    {declare} (function obj2_does ((int) (int)) (returns int))
    {declare} (function obj3_does ((int) (int)) (returns int))))

;; Default compilation
(target "obj1.c"
  (:compile #t)
  (include "mymath.h")
  (function obj1_does ((int x) (int y)) (returns int)
	    (return (+ x y))))

;; Custom compilation
(target "obj2.c"
  (:compile ("-c" "obj2.c" "-o" "objmul.o"))
  (include "mymath.h")
  (function obj2_does ((int x) (int y)) (returns int)
	    (return (* x y))))

;; Library creation and linking
(target "obj3.c"
  (:compile #t :link ("-o" "libMyMath.la" "obj1.lo" "objmul.lo" "obj3.lo"))
  (include "mymath.h")
  (function obj3_does ((int x) (int y)) (returns int)
	    (return (obj1_does (obj2_does x y) (obj2_does x y)))))

;; Executable creation and linking
(target "main.c"
  (:std #t :compile #t :link ("-o" "CompileTest" "main.lo" "-lMyMath"))
  (include "mymath.h")
  (function main ((int argc)
		  (char * argv []))
	    (if (!= argc 3)
		(block
		 (printf "two digits needed!")
		 (return EXIT_FAILURE)))
	    (let ((int x . #'(atoi (nth 1 argv)))
		  (int y . #'(atoi (nth 2 argv))))
	      (printf "MyMath lib outputs: %d\n" (obj3_does x y)))
	    (return EXIT_SUCCESS)))
lcc: compiling target mymath.h
lcc: compiling target obj1.c
libtool: compile:  gcc -g -O -c obj1.c  -fPIC -DPIC -o .libs/obj1.o
libtool: compile:  gcc -g -O -c obj1.c -o obj1.o >/dev/null 2>&1
lcc: compiling target obj2.c
libtool: compile:  gcc -g -O -c obj2.c  -fPIC -DPIC -o .libs/objmul.o
libtool: compile:  gcc -g -O -c obj2.c -o objmul.o >/dev/null 2>&1
lcc: compiling target obj3.c
libtool: compile:  gcc -g -O -c obj3.c  -fPIC -DPIC -o .libs/obj3.o
libtool: compile:  gcc -g -O -c obj3.c -o obj3.o >/dev/null 2>&1
libtool: link: rm -fr  .libs/libMyMath.a .libs/libMyMath.la
libtool: link: ar cr .libs/libMyMath.a .libs/obj1.o .libs/objmul.o .libs/obj3.o 
libtool: link: ranlib .libs/libMyMath.a
libtool: link: ( cd ".libs" && rm -f "libMyMath.la" && ln -s "../libMyMath.la" "libMyMath.la" )
lcc: compiling target main.c
libtool: compile:  gcc -g -O -c main.c  -fPIC -DPIC -o .libs/main.o
libtool: compile:  gcc -g -O -c main.c -o main.o >/dev/null 2>&1
libtool: link: gcc -g -O -o CompileTest .libs/main.o  /home/saman/Projects/LCC/trunk/.libs/libMyMath.a

Sections

  • Documentations: starts with semi-colon(s) ";"
;;; about a lisp file
;;;; author, licence and/or documentation about each target
(variable long height) ; description of a form
(function sqr ((double a)) 
  (returns double)
  ;; some commented code or documentation inside code
  (return (* a a)))
  • Preprocessor Forms: a form which starts with at-sign "@" and accepts one argument. code form is used for writing C code inside lcc.
(@define (code "SHA1_ROTL(bits, word) (((word) << (bits)) | ((word) >> (32-(bits)))"))

(struct SHA512Context
  (@ifdef USE_32BIT_ONLY)
  (member uint32_t Intermediate_Hash[(/ SHA512HashSize 4)]) ; Message Digest
  (member uint32_t Length[4])                               ; Message length in bits
  (@else)                                                   ; !USE_32BIT_ONLY
  (member uint64_t Intermediate_Hash[(/ SHA512HashSize 8)]) ; Message Digest
  (member uint64_t Length_High)
  (member uint64_t Length_Low)                              ; Message length in bits
  (@endif)                                                  ; USE_32BIT_ONLY
  (member int_least16_t Message_Block_Index)                ; Message_Block array index
  (member uint8_t Message_Block[SHA512_Message_Block_Size]) ; 1024-bit message blocks
  (member int Computed)                                     ; Is the hash computed?
  (member int Corrupted))                                   ; Cumulative corruption code
#define SHA1_ROTL(bits, word) (((word) << (bits)) | ((word) >> (32-(bits)))

typedef struct SHA512Context {
#ifdef USE_32BIT_ONLY
  uint32_t Intermediate_Hash [SHA512HashSize / 4];
  uint32_t Length [4];
#else
  uint64_t Intermediate_Hash [SHA512HashSize / 8];
  uint64_t Length_High;
  uint64_t Length_Low;
#endif
  int_least16_t Message_Block_Index;
  uint8_t Message_Block [SHA512_Message_Block_Size];
  int Computed;
  int Corrupted;
} SHA512Context;
  • Main Function: The main function is where program execution begins. Every lcc program must contain only one main function.

Decision Making

if

If form accepts 2 or 3 argument. condition, form for true evaluation of condition and form for false evaluation. third part(else) could be omitted. use block form if you need more forms in each part.

(let ((int a . 5)
      (int b . 6))
  (if (> a b)
     (printf "a is greater")
    (printf "maybe b is greater")))
{
  int a = 5;
  int b = 6;
  if (a > b)
    printf("a is greater");
  else
    printf("maybe b is greater");
}
(let ((int a . 5)
      (int b . 6))
  (if (> a b)
     (block
       (printf "a is greater")
       (set a (* a b)))
    (block
      (printf "maybe b is greater")
      (set b (* b a)))))
{
  int a = 5;
  int b = 6;
  if (a > b) {
    printf("a is greater");
    a = a * b;
  } else {
    printf("maybe b is greater");
    b = b * a;
  }
}

switch

(let ((int a))
  (printf "Please enter a number between 1 and 5: ")
  (scanf "%d" (addressof a))
  
  (switch a
    (case 1 (printf "You chose One")   (break))
    (case 2 (printf "You chose Two")   (break))
    (case 3 (printf "You chose Three") (break))
    (case 4 (printf "You chose Four")  (break))
    (case 5 (printf "You chose Five")  (break))
    (default (printf "Invalid Choice."))))
{
  int a;
  printf("Please enter a number between 1 and 5: ");
  scanf("%d", &a);
    
  switch (a) {
    case 1:
      printf("You chose One");
      break;
    case 2:
      printf("You chose Two");
      break;
    case 3:
      printf("You chose Three");
      break;
    case 4:
      printf("You chose Four");
      break;
    case 5:
      printf("You chose Five.");
      break;
    default:
      printf("Invalid Choice");
      break;
  }
}

Loops

while

(let ((int n . 1)
      (int times . 5))
  (while (<= n times)
    (printf "lcc while loops: %d\n" n)
    (++# n)))
{
  int n = 1, times = 5;

  while (n <= times) {
      printf("C while loops: %d\n", n);
      n++;
  }
}

do

(let ((int n . 1)
      (int times . 5))
  (do (<= n times)
    (printf "lcc do loops: %d\n" n)
    (++# n)))
{
  int n = 1, times = 5;

  do {
      printf("C do loops: %d\n", n);
      n++;
  } while (n <= times)
}

for

(for ((int n . 1)
      (int times . 5))
  (<= n times)
  (++# n)
  (printf "lcc for loops: %d\n" n))
for (int n = 1, int times = 5; (n <= times);) {
  n++;
  printf("lcc for loops: %d\n", n);
}

for-each

Static array:

(let ((int ages [] . '{20 22 24 26}))
  (for-each (int i) ages (/ (sizeof ages) (sizeof int))
    (printf "each age: %d\n" i)))
{
  int ages[] = {20, 22, 24, 26};
  for (int G4321 = 0; G4321 < sizeof(ages) / sizeof(int); G4321++) {
    int i = ages[G4321];
    printf("each age: %d\n", i);
  }
}

Dynamic array:

(function main ((int argc) (char ** argv))
  (for-each (char * arg) argv argc
    (printf "%s\n" arg)))
int main (int argc, char ** argv) 
{
  for (int G4321 = 0; G4321 < argc; G4321++) {
    char * arg = argv[G4321];
    printf("%s\n", arg);
  }
}

Function

lcc has some points on functions:

  • Use returns form for setting the return type. returns form must be first form of a function after arguments list. A fucntion without returns form will returns void instead of main which returns int.
  • Function's attributes must set in declaration time. each attribute enclosed in braces or parentheses.
    • {declare}
    • {static}
    • {inline}
    • {extern}
  • Each declared function defined a function pointer typedef named FunctionName_t.
(target "main.c"
  (:std #t)
  
  ;; function declaration
  {declare} (function addition ((int * a) (int * b)) (returns int))
  
  (function main ()
    ;; local variable definition
    (let ((int answer)
          (int num1 . 10)
          (int num2 . 5))
      
      ;; calling a function to get addition value
      (set answer (addition (addressof num1) (addressof num2)))
      (printf "The addition of two numbers is: %d\n" answer))
    (return 0))
  
  ;; function returning the addition of two numbers
  (function addition ((int * a) (int * b))
    (returns int)
    (return (+ (contentof a) (contentof b)))))
#include <stdio.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>

/* function declaration */
int addition(int *num1, int *num2);

int main()
{
  {
    /* local variable definition */    
    int answer;
    int num1 = 10;
    int num2 = 5;
    
    /* calling a function to get addition value */    
    answer = addition(&num1, &num2);
    printf("The addition of two numbers is: %d\n", answer);
  }
  return 0;
}

/* function returning the addition of two numbers */
int addition(int *a,int *b)
{
    return *a + *b;
}
{declare} (function function_pointer ((int) (int)))
void function_pointer (int, int);
typedef void (*function_pointer_t) (int, int);

Array

Define

(variable double amount [5])
double amount[5];

Initialize

(variable int ages [5] . '{22 23 24 25 26})
int ages[5] = {22 23 24 25 26};
(variable int myArray [5])

;; Initializing elements of array seperately
(for ((int n . 0))
  (< n (/ (sizeof myArray) (sizeof int)))
  (++# n)
  (set (nth n myArray) n))
int myArray[5];

// Initializing elements of array seperately
for(int n = 0; n < sizeof(myArray) / sizeof(int); n++)
{
  myArray[n] = n;
}

String

(variable char name [6] . '{#\C #\l #\o #\u #\d #\Null})
(variable char name []  . "Cloud")
(variable char * name   . "Cloud")
char name[6] = {'C', 'l', 'o', 'u', 'd', '\0'};
char name[]  = "Cloud";
char * name  = "Cloud";

Special Characters

#\Null #\Space #\Newline #\Tab #\Page #\Rubout #\Linefeed #\Return #\Backspace

Pointer

(variable int * width)
(variable int * letter)
int  *width;
char *letter;
(target "main.c"
  ()
  (include <stdio.h>)
  
  (function main ((int argc) (char * argv []))
    (let ((int n . 20)
          (int * pntr))        ; actual and pointer variable declaration
      (set pntr (addressof n)) ; store address of n in pointer variable
      (printf "Address of n variable: %x\n" (addressof n))
      
      ;; address stored in pointer variable
      (printf "Address stored in pntr variable: %x\n" pntr)

      ;; access the value using the pointer
      (printf "Value of *pntr variable: %d\n" (contentof pntr)))
    (return 0))
#include<stdio.h>

int main (int argc, char *argv[])
{
  {
    int n = 20, *pntr;  /* actual and pointer variable declaration */
    pntr = &n;          /* store address of n in pointer variable  */
    printf("Address of n variable: %x\n", &n);

    /* address stored in pointer variable */   
    printf("Address stored in pntr variable: %x\n", pntr);

    /* access the value using the pointer */   
    printf("Value of *pntr variable: %d\n", *pntr);
  }
  return 0;
}

Dynamic Memory Allocation

C dynamic memory allocation functions malloc(), calloc(), realloc(), free() are available. Other keyword alloc that works in let initialization part which automatically is checking pointer and freeing allocated memory at the end of let scope.

(let ((char * mem_alloc . #'(malloc (* 15 (sizeof char))))) ; memory allocated dynamically
  (if (== mem_alloc nil) (printf "Couldn't able to allocate requested memory\n"))
  (free mem_alloc))
{    
  char * mem_alloc = malloc(15 * sizeof(char)); /* memory allocated dynamically */
  if (mem_alloc == NULL) {
    printf("Couldn't able to allocate requested memory\n");
  }
  free(mem_alloc);
}

Allocation with alloc and equivalent code in C:

(let ((char * safe_alloc . #'(alloc (* 15 (sizeof char)))))
  (printf "Memory allocated safely\n"))
{
    char * safe_alloc = ((char *)malloc((15 * sizeof(char))));
    if (safe_alloc == NULL) printf("dynamic memory allocation failed! safe_alloc\n");
    printf("Memory allocated safely\n");
    free(safe_alloc);
}
(let ((int n_rows . 4)
      (int n_columns . 5)
      (int ** matrix . #'(alloc (* (* n_rows n_columns) (sizeof int)))))
  (printf "Matrix allocated\n"))
{
    int n_rows = 4;
    int n_columns = 5;
    int ** matrix = ((int **)malloc(((n_rows * n_columns) * sizeof(int))));
    if (matrix == NULL) printf("dynamic memory allocation failed! matrix\n");
    printf("Matrix allocated\n");
    free(matrix);
}

Allocation by alloc and equivalent calloc:

(let ((char * safe_alloc . #'(alloc 15 (sizeof char))))
  (printf "Memory allocated safely\n"))
{    
  char * safe_alloc = calloc(15, sizeof(char)); 
  if (safe_alloc == NULL) printf("dynamic memory allocation failed! safe_alloc\n");
  printf("Memory allocated safely\n");
  free(safe_alloc);
}

Structure

declares form is for declaring one or more variable(s) at the end of nested struct declaration just for anonymous structures. Use $ form for struct's member access and -> form for member access of pointer of struct.

(struct Course
  (member char WebSite [50])
  (member char Subject [50])
  (member int  Price))
  
(variable Course c1 . '{"domain.com" "Compilers" 100})
(variable Course * pc1 . #'(addressof c1))
  
(function print_course ()
  (printf "Course: %s in %s for %d$" 
    ($ c1 Subject) 
    ($ c1 WebSite)
    ($ c1 Price))
  (printf "Course: %s in %s for %d$" 
    (-> pc1 Subject) 
    (-> pc1 WebSite)
    (-> pc1 Price)))
typedef struct Course {
  char WebSite [50];
  char Subject [50];
  int Price;
} Course;

Course c1 = {"domain.com", "Compilers", 100};
Course * pc1 = &c1;

void print_course () {
  printf("Course: %s in %s for %d$", c1.Subject, c1.WebSite, c1.Price);
  printf("Course: %s in %s for %d$", pc1->Subject, pc1->WebSite, pc1->Price);
}

Union

declares form is for declaring one or more variable(s) at the end of nested union declaration just for anonymous unions. Use $ form for union's member access and -> form for member access of pointer of union.

(struct USHAContext
  (member int whichSha)                 ; which SHA is being used
  (union
    (member SHA1Context   sha1Context)
    (member SHA224Context sha224Context) 
    (member SHA256Context sha256Context)
    (member SHA384Context sha384Context) 
    (member SHA512Context sha512Context)
    (declares ctx)))
typedef struct USHAContext {
  int whichSha;
  union {
    SHA1Context sha1Context;
    SHA224Context sha224Context;
    SHA256Context sha256Context;
    SHA384Context sha384Context;
    SHA512Context sha512Context;
  } ctx;
} USHAContext;

Enum

(enum
  (shaSuccess . 0)
  (shaNull)            ; Null pointer parameter
  (shaInputTooLong)    ; input data too long
  (shaStateError)      ; called Input after FinalBits or Result
  (shaBadParam))       ; passed a bad parameter
enum {
  shaSuccess = 0,
  shaNull,
  shaInputTooLong,
  shaStateError,
  shaBadParam
};

Guard

(guard __STUDENT_H__
  (struct Student
    (member char name [50])
    (member char family [50])
    (member int  class_no)))
#ifndef __STUDENT_H__
#define __STUDENT_H__
typedef struct Student {
  char name [50];
  char family [50];
  int class_no;
} Student;
#endif /* __STUDENT_H__ */ 

Typedef

(typedef int * intptr_t)
typedef int * intptr_t;

C++ Compiler

C++ compiler could be used instead of C compiler then some features availables:

  • & modifier in function argument for pass by reference.
  • Default value for members of structs.
  • method form for defining a member function inside of structs.

Good Luck!

About

Lisp C Compiler, Lisp-like syntax for writing C code in addition of some forms and pointer managements

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published