The Felix language is somewhat novel in that there are no primitive data types, not even bool. Instead, Felix uses binding definitions to declare abstract primitive data types, and bind them to concrete C++ data type.
Here is an example of a binding.
1: include "std"; 2: header """ 3: // This is C++ code! 4: class gauss; 5: 6: struct gauss { 7: int x; 8: int y; 9: 10: gauss() : x(0), y(0) {} 11: gauss(int _x, int _y) : x(_x), y(_y) {} 12: 13: gauss operator +(gauss z) const 14: { 15: return gauss(x+z.x, y+z.y); 16: } 17: gauss operator *(gauss z) const 18: { 19: return gauss (x *z.x - y*z.y, x*z.y + y*z.x); 20: } 21: }; 22: """; 23: 24: // Now the Felix binding 25: type gauss = "gauss"; 26: proc _set : &gauss * gauss = "*$1 = $2;"; 27: fun add: gauss * gauss -> gauss = "$1 + $2"; 28: fun mul: gauss * gauss -> gauss = "$1 * $2"; 29: fun mkgauss: int * int -> gauss = "gauss($1,$2)"; 30: fun real: gauss -> int = "$1.x"; 31: fun imag: gauss -> int = "$1.y"; 32: 33: // Now a pure Felix procedure 34: proc print(z:gauss) { 35: print "("; 36: print (real z); 37: print ", "; 38: print (imag z); 39: print ")"; 40: } 41: 42: // And two pure Felix functions 43: fun sqr(z:gauss):gauss = { 44: return z * z; 45: } 46: 47: fun norm(z:gauss): int = { 48: return 49: real z * real z + imag z * imag z 50: ; 51: } 52: 53: // Finally, some test code 54: val z1 = mkgauss(1,2); 55: val z2 = z1 + z1; 56: val z3 = sqr z2; 57: val n = norm z3; 58: print z1; endl; 59: print z2; endl; 60: print z3; endl; 61: print n; endl; 62:
First, the header statement
header """ .... """;specifies text to be emitted literally in the generated C++ header file: note the trailing semicolon. In this case, we emit a C++ class defining a basic gaussian integer type. There is also a 'body' keyword to literally emit code in the generated implementation (body) file.
Next, we define the felix type gauss.
type gauss = "gauss";The definition specifies the Felix name, gauss, and then the C++ name, which also happens to be gauss.
Then we define the semantics of our new primitive by primitive functions and procedures which allow us to manipulate it. Theses consists of a function or procedure name, a type, and the C++ code to generate when the function or procedure is called.
proc _set : &gauss * gauss = "*$1 = $2;"; fun add: gauss * gauss -> gauss = "$1 + $2"; fun mul: gauss * gauss -> gauss = "$1 * $2"; fun mkgauss: int * int -> gauss = "gauss($1,$2)"; fun real: gauss -> int = "$1.x"; fun imag: gauss -> int = "$1.y";In the definition string, $1 means the first argument of the argument tuple, and $2 means the second, etc.
If the type of an argument component is prefixed by a &, this means that a pointer is passed.
You should note that there is a predefined correspondence between Felix operators and certain function or procedure names. The ones we have used here are:
Operator Name = set + add * mul == eq
Finally, note carefully that functions bind to C++ expressions, whilst procedures bind to C++ statements. This is why the set procedure binding contains a ; inside the string, as well as after it. You can use a compound statement as well.