2017-10-09

sph-sc

compiles s-expressions to c

supports all c and future syntax with a quote form to insert arbitrary c strings

mostly a 1:1 mapping from sc to c - sc is c. after formatting the output is as if originally written in c. if you don't like sc, you can go back

a simpler c syntax

greatly simplifies the use of features with intricate c-syntax (preprocessor commands, multiline macros, function pointers, type definitions, nested if expressions,, ...)

declare function pointer variables like other variables. for example: (define mysub (function-pointer int char char*)) or (define (mysub a b) (int (function-pointer (function-pointer char int) int)))

use scheme-typical identifiers with the characters "-", "->", "?", "!" and more

command-line compiler and scheme library

"pre-let" form to define scoped preprocessor macros

"pre-include-once" form that defines preprocessor variables and does not include the same file id twice

"sc-include" and "sc-include-once" forms to include .sc files. optionally with the use of load-paths from a $SC_LOAD_PATH environment variable

supports docstrings

output is unformatted for faster processing and saved complexity. to format, programs like "clang-format" can be used

sc only outputs valid c syntax

can also be used as an intermediate language for transcompiling to c

modern c compilers indicate run-time errors with context, and particularly if the output code is auto-formatted, finding the source of errors is usually not more difficult compared to plain c

status: stable, been around for a while, easy to maintain and extend

questions, change requests, and issues are currently best communicated per email (see start page)

dependencies

>= guile 2

sph-lib

installation

manual

install all dependencies if there are some

download

download

alternatives: releases, git clone git://sph.mn/sph-sc, github

unpack

unpack the downloaded archive

for example

tar -xf sph-sc.tgz

-x is for extract

-f is for the source file name

install

cd sph-sc
su root
./exe/install

in most cases the installer only copies files

the install script might have a "--help" option that will list more install options

pacman

using aurget:

aurget -S --deps sph-sc-git

command-line application

$ sc --help

parameters
  options ... source-path ... destination-path
  options ... source-path
options
  --help | -h
  --interface

license

gpl3+, does not apply to generated code

other

filename extension for source files: .sc

the & operator is "address-of" or & just like in c

"sc-include" relative-paths are source-file relative unless they start with a slash

for the most standard c like output you might want to prefer "array-get" to simple pointer "deref" and avoid nested "if*" which create ternary expressions

example code from projects using sc

syntax

sc-expression and the c result. taken from the automated tests. the b32 types are not part of sc

(begin "\"\"")
->
"\"\"";

(begin "")
->
"";

(begin "a")
->
"a";

(begin a?)
->
a_p;

(begin a-b)
->
a_b;

(begin a+b)
->
a_and_b;

(begin a->b)
->
a_to_b;

(begin a!)
->
a_x;

(begin a!?<->)
->
a_x_p_less_to_;

(begin <)
->
<;

