Pliant programming language is a superset of C, so that you can translate a C program to Pliant, mostly line by line.
Parenthesing and blocs
When calling a function, Pliant puts the function name inside parenthesis. Then C uses coma for arguments separation, whereas Pliant just uses space. Let's assume that 'fb' is a function with two numeric arguments:
C
Pliant
fb(12,20);
fb 12 20 or (fb 12 20)
C
Pliant
fa(5)
fa 5
fa:5 or fa:5
In Pliant colon is just a way to replace parenthesis when there is only one argument.
Pliant
same as
a:b a:b:c:d
(a b) (((a b) c) d)
Pliant uses indentation for blocs, just like Python, whereas C uses { } Then, in C each instruction ends with a semicolon whereas in Pliant semicolon is only used as a separator when several instructions are provided on the same line.
C
Pliant
<instruction1> ; <instruction2> ;
<instruction1> ; <instruction2> ;
if <condition> { <do_something> }
<instruction1> <instruction2>
<instruction1> ; <instruction2>
if <condition> <do_something>
In Pliant, the standard indentation is two spaces.
Also, it is still possible to use { } in Pliant for one line compact blocs :
C
Pliant
if <condition> { <instr1> ; <instr2> ; <instr3> ; }
if <condition> { <instr1> ; <instr2> ; <instr3> }
Pliant comment is starting with hastag character and ending at the end of the line :
As you can see in the following table, C should be 64 bits on 64 bits plateforms, but it's not. This makes C a crap language :
Plateform
C pointer size
C int size
C long size
C size_t size
Pliant Int size
32 bits Unix 32 bits Windows 64 bits
32 bits 64 bits 64 bits
32 bits 32 bits 32 bits
32 bits 64 bits 32 bits
32 bits 64 bits 64 bits
32 bits 64 bits 64 bits
So, we have potential trouble when translating C 'int' and 'unsigned int' to Pliant. When they are used to store an arbitrary size number, they should be translated as 'Int' and 'uInt', but sometime, they might truely mean 'int32_t' and 'uint32_t' so be translated to 'Int32' and 'uInt32'.
Then, C 'long' and 'unsigned long' should also be translated to Pliant 'Int' and 'uInt', but in very rare cases for code intended only for Windows 64 bits, they might also mean 'int32_t' or 'uint32_t' so must be translated to 'Int32' and 'uInt32'
C type
Should mean in the program
But might mean
Sould be translated as Pliant
int long size_t
32 bits is ok, but 64 bits is also 32 bits on 32 bits plateforms, 64 bits on 64 bits plateforms
strictly 32 bits 32 bits under Windows 64 bits
Int Int uInt
When defining structure data types, it is even more difficult to select if C 'int' must be translated as Pliant 'Int' or as 'Int32'.
Here is the summary of types translation from clean C code to Pliant :
C
Pliant
unsigned char
uInt8
char
Int8
unsigned short
uInt16
short
Int16
unsigned int
most time uInt, but simetime uInt32
int
most time Int, but sometime Int32
unsigned long
uInt, absolutely valid only under Unix
long
Int, absolutely valid only under Unix
unsigned long long
uInt64
long long
Int64
size_t
uInt
Pliant uses 'var' instruction to declare local variables. All local variables must be declared before use. Pliant uses 'gvar' to delcare global variables. If you need to declare several local variables and provide initial values, then with Pliant you need one instruction per local variable :
C
Pliant
long a, b, c;
long a=0, b=12, c=15;
var Int a b c
var Int a := 0 ; var Int b := 12 ; var Int c := 15
Pliant casting is using 'cast' instruction:
C
Pliant
Comment
unsigned long a; long b; unsigned long c; c = b; c = a * (unsigned long)b;
var uInt a ; var Int b ; var uInt c c := cast b uInt c := a * (cast b uInt)
Forces to compute unsigned multiplication.
Arithmetic
C affectation is equal sign whereas Pliant affectation is colon followed by equal.
C
Pliant
long a, b, c; c = a + b; c = a-b; c = a*b; c := a/b; c = a%b; c := a << b; c = a >> b; c = a & b; c := a | b; c = a ^ b; c = ~ a;
var Int a b c c := a+b c := a-b c := a*b c := a\b c := a % b c := a * 2^b c := a \ 2^b c := a .and. b c := a .or. b c := a .xor. b c := .not. a
In Pliant backslash is integer division and slash is floatting point division.
Here are Pliant equivalents of some C combined operators, assuming that 'fa' is a function using a single numerical argument :
C
Pliant
long a, b; a += b; a -= b; a *= b; a /= b; b = f(a++); b = f (++a); b = f(a--); b = fa(--a)
var Int a b a += b a -= b a *= b a \= b b := f a ; a += 1 a += 1 ; b := f a b := fa a ; a -= 1 a -= 1 ; b := f a
Comparisons and booleans
C equality comparison is double equal whereas Pliant equality comparison is equal sign.
C
Pliant
long a, b; a == b a < b a <= b a > b a >= b a != b
var Int a b a = b a < b a <= b a > b a >= b a <> b
In C, a boolean is just an integer, and it is assumed to be true if it's value is non zero. In Pliant, a boolean has type 'CBool' and is semantically not a numeric value.
C
Pliant
int a, b; a && b a || b ! a 1 0
var CBool a b a and b a or b not a true false
It is not recommanded, but if you truely need to handle a Pliant boolean as a numerical value where true means non zero, you can :
C
Pliant
Comment
int c, i; i = c; i = c; i = c; c = i; c = i; c = i;
var CBool c ; var Int i i := c i := shunt c 1 0 i := addressof:c map Int c := i c := i<>0 addressof:c map Int := i
Forbidden in Pliant, a boolean is not a numerical value. Allowed in Pliant, but not strictly equivalent of the C code because C 'c' might not be zero or one. Allowed in Pliant. Forbidden in Pliant, a boolean is not a numerical value. Allowed in Pliant, but not strictly equivalent of the C code because C 'i' might not be zero or one. Also allowed in Pliant.
Controls
C
Pliant
if <condition> { <do_something> } else { <do_another_thing> }
while <condition> { <do_something> }
int i, s; for (int i=0; i<s; i++) { <do_something> }
for (int i=6; i<=12; i += 2) { <do_something> }
do { <do_something> } while <condition>;
if <condition> <do_something> else <do_another_thing>
while <condition> <do something>
var Int s for (var Int i) 0 s-1 <do_something>
or
var Int i s for i 0 s-1 <do_something>
for (var Int i) 6 12 step 2 <do_something>
part do_while <do_something> if <condition> restart do_while
Pliant has no direct equivalent to C 'goto', 'continue' and 'break' instructions. On the other hand, a Pliant program can define 'part' blocs identified by a name, then within the bloc, use 'leave' instruction to jump at the end of the bloc, or 'restart' instruction to jump at the beginning of it. This can be used to emulate C 'continue' and 'break' instructions, and most of C 'goto' use cases.
C
Pliant
int j = 50; for (int i= 0; i < 10; i++) { if (i == 2) continue; j += i;
var Int j := 50 for (var Int i) 0 9 part the_lap if i=2 leave the_lap j += i
int j = 50; for (int i=0; i < 10; i++) { if (i == 7) break; j += i;
var Int j := 50 part the_loop for (var Int i) 0 9 if i = 7 leave the_loop j += i
Pliant equivalent to C question mark then colon is 'shunt':
Pliant uses 'type' and 'field' to declare structures :
C
Pliant
typedef struct { void *a; long s; } Foo;
type Foo field Address a field Int s
C compilers tend to add implicit padding to align fields according to their size, whereas with Pliant, padding must be explicit. Here is an example :
C
Pliant
typedef struct { int8_t a; int32_t b; } Bar;
type Bar field Int8 a field (Array Int8 3) padding field Int32 b
Declaring a function
Pliant uses 'arg', 'arg_rw', 'arg_w' instructions in the function body to specify arguments types and minus followed by greater than sign to specify the name of the optional result, whereas modern C provides arguments types in the function header, provides no name for the result, and specifies void type for the result if the function has no result. For the result, Pliant can also use 'arg_R', 'arg_RW' and 'arg_C'. They mean that the address of the result will be returned. 'arg_R' means that the content of the result should not be modified by the caller, as opposed to 'arg_RW'. Then 'arg_C' means that if the caller has only read access to the first argument, it shoud not modify the result, but if it has read write access to the first argument, then it is allowed to modify the result. The 'C' of 'arg_C' stands for consistent.
C
Pliant
Alternative, since Pliant provides a name for the result
long fact(long x) { if (x == 0) return 1; else return x * fact(x-1); }
function fact x -> y arg Int x y if x = 0 return 1 else return x * (fact x-1)
function fact x -> y arg Int x y if x = 0 y := 1 else y := x * (fact x-1)
In Pliant 'arg' means that the argument will be only read, 'arg_rw' means that it will be modified by the called function. Then 'arg_w' is a special case of 'arg_rw' specifying that the value of the argument at the end of the function does not depend on it's value at the beginning, that is that the argument will be completely written, not just updated.
Pliant passes an argument by value only if it's atomic, and futhermore it is either read only or it is the result. Pliant atomic data types are mostly 'uInt', 'Int', 'Address', 'Pointer:xxx' where 'xxx' is any Pliant type. More precisely, Pliant atomic data types are the data types that have the same size as Pliant 'Int', and futher more do not have initialisation, copy or destruction special routines.
In order to illustrate all these rules, here are various arguments translation form C to Pliant, assuming 'Foo' is the structure type we previously declared:
C argument
Pliant argument
Why ?
long a long *a constant Foo *a Foo *a Foo a
arg Int a arg_rw Int a arg Foo a arg_rw Foo a Not possible with Pliant
A read only atomic argument, passed by value. A read write atomic argument, passed by address. A read only non atomic argument, passed by address. A read write argument, passed by address. Because Pliant always passes non atomic arguments by address.
C 'void *' is the equivalent of Pliant 'Address' data type, and Pliant 'Address' data type is atomic, so according to the previously specifyed rules, in a function prototype, C 'void *' and 'void **' will be translated according to the following table :
C argument
Pliant argument
Why ?
void *a const void * void **a
arg Address a arg Address a arg_rw Address a
Pliant 'arg' means that the function will not change the passed memory address, but it may well change the memory area it points. Pliant cannot specfiy that the pointed memory area will not be modified, but the translation is ok. Pliant 'arg_rw' means that the function might change the pointer, not only the memory it points.
Here is another example of a function prototype translation :
C
Pliant
void my_function(const Foo *a, int b, void *c, long *r) { ... }
function my_function a b c r arg Foo a ; arg Int b ; arg Address c ; arg_rw Int r ...
Lastly, a Pliant argument declared with 'arg', as opposed to 'arg_rw', is read only also inside the function, as opposed C :
C
Invalid Pliant code
Valid Pliant code
void some_function(long a,long *b) { while (a > 0) { *b := *b + a; a -= 3;
} }
function some_function a b arg Int a ; arg_rw Int b while a > 0 b := b + a a -= 3 # Modifying 'a' is not allowed
function some_function a b arg Int a ; arg_rw Int b var Int wa := a while wa >= 0 b := b + wa aw -= 3
Pointers
Pliant pointers are automatically dereferenced. In Pliant, colon followed by greater than sign means set the pointer and colon followed by equal sign means set the pointed value:
C
Pliant
long *a, *b, c; a = &c ; b = a; *b = *a ; c = *a; b = a + 3; c = a[5];
var Pointer:Int a b ; var Int c a :> c b :> a b := a c := a b :> addressof:a map Int 3 c := addressof:a map Int 5
If 'fb' is a function with two numerical arguments, you have to write : fb (addressof:c map Int) 5 and not just: fb addressof:a map Int 5 because without parenthesis, 'fb' would be assumed to have four arguments.
Now the double pointers :
C
Pliant
long **a, **b, *p, c; a = &p ; b = a; *b = *a ; **b = **a;
var (Pointer Pointer:Int) a b ; var Pointer:Int p ; var Int c a :>> p b :>> a b :> a b := a
Pliant has a special type 'Address' for storing a memory address, whereas C code tend to use 'void *':
C
Pliant
Comment
void *a, *t; long *p, v; a = (void *)&v; p = (long *)a; *p = *(long *)a; *p = ((long *)a)[5]; *p = *((long *)a+5) t = (void *)((int *)a+3); a = NULL; a = 0; p = null; if (a == NULL) { ... } if (p == NULL) { ... }
var Address a t ; var Pointer:Int p ; var Int v a := addressof v p :> a map Int p := a map Int p := a map Int 5 p := (a translate Int 5) map Int t := a translate Int 3 a := null a := cast 0 Address p :> null map Int if a=null ...
if addressof:p = null ... or if not exists:p ...
Same as previous, both in C and Pliant.
Pliant requires explicit casting for turning an integer to an address or a pointer.