EXAMPLE:
$ ./sxcc.sh hello.sxc
$ ./a.out
Hello, World!
$ make fire # TODO: make fire come out
$ C-c # Control-C to get your terminal back
sxc is an s-expression based language source transpiler for C. By providing an S-expression based syntax, sxc will attempt to add Lisp-like macro and code generation capabilities to the C language (WIP).
Lisp as a language uses lists as data structures for it's code represenation and is a language designed to work with lists. This combination has allowed for the creation of 'macros', or programs that take an input program, analyze it and then transform or generate arbitrary code from it. This gives great power to the programmer to create new abstraction and control structures that are not available in the original language.
sxc makes C a programmable language, just like a Lisp, with a the mental programming model of C.
- transpilation from .sxc to .c sources
- source line gdb debugging of original sources
Install sbcl from http://sbcl.org.
Type make. Output will be in ./sxc.
Use the wrapper scripts ./sxc.sh and ./sxcc.sh to compile and build an executable from one or more .sxc files:
sxcc main.sxc lib.sxc
The result goes into ./a.out.
All options to sxcc are passed to the compiler other than file names.
The following is a basic "Hello World" program:
//
// hello.sxc
//
(#include <stdio.h>)
(int main ((int argc) (char (** argv)))
(printf "Hello, World!\n")
(return 0))
This can be compiled and run using the following:
./sxcc.sh -o hello hello.sxc && ./hello
A more complex example is adding a new control structure to the language, like a 'string switch', which is like a standard C switch but works with strings as arguments:
//
// sswitch - string switch
//
// a switch statement that works with strings
//
(macro sswitch (val &body cases)
(labels ((genswitch (cases)
(let* ((case (car cases))
(cmd (first case))
(val (second case))
(statements (cddr case))
condition)
(when case
(unless (listp val)
(setf val (list val)))
(mapcar (lambda (v)
(setf condition (append conditiion `((strcmp ,v ,val) &&))))
val)
(setf condition (append condition '(1)))
`(if ,condition
,@statements
(else
,@(genswitch (cdr cases))))))))
(genswitch cases))
(int main ((int argc) (char (** argv)))
(sswitch ([] argv 1)
(case ("a" "c")
(printf "The value is \"a\" or \"c\"\n"))
(case "d"
(goto e-label))
(case "b"
(printf "The value is \"b\"\n"))
(case "e"
(: e-label)
(printf "The value is \"d\" or \"e\"\n"))
(default
(printf "The value is neither \"a\", \"b\", \"c\", \"d\", or \"e\"\n")))
(return 0))
SEE ./tests/ FOR MORE SYNTAX EXAMPLES.
--
The basic syntax of sxc follows that of the Lisp family of languages.
The first argument to an s-expression is the function or keyword to be called or used, followed by it's arguments.
There are a some exceptions for convenience:
++x - pre-increment on variables x++ - post-increment on variables
This is due to the fact that all symbols are passed through directly to the C compiler, and you will find there are no exceptions to handle this in the sxc code.
Also, for array operations:
(?++ ...) - post increment on pointers (x++) (++? ...) - pre increment on pointers ((++x))
(?-- ...) - post decrement on pointers (x--) (--? ...) - pre decrement on pointers ((--x))
These are handled specially by sxc in the code.
A wrapper script 'sxcc.sh' is provided to simplify building and compilation of program sources into an executable.
You can find some things to translate in to SXC in the to-translate/ directory.
-- Burton Samograd busfactor1@icloud.com 2018