(begin #t)
->
1;

(begin -1)
->
-1;

(begin 1 (begin 2 3))
->
1;2;3;

(if* a (if* b c d) e)
->
(a?(b?c:d):e)

(if* a (if* (if* b c) d) e)
->
(a?((b?c:0)?d:0):e)

(if* (= a 3) (begin (set b+c 4) (myproc a b+c)) a)
->
((a==3)?((b_and_c=4),myproc(a,b_and_c)):a)

(if (= a 3) (exit 1) (return (bit-or b c)))
->
if((a==3)){exit(1);}else{return((b|c));}

(set a 1 b+2 2 c-3 3)
->
a=1;b_and_2=2;c_3=3;

(if* (not a) #t #f)
->
(!a?1:0)

(define a b32 1)
->
b32 a=1

(define a b32 b+2 b16 c-3 b0)
->
b32 a;b16 b_and_2;b0 c_3;

(define (pre-concat a b) b32 1)
->
b32 a##b=1

(define (abc) b32 (return 0))
->
b32 abc(){return(0);}

(define (abc d e) (b32 b64 b16) (return 0))
->
b32 abc(b64 d,b16 e){return(0);}

(define (abc d e) (b32 (pre-concat b 64) b16) (return 0))
->
b32 abc(b##64 d,b16 e){return(0);}

(set a 1)
->
a=1

(deref a 1)
->
(*(a+1))

(deref (deref a 1) 2)
->
(*((*(a+1))+2))

(not 1)
->
!1

(if* (not 1) a b)
->
(!1?a:b)

(and 1 2 3)
->
(1&&2&&3)

(= 1 2 3)
->
(1==2==3)

(equal? a 1)
->
(a==1)

(while #t 1 2 3)
->
while(1){1;2;3;}

(do-while #t 1 2 3)
->
do{1;2;3;}while(1)

(cond ((= a 1) (equal? b 2)) ((equal? c 3) #t))
->
if((a==1)){(b==2);}else{if((c==3)){1;};}

(cond* ((= a 1) (equal? b 2)) ((equal? c 3) #f #t) (else #t #f))
->
((a==1)?(b==2):((c==3)?(0,1):(1,0)))

(quote "var a = 3")
->
var a = 3

(return)
->
return

(return 1 2)
->
return(1,2)

(if 1 2 (begin 3 4 (return #t)))
->
if(1){2;}else{3;4;return(1);}

(deref a-b)
->
(*a_b)

(struct-get a b)
->
a.b

(struct-get a b c d e)
->
a.b.c.d.e

(struct-get (deref a) b)
->
(*a).b

(struct-pointer-get a b)
->
(*a).b

(struct-pointer-get a b c d)
->
(*a).b.c.d

(pre-define (my-macro a b) (if* a #t #f))
->
#define my_macro(a,b) (a?1:0)

(pre-define ob-ject 3)
->
#define ob_ject 3

(pre-if (equal? a b) (begin c d e) (begin f g))
->
#if (a==b)
c;d;e;
#else
f;g;
#endif

(pre-undefine my-macro)
->
#undef my_macro

(pre-concat a b cd e)
->
a##b##cd##e

(pre-if-not-defined a b c)
->
#ifndef a
b;
#else
c;
#endif

(pre-if-defined a b c)
->
#ifdef a
b;
#else
c;
#endif

(define-type mytype int)
->
typedef int mytype

(define-type type-name (function-pointer type-return type-argument-1 type-argument-2))
->
typedef type_return(*type_name)(type_argument_1,type_argument_2)

(address-of a-b)
->
&a_b

(convert-type abc int)
->
((int)(abc))

((convert-type abc function-t) d e)
->
((function_t)(abc))(d,e)

(bit-shift-right 1 2)
->
(1>>2)

(bit-shift-left 1 2)
->
(1<<2)

(bit-not a-b)
->
~a_b

(struct (a (unsigned int)) (b (unsigned char) 3))
->
struct{unsigned int a;unsigned char b:3;}

(struct testname (a (uns-igned int)) (b (unsigned char) 3))
->
struct testname{uns_igned int a;unsigned char b:3;}

(struct (a-b (function-pointer b c-e d)) (b i-nt))
->
struct{b(*a_b)(c_e,d);i_nt b;}

(define-type e (struct (a-b (function-pointer b c-e d)) (b i-nt)))
->
typedef struct{b(*a_b)(c_e,d);i_nt b;} e

(union (a (unsigned int)) (b (unsigned char) 3))
->
union{unsigned int a;unsigned char b:3;}

(pre-let (a 1 b 2) (+ a b))
->
#define a 1
#define b 2
(a+b);
#undef a
#undef b

(pre-let (a 1) a)
->
#define a 1
a;
#undef a

(pre-let ((a b) 1) a)
->
#define a(b) 1
a;
#undef a

(pre-let ((a b) 1 (c d) 2) a)
->
#define a(b) 1
#define c(d) 2
a;
#undef a
#undef c

(let* ((a size_t 1) (b size_t 2) (c 3)) (set c 7) (return (if* 4 5 6)))
->
{size_t a=1;size_t b=2;c=3;c=7;return((4?5:6));}

(pre-define (->test a b) c)
->
#define _to_test(a,b) c

(define-array aa size-t (1))
->
size_t aa[1]

(define-array aa size-t (1 2 3))
->
size_t aa[1][2][3]

(define-array aa size-t (b-b))
->
size_t aa[b_b]

(define-array aa size-t (2) 3 4)
->
size_t aa[2]={3,4}

(array-get aaa 3)
->
(*(aaa+3))

(array-get aaa 3 4 5)
->
(*(aaa+((3*4)+5)))

(define-array aa size-t (1 2 3) (array-literal (array-literal -4 5 test-c) (array-literal 6 7 8)))
->
size_t aa[1][2][3]={{{-4,5,test_c},{6,7,8}}}

(array-set aa 0 11 1 22 2 33)
->
(*(aa+0))=11;(*(aa+1))=22;(*(aa+2))=33;

(pre-include "./a/b.c")
->
#include "./a/b.c"

(pre-include "../a/b.c")
->
#include "../a/b.c"

(pre-include "a/b.c")
->
#include <a/b.c>

(pre-include "bb.h")
->
#include <bb.h>

(pre-include "a" "b" "c")
->
#include <a>
#include <b>
#include <c>

(pre-include-once a "./a.c" b "b.h")
->
#ifndef sc_included_a
#include "./a.c"
#define sc_included_a
#endif
#ifndef sc_included_b
#include <b.h>
#define sc_included_b
#endif

(pre-include-once a "./a.c")
->
#ifndef sc_included_a
#include "./a.c"
#define sc_included_a
#endif

(pre-define-if-not-defined abc 3 def 4)
->
#ifndef abc
#define abc 3
#endif
#ifndef def
#define def 4
#endif

(case = myvalue ((3 2) #t) (4 #f) (("a" "b") #t #t) (else #f #f))
->
if((((3==myvalue))||((2==myvalue)))){1;}else{if((4==myvalue)){0;}else{if(((("a"==myvalue))||(("b"==myvalue)))){1;1;}else{0;0;};};}

(case* = myvalue ((3 2) #t) (4 #f) (("a" "b") #t #t) (else #f #f))
->
((((3==myvalue))||((2==myvalue)))?1:((4==myvalue)?0:(((("a"==myvalue))||(("b"==myvalue)))?(1,1):(0,0))))

(enum test (a b c d e))
->
enum test{a,b,c,d,e}

(define-type test-t (enum (a b c)))
->
typedef enum{a,b,c} test_t

(enum (a b c d e))
->
enum{a,b,c,d,e}

(begin (enum (a b c d e)) (define a int))
->
enum{a,b,c,d,e};int a;

(enum (a b (c 3) d (e 4)))
->
enum{a,b,c=3,d,e=4}

(pre-stringify abc)
->
#abc

(array-literal 1 "2" 3 4)
->
{1,"2",3,4}

(struct-literal (a 1) (b "2"))
->
{.a=1,.b="2"}

(struct-literal a 1)
->
{a,1}

(function-pointer void vo-id*)
->
void(*)(vo_id*)

(convert-type a (function-pointer void void*))
->
((void(*)(void*))(a))

(define a (function-pointer (function-pointer (unsigned int) float) double))
->
unsigned int(*(*a)(double))(float)

(define a (function-pointer (function-pointer (function-pointer int float) double) (long long int)))
->
int(*(*(*a)(long long int))(double))(float)

(define (a) (function-pointer b32 b64) #t)
->
b32(*a())(b64){1;}

(define (a b) ((function-pointer b-32 b64) (function-pointer b-32 b64)) #t)
->
b_32(*a(b_32(*b)(b64)))(b64){1;}

(define (a b) ((function-pointer (function-pointer b32 b-16) b8) b-64))
->
b32(*(*a(b_64 b))(b8))(b_16)

(pre-define-if-not-defined (a b c) #t)
->
#ifndef a
#define a(b,c) 1
#endif

(define (a) b0 "test-docstring")
->
/** test-docstring */
b0 a(){}

(define (a b c) (b0 b0 b0) "test-docstring" (+ b c))
->
/** test-docstring */
b0 a(b0 b,b0 c){(b+c);}

(pre-define (a b) "test-docstring" (+ b c) 3)
->
/** test-docstring */
#define a(b) (b+c);\
  3

(and a (set b (c d)))
->
(a&&(b=c(d)))

(label abc (define a b32 3) (+ a b))
->
abc:b32 a=3;(a+b);

import name

(sph lang sc)

exports

sc->c

procedure

signature

a [load-paths] ->

expression [(string ...)] -> string

sc-default-load-paths

variable

sph-lang-sc-description

variable

tags: programming guile library scheme overview start q1 computer project sc sph-sc