├── README ├── cmacros.h ├── pyrite-lib.c ├── pyrite-lib.h ├── pyrite-project.c ├── pyrite-undefs.h ├── pyrite.h ├── test.c └── test.pyr /README: -------------------------------------------------------------------------------- 1 | 2 | Pyrite: 3 | A BASIC "compiler" implemented entirely using the C macro system 4 | 5 | This project is entirely public domain 6 | (I will not be held responsible for this madness.) 7 | 8 | Pyrite uses a largish number of C preprocessor macros to build a BASIC-style 9 | syntax layer on top of C. It requires a C99-compliant preprocessor (for the 10 | variadic macros), and for various reasons will not work with C++ due to the 11 | nature of the output code. Heavy-duty use of macros means that a powerful 12 | preprocessor like GCC's will probably give best results. 13 | 14 | Features: 15 | - BASIC-like syntax forms 16 | - garbage collection 17 | - exceptions 18 | - user-defined objects 19 | - arrays 20 | - string and collection methods (not yet included) 21 | 22 | The project consists of the following files: 23 | README : this document 24 | pyrite.h : the main include to use, defines BASIC syntax forms 25 | pyrite-lib.h : declares the Pyrite runtime functions and other things 26 | (included by pyrite.h) 27 | pyrite-lib.c : implements the runtime features declared in pyrite-lib.h; 28 | link against this when compiling 29 | cmacros.h : generic metaprogramming library (not directly related to Pyrite) 30 | pyrite-project.c : a C "wrapper" project file to allow writing whole programs 31 | using only Pyrite source code 32 | pyrite-undefs.h : #undef directives for the Pyrite macros most likely to mess 33 | up C code: necessary for writing C after a Pyrite block 34 | test.c : an example program that tests actually very little 35 | test.pyr : a minimal Pyrite program to demonstrate the project system 36 | 37 | You can compile the test file with: 38 | gcc -std=c99 pyrite-lib.c test.c -o test 39 | 40 | pyrite-project.c is a stub application intended to hide most of the necessary C 41 | boilerplate so that a program may be presented as though written entirely in 42 | BASIC. It sets up the GC, puts the command line arguments into a data structure, 43 | and passes them to a function called MAIN expected to be found in Pyrite code. 44 | Pass the name of your source file to GCC with -DPYRITE_PROJECT=\"myFile.pyr\" 45 | and it will include it in the right place. The only concession to the syntax of 46 | C is that the Pyrite code must still be wrapped in a BASIC() form. 47 | 48 | Pyrite code may be included in a C file in much the same way: #include the 49 | syntax definitions in pyrite.h, enclose Pyrite code in a BASIC() block, and 50 | #undef the syntax when done using pyrite-undefs.h, as it will otherwise 51 | interfere with the ability to write in normal C. 52 | 53 | 54 | Pyrite syntax: 55 | ================ 56 | 57 | Pyrite introduces a number of syntax forms, as follows. Short examples of all 58 | forms may also be found at the bottom of pyrite.h. 59 | 60 | Function definition: 61 | 62 | function NAME as (arg0 as TYPE, arg1 as TYPE) of RETTYPE do 63 | // ... 64 | end 65 | 66 | This form declares a function named NAME, with arguments arg0 and arg1 of type 67 | 'TYPE', returning a value of type RETTYPE. A nullary function takes 'none' as 68 | its argument list; a void function may have 'void' as its return type. 69 | There are also allowances for the initial declaration to more closely resemble 70 | a C function name, but that doesn't play well with the GC. 71 | 72 | Forward declaration: 73 | 74 | declare(f6 as (IntF, int) of void, 75 | f7 as (Vector3) of int) 76 | 77 | Forward declarations are required in the same way as in C, if a function is to 78 | be used before its point of definition. 79 | Nullary functions must be declared with an empty argument list here. 80 | 81 | Type declaration: 82 | 83 | functype(IntF, ((int), void) ) 84 | functype(Op, ((float, float), float)) 85 | arraytype(IntF) 86 | 87 | type MyType 88 | field x as int, y as int 89 | field f as IntF 90 | endtype 91 | 92 | Since variable declaration only works with named types, these forms must be used 93 | to declare types that can then be attached to variables. 94 | 95 | 'functype' declares a typedef for a function pointer type: in the above example 96 | it creates the types 'IntF', for functions that take one int and return void; 97 | and 'Op', for those that take two floats and return one float. 98 | 99 | 'arraytype' declares the a type for arrays containing the passed type. So the 100 | above example declares a new type named 'IntFArray' that can store IntF values. 101 | 'arraytype' is called automatically by 'type' and 'functype', and is not 102 | generally needed in user code. 103 | 104 | 'type' declares a record with the given fields. Records are always reference 105 | objects and, like strings and arrays, are garbage-collected. Record variables 106 | may be null; new values must be created with new(TypeName). 107 | 108 | Arrays and strings: 109 | 110 | a = array(int, 10) 111 | elem(a, 0) = foo 112 | bar = elem(a, 1) 113 | s = str("foobar") 114 | 115 | Arrays are created with the 'array' command, taking a type and a size. Their 116 | elements are accessed via the 'elem' operation, which is essentially the same as 117 | C's [] indexing. Strings literals are created using the str() form around a 118 | const char, or by using other string functions. 119 | 120 | Loops: 121 | 122 | while condition do 123 | //... 124 | end 125 | 126 | for x in 0 upto 10 do 127 | //... 128 | end 129 | 130 | for x in 10 downto 0 do 131 | //... 132 | end 133 | 134 | for x in 0 to 9 do 135 | //... 136 | end 137 | 138 | for x in 0 to 10 step 2 do 139 | //... 140 | end 141 | 142 | for e in list do 143 | //... 144 | end 145 | 146 | repeat 147 | //... 148 | forever 149 | 150 | repeat 151 | //... 152 | until (condition) 153 | 154 | There are a number of looping forms available. While loops are similar to their 155 | C counterparts. Repeat/forever is an infinite loop; repeat/until is similar to 156 | C's do/while except with a negative condition check. The argument to until must 157 | be parenthesized. 158 | 159 | There are several 'for' variants available. The first three all iterate over 160 | integers; the upto and downto forms stop short of their target, while 'to' 161 | will try to finish *on* the target. The 'to' form may also take an optional 162 | 'step' value (the step is implicitly 1 or -1 when using upto and downto). It 163 | is also possible to iterate over elements of a collection; for this to work, the 164 | index variable must be of the right type, and the collection must support the 165 | 'getIterator', 'hasNext' and 'next' functions (currently this functionality is 166 | not complete and Iterator is not defined). 167 | 168 | Selection: 169 | 170 | if a then b else c 171 | 172 | if a do 173 | b() 174 | elseif c do 175 | d() 176 | elsedo 177 | e() 178 | end 179 | 180 | select x in 181 | case a, b do 182 | print(a) 183 | case c, d do 184 | print(d) 185 | default 186 | print(0) 187 | end 188 | 189 | The "inline" if form at the top is limited in the same way as a braceless 'if' 190 | in C: it is not possible for its branches to contain multiple statements. Doing 191 | so will lead to program errors. Notice that the block-if form requires the use 192 | of 'elsedo' rather than 'else'. 'select' may be used to select an integer from 193 | a list of options: each is tried in sequence and the case values may be function 194 | calls, side-effectful, etc. 195 | 196 | Exceptions: 197 | 198 | try 199 | something() 200 | catch(Vector3) 201 | handle((Vector3)exception) 202 | catch(string) 203 | handle2((string)exception) 204 | end 205 | 206 | Any record object or array may be "thrown" as an exception value. Catch blocks 207 | select the appropriate branch by the runtime type of the thrown object. The 208 | object is retrieved from the read-only value 'exception' and should be cast to 209 | the right type. Exceptions are raised with the 'throw' command. If none of the 210 | catch blocks match, the exception will be re-raised (this may cause a program 211 | crash if the exception propagates past the outermost level of handlers!). 212 | 213 | Variable declaration: 214 | 215 | let 216 | i as int equal 10, j as int equal 20 217 | end 218 | 219 | var(a as int, b as float) 220 | 221 | with a as int, s as string do 222 | write(s, a) 223 | end 224 | 225 | Apart from function parameters, variables may be declared in three main ways. 226 | 'let' blocks declare variables, assign values (not optional), and add any non- 227 | primitive type variables to be tracked by the GC (don't use 'let' within a loop, 228 | it will cause chaos as well as being slow). 229 | 230 | 'var' statements simply declare an uninitialised variable slot. These do not 231 | hook into the GC system and must be added manually with 'gc_trackvar'. These are 232 | the main way to declare globals. 233 | 234 | 'with' blocks create a nested scope level. The variables are added to the GC 235 | system, and will go out of scope at the end of the block, freeing their values 236 | for collection if necessary. Note that it is *not* safe to jump out of a 'with' 237 | block using break or continue; use return or exceptions. 238 | 239 | Returning values: 240 | 241 | return expr end 242 | 243 | 'return' works much the same as in plain C, except that its expression must be 244 | terminated with an explicit 'end'. 245 | 246 | -------------------------------------------------------------------------------- /cmacros.h: -------------------------------------------------------------------------------- 1 | // 2 | // Simple (i.e. very limited) metaprogramming macros 3 | // 4 | 5 | #ifndef C_MACROS_H 6 | #define C_MACROS_H 1 7 | 8 | 9 | #define M_MAX_DEPTH 32 10 | 11 | #define M_NARGS(...) M_NARGS_(__VA_ARGS__, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, \ 12 | 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) 13 | #define M_NARGS_(_32, _31, _30, _29, _28, _27, _26, _25, _24, _23, _22, _21, _20, _19, _18, _17, \ 14 | _16, _15, _14, _13, _12, _11, _10, _9, _8, _7, _6, _5, _4, _3, _2, _1, N, ...) N 15 | 16 | #define M_2ORMORE(...) M_NARGS_(__VA_ARGS__, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \ 17 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0) 18 | 19 | #define M_ILIST (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, \ 20 | 17, 18, 19, 20, 21, 23, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32) 21 | 22 | #define M_ID(...) __VA_ARGS__ 23 | #define M_FIRST(...) M_FIRST_(__VA_ARGS__) 24 | #define M_REST(...) M_REST_(__VA_ARGS__) 25 | #define M_FIRST_(A, ...) A 26 | #define M_REST_(A, ...) __VA_ARGS__ 27 | #define M_FIRST2(A, B, ...) A, B 28 | #define M_REST2(A, B, ...) __VA_ARGS__ 29 | #define M_ROT(A, ...) __VA_ARGS__, A 30 | #define M_REVERSE(...) M_RECUR(M_REVERSE_, M_NARGS(__VA_ARGS__), (__VA_ARGS__)) 31 | #define M_REVERSE_(...) (M_REST(__VA_ARGS__)), M_FIRST(__VA_ARGS__) 32 | #define M_CONC(A, B) M_CONC_(A, B) 33 | #define M_CONC_(A, B) A##B 34 | #define M_DIV2(N) M_CONC(M_DIV2_, N) 35 | #define M_DIV3(N) M_CONC(M_DIV3_, N) 36 | #define M_DIV4(N) M_CONC(M_DIV4_, N) 37 | #define M_DECR(N) M_CONC(M_DECR_, N) 38 | #define M_IF(P, T, E) M_CONC(M_IF_, P)(T, E) 39 | #define M_IF_0(T, E) M_ID E 40 | #define M_IF_1(T, E) M_ID T 41 | #define M_ENLIST(X, N) M_DO_1(M_REST M_ENLIST_1(X, N)) 42 | #define M_ENLIST_1(X, N) (M_CONC(M_NEST_, N)(M_ENLIST_2, X)) 43 | #define M_ENLIST_2(...) M_FIRST(__VA_ARGS__), __VA_ARGS__ 44 | 45 | #define M_HEAD(...) M_REVERSE(M_REST(M_REVERSE(__VA_ARGS__))) 46 | #define M_TAIL(...) M_FIRST(M_REVERSE(__VA_ARGS__)) 47 | #define M_ID2(A) M_ID A 48 | #define M_STR(S) M_STR_(S) 49 | #define M_STR_(S) #S 50 | 51 | #define M_BEFORE_AFTER(BEF, AFT) for(int _b = (BEF, 1); _b; (AFT, _b = 0)) 52 | 53 | #define M_STATIC_ASSERT(EXP, NAME) {if(sizeof(struct{unsigned int NAME:((EXP)?1:-1);}));} 54 | 55 | #define M_RECUR(ACT, N, ARG) M_CONC(M, _REST M_CONC_(M_DO_, N)(M_CONC_(M_REPT_, N)(ACT)ARG)) 56 | 57 | #define M_FOR_EACH(ACTN, ...) M_CONC(M_FOR_EACH_, M_NARGS(__VA_ARGS__)) (ACTN, __VA_ARGS__) 58 | #define M_FOR_E2(ACTN, ...) M_REST(M_FOR_EACH(ACTN, __VA_ARGS__)) 59 | 60 | #define M_FOR_EACH_0(ACTN, E) E 61 | #define M_FOR_EACH_1(ACTN, E) ACTN(E) 62 | #define M_FOR_EACH_2(ACTN, E, ...) ACTN(E) M_FOR_EACH_1(ACTN, __VA_ARGS__) 63 | #define M_FOR_EACH_3(ACTN, E, ...) ACTN(E) M_FOR_EACH_2(ACTN, __VA_ARGS__) 64 | #define M_FOR_EACH_4(ACTN, E, ...) ACTN(E) M_FOR_EACH_3(ACTN, __VA_ARGS__) 65 | #define M_FOR_EACH_5(ACTN, E, ...) ACTN(E) M_FOR_EACH_4(ACTN, __VA_ARGS__) 66 | #define M_FOR_EACH_6(ACTN, E, ...) ACTN(E) M_FOR_EACH_5(ACTN, __VA_ARGS__) 67 | #define M_FOR_EACH_7(ACTN, E, ...) ACTN(E) M_FOR_EACH_6(ACTN, __VA_ARGS__) 68 | #define M_FOR_EACH_8(ACTN, E, ...) ACTN(E) M_FOR_EACH_7(ACTN, __VA_ARGS__) 69 | #define M_FOR_EACH_9(ACTN, E, ...) ACTN(E) M_FOR_EACH_8(ACTN, __VA_ARGS__) 70 | #define M_FOR_EACH_10(ACTN, E, ...) ACTN(E) M_FOR_EACH_9(ACTN, __VA_ARGS__) 71 | #define M_FOR_EACH_11(ACTN, E, ...) ACTN(E) M_FOR_EACH_10(ACTN, __VA_ARGS__) 72 | #define M_FOR_EACH_12(ACTN, E, ...) ACTN(E) M_FOR_EACH_11(ACTN, __VA_ARGS__) 73 | #define M_FOR_EACH_13(ACTN, E, ...) ACTN(E) M_FOR_EACH_12(ACTN, __VA_ARGS__) 74 | #define M_FOR_EACH_14(ACTN, E, ...) ACTN(E) M_FOR_EACH_13(ACTN, __VA_ARGS__) 75 | #define M_FOR_EACH_15(ACTN, E, ...) ACTN(E) M_FOR_EACH_14(ACTN, __VA_ARGS__) 76 | #define M_FOR_EACH_16(ACTN, E, ...) ACTN(E) M_FOR_EACH_15(ACTN, __VA_ARGS__) 77 | #define M_FOR_EACH_17(ACTN, E, ...) ACTN(E) M_FOR_EACH_16(ACTN, __VA_ARGS__) 78 | #define M_FOR_EACH_18(ACTN, E, ...) ACTN(E) M_FOR_EACH_17(ACTN, __VA_ARGS__) 79 | #define M_FOR_EACH_19(ACTN, E, ...) ACTN(E) M_FOR_EACH_18(ACTN, __VA_ARGS__) 80 | #define M_FOR_EACH_20(ACTN, E, ...) ACTN(E) M_FOR_EACH_19(ACTN, __VA_ARGS__) 81 | #define M_FOR_EACH_21(ACTN, E, ...) ACTN(E) M_FOR_EACH_20(ACTN, __VA_ARGS__) 82 | #define M_FOR_EACH_22(ACTN, E, ...) ACTN(E) M_FOR_EACH_21(ACTN, __VA_ARGS__) 83 | #define M_FOR_EACH_23(ACTN, E, ...) ACTN(E) M_FOR_EACH_22(ACTN, __VA_ARGS__) 84 | #define M_FOR_EACH_24(ACTN, E, ...) ACTN(E) M_FOR_EACH_23(ACTN, __VA_ARGS__) 85 | #define M_FOR_EACH_25(ACTN, E, ...) ACTN(E) M_FOR_EACH_24(ACTN, __VA_ARGS__) 86 | #define M_FOR_EACH_26(ACTN, E, ...) ACTN(E) M_FOR_EACH_25(ACTN, __VA_ARGS__) 87 | #define M_FOR_EACH_27(ACTN, E, ...) ACTN(E) M_FOR_EACH_26(ACTN, __VA_ARGS__) 88 | #define M_FOR_EACH_28(ACTN, E, ...) ACTN(E) M_FOR_EACH_27(ACTN, __VA_ARGS__) 89 | #define M_FOR_EACH_29(ACTN, E, ...) ACTN(E) M_FOR_EACH_28(ACTN, __VA_ARGS__) 90 | #define M_FOR_EACH_30(ACTN, E, ...) ACTN(E) M_FOR_EACH_29(ACTN, __VA_ARGS__) 91 | #define M_FOR_EACH_31(ACTN, E, ...) ACTN(E) M_FOR_EACH_30(ACTN, __VA_ARGS__) 92 | #define M_FOR_EACH_32(ACTN, E, ...) ACTN(E) M_FOR_EACH_31(ACTN, __VA_ARGS__) 93 | 94 | #define M_ZIP_WITH(ACTN, L1, L2) M_CONC(M_ZIP_WITH_, M_NARGS L1)(ACTN, L1, L2) 95 | #define M_ZIP_W2(ACTN, L1, L2) M_REST(M_ZIP_WITH(ACTN, L1, L2)) 96 | 97 | #define M_ZIP_WITH_0(ACTN, L1, L2) 98 | #define M_ZIP_WITH_1(ACTN, L1, L2) ACTN(M_FIRST L1, M_FIRST L2) 99 | #define M_ZIP_WITH_2(ACTN, L1, L2) ACTN(M_FIRST L1, M_FIRST L2) M_ZIP_WITH_1(ACTN, (M_REST L1), (M_REST L2)) 100 | #define M_ZIP_WITH_3(ACTN, L1, L2) ACTN(M_FIRST L1, M_FIRST L2) M_ZIP_WITH_2(ACTN, (M_REST L1), (M_REST L2)) 101 | #define M_ZIP_WITH_4(ACTN, L1, L2) ACTN(M_FIRST L1, M_FIRST L2) M_ZIP_WITH_3(ACTN, (M_REST L1), (M_REST L2)) 102 | #define M_ZIP_WITH_5(ACTN, L1, L2) ACTN(M_FIRST L1, M_FIRST L2) M_ZIP_WITH_4(ACTN, (M_REST L1), (M_REST L2)) 103 | #define M_ZIP_WITH_6(ACTN, L1, L2) ACTN(M_FIRST L1, M_FIRST L2) M_ZIP_WITH_5(ACTN, (M_REST L1), (M_REST L2)) 104 | #define M_ZIP_WITH_7(ACTN, L1, L2) ACTN(M_FIRST L1, M_FIRST L2) M_ZIP_WITH_6(ACTN, (M_REST L1), (M_REST L2)) 105 | #define M_ZIP_WITH_8(ACTN, L1, L2) ACTN(M_FIRST L1, M_FIRST L2) M_ZIP_WITH_7(ACTN, (M_REST L1), (M_REST L2)) 106 | #define M_ZIP_WITH_9(ACTN, L1, L2) ACTN(M_FIRST L1, M_FIRST L2) M_ZIP_WITH_8(ACTN, (M_REST L1), (M_REST L2)) 107 | #define M_ZIP_WITH_10(ACTN, L1, L2) ACTN(M_FIRST L1, M_FIRST L2) M_ZIP_WITH_9(ACTN, (M_REST L1), (M_REST L2)) 108 | #define M_ZIP_WITH_11(ACTN, L1, L2) ACTN(M_FIRST L1, M_FIRST L2) M_ZIP_WITH_10(ACTN, (M_REST L1), (M_REST L2)) 109 | #define M_ZIP_WITH_12(ACTN, L1, L2) ACTN(M_FIRST L1, M_FIRST L2) M_ZIP_WITH_11(ACTN, (M_REST L1), (M_REST L2)) 110 | #define M_ZIP_WITH_13(ACTN, L1, L2) ACTN(M_FIRST L1, M_FIRST L2) M_ZIP_WITH_12(ACTN, (M_REST L1), (M_REST L2)) 111 | #define M_ZIP_WITH_14(ACTN, L1, L2) ACTN(M_FIRST L1, M_FIRST L2) M_ZIP_WITH_13(ACTN, (M_REST L1), (M_REST L2)) 112 | #define M_ZIP_WITH_15(ACTN, L1, L2) ACTN(M_FIRST L1, M_FIRST L2) M_ZIP_WITH_14(ACTN, (M_REST L1), (M_REST L2)) 113 | #define M_ZIP_WITH_16(ACTN, L1, L2) ACTN(M_FIRST L1, M_FIRST L2) M_ZIP_WITH_15(ACTN, (M_REST L1), (M_REST L2)) 114 | #define M_ZIP_WITH_17(ACTN, L1, L2) ACTN(M_FIRST L1, M_FIRST L2) M_ZIP_WITH_16(ACTN, (M_REST L1), (M_REST L2)) 115 | #define M_ZIP_WITH_18(ACTN, L1, L2) ACTN(M_FIRST L1, M_FIRST L2) M_ZIP_WITH_17(ACTN, (M_REST L1), (M_REST L2)) 116 | #define M_ZIP_WITH_19(ACTN, L1, L2) ACTN(M_FIRST L1, M_FIRST L2) M_ZIP_WITH_18(ACTN, (M_REST L1), (M_REST L2)) 117 | #define M_ZIP_WITH_20(ACTN, L1, L2) ACTN(M_FIRST L1, M_FIRST L2) M_ZIP_WITH_19(ACTN, (M_REST L1), (M_REST L2)) 118 | #define M_ZIP_WITH_21(ACTN, L1, L2) ACTN(M_FIRST L1, M_FIRST L2) M_ZIP_WITH_20(ACTN, (M_REST L1), (M_REST L2)) 119 | #define M_ZIP_WITH_22(ACTN, L1, L2) ACTN(M_FIRST L1, M_FIRST L2) M_ZIP_WITH_21(ACTN, (M_REST L1), (M_REST L2)) 120 | #define M_ZIP_WITH_23(ACTN, L1, L2) ACTN(M_FIRST L1, M_FIRST L2) M_ZIP_WITH_22(ACTN, (M_REST L1), (M_REST L2)) 121 | #define M_ZIP_WITH_24(ACTN, L1, L2) ACTN(M_FIRST L1, M_FIRST L2) M_ZIP_WITH_23(ACTN, (M_REST L1), (M_REST L2)) 122 | #define M_ZIP_WITH_25(ACTN, L1, L2) ACTN(M_FIRST L1, M_FIRST L2) M_ZIP_WITH_24(ACTN, (M_REST L1), (M_REST L2)) 123 | #define M_ZIP_WITH_26(ACTN, L1, L2) ACTN(M_FIRST L1, M_FIRST L2) M_ZIP_WITH_25(ACTN, (M_REST L1), (M_REST L2)) 124 | #define M_ZIP_WITH_27(ACTN, L1, L2) ACTN(M_FIRST L1, M_FIRST L2) M_ZIP_WITH_26(ACTN, (M_REST L1), (M_REST L2)) 125 | #define M_ZIP_WITH_28(ACTN, L1, L2) ACTN(M_FIRST L1, M_FIRST L2) M_ZIP_WITH_27(ACTN, (M_REST L1), (M_REST L2)) 126 | #define M_ZIP_WITH_29(ACTN, L1, L2) ACTN(M_FIRST L1, M_FIRST L2) M_ZIP_WITH_28(ACTN, (M_REST L1), (M_REST L2)) 127 | #define M_ZIP_WITH_30(ACTN, L1, L2) ACTN(M_FIRST L1, M_FIRST L2) M_ZIP_WITH_29(ACTN, (M_REST L1), (M_REST L2)) 128 | #define M_ZIP_WITH_31(ACTN, L1, L2) ACTN(M_FIRST L1, M_FIRST L2) M_ZIP_WITH_30(ACTN, (M_REST L1), (M_REST L2)) 129 | #define M_ZIP_WITH_32(ACTN, L1, L2) ACTN(M_FIRST L1, M_FIRST L2) M_ZIP_WITH_31(ACTN, (M_REST L1), (M_REST L2)) 130 | 131 | #define M_REPT_0(A, ...) 132 | #define M_REPT_1(A, ...) A __VA_ARGS__ 133 | #define M_REPT_2(A, ...) M_REPT_1(A, A __VA_ARGS__) 134 | #define M_REPT_3(A, ...) M_REPT_2(A, A __VA_ARGS__) 135 | #define M_REPT_4(A, ...) M_REPT_3(A, A __VA_ARGS__) 136 | #define M_REPT_5(A, ...) M_REPT_4(A, A __VA_ARGS__) 137 | #define M_REPT_6(A, ...) M_REPT_5(A, A __VA_ARGS__) 138 | #define M_REPT_7(A, ...) M_REPT_6(A, A __VA_ARGS__) 139 | #define M_REPT_8(A, ...) M_REPT_7(A, A __VA_ARGS__) 140 | #define M_REPT_9(A, ...) M_REPT_8(A, A __VA_ARGS__) 141 | #define M_REPT_10(A, ...) M_REPT_9(A, A __VA_ARGS__) 142 | #define M_REPT_11(A, ...) M_REPT_10(A, A __VA_ARGS__) 143 | #define M_REPT_12(A, ...) M_REPT_11(A, A __VA_ARGS__) 144 | #define M_REPT_13(A, ...) M_REPT_12(A, A __VA_ARGS__) 145 | #define M_REPT_14(A, ...) M_REPT_13(A, A __VA_ARGS__) 146 | #define M_REPT_15(A, ...) M_REPT_14(A, A __VA_ARGS__) 147 | #define M_REPT_16(A, ...) M_REPT_15(A, A __VA_ARGS__) 148 | #define M_REPT_17(A, ...) M_REPT_16(A, A __VA_ARGS__) 149 | #define M_REPT_18(A, ...) M_REPT_17(A, A __VA_ARGS__) 150 | #define M_REPT_19(A, ...) M_REPT_18(A, A __VA_ARGS__) 151 | #define M_REPT_20(A, ...) M_REPT_19(A, A __VA_ARGS__) 152 | #define M_REPT_21(A, ...) M_REPT_20(A, A __VA_ARGS__) 153 | #define M_REPT_22(A, ...) M_REPT_21(A, A __VA_ARGS__) 154 | #define M_REPT_23(A, ...) M_REPT_22(A, A __VA_ARGS__) 155 | #define M_REPT_24(A, ...) M_REPT_23(A, A __VA_ARGS__) 156 | #define M_REPT_25(A, ...) M_REPT_24(A, A __VA_ARGS__) 157 | #define M_REPT_26(A, ...) M_REPT_25(A, A __VA_ARGS__) 158 | #define M_REPT_27(A, ...) M_REPT_26(A, A __VA_ARGS__) 159 | #define M_REPT_28(A, ...) M_REPT_27(A, A __VA_ARGS__) 160 | #define M_REPT_29(A, ...) M_REPT_28(A, A __VA_ARGS__) 161 | #define M_REPT_30(A, ...) M_REPT_29(A, A __VA_ARGS__) 162 | #define M_REPT_31(A, ...) M_REPT_30(A, A __VA_ARGS__) 163 | #define M_REPT_32(A, ...) M_REPT_31(A, A __VA_ARGS__) 164 | 165 | #define M_NEST_0(A, ...) 166 | #define M_NEST_1(A, ...) A(__VA_ARGS__) 167 | #define M_NEST_2(A, ...) M_NEST_1(A, A(__VA_ARGS__)) 168 | #define M_NEST_3(A, ...) M_NEST_2(A, A(__VA_ARGS__)) 169 | #define M_NEST_4(A, ...) M_NEST_3(A, A(__VA_ARGS__)) 170 | #define M_NEST_5(A, ...) M_NEST_4(A, A(__VA_ARGS__)) 171 | #define M_NEST_6(A, ...) M_NEST_5(A, A(__VA_ARGS__)) 172 | #define M_NEST_7(A, ...) M_NEST_6(A, A(__VA_ARGS__)) 173 | #define M_NEST_8(A, ...) M_NEST_7(A, A(__VA_ARGS__)) 174 | #define M_NEST_9(A, ...) M_NEST_8(A, A(__VA_ARGS__)) 175 | #define M_NEST_10(A, ...) M_NEST_9(A, A(__VA_ARGS__)) 176 | #define M_NEST_11(A, ...) M_NEST_10(A, A(__VA_ARGS__)) 177 | #define M_NEST_12(A, ...) M_NEST_11(A, A(__VA_ARGS__)) 178 | #define M_NEST_13(A, ...) M_NEST_12(A, A(__VA_ARGS__)) 179 | #define M_NEST_14(A, ...) M_NEST_13(A, A(__VA_ARGS__)) 180 | #define M_NEST_15(A, ...) M_NEST_14(A, A(__VA_ARGS__)) 181 | #define M_NEST_16(A, ...) M_NEST_15(A, A(__VA_ARGS__)) 182 | #define M_NEST_17(A, ...) M_NEST_16(A, A(__VA_ARGS__)) 183 | #define M_NEST_18(A, ...) M_NEST_17(A, A(__VA_ARGS__)) 184 | #define M_NEST_19(A, ...) M_NEST_18(A, A(__VA_ARGS__)) 185 | #define M_NEST_20(A, ...) M_NEST_19(A, A(__VA_ARGS__)) 186 | #define M_NEST_21(A, ...) M_NEST_20(A, A(__VA_ARGS__)) 187 | #define M_NEST_22(A, ...) M_NEST_21(A, A(__VA_ARGS__)) 188 | #define M_NEST_23(A, ...) M_NEST_22(A, A(__VA_ARGS__)) 189 | #define M_NEST_24(A, ...) M_NEST_23(A, A(__VA_ARGS__)) 190 | #define M_NEST_25(A, ...) M_NEST_24(A, A(__VA_ARGS__)) 191 | #define M_NEST_26(A, ...) M_NEST_25(A, A(__VA_ARGS__)) 192 | #define M_NEST_27(A, ...) M_NEST_26(A, A(__VA_ARGS__)) 193 | #define M_NEST_28(A, ...) M_NEST_27(A, A(__VA_ARGS__)) 194 | #define M_NEST_29(A, ...) M_NEST_28(A, A(__VA_ARGS__)) 195 | #define M_NEST_30(A, ...) M_NEST_29(A, A(__VA_ARGS__)) 196 | #define M_NEST_31(A, ...) M_NEST_30(A, A(__VA_ARGS__)) 197 | #define M_NEST_32(A, ...) M_NEST_31(A, A(__VA_ARGS__)) 198 | 199 | #define M_DO_0(...) 200 | #define M_DO_1(...) (__VA_ARGS__) 201 | #define M_DO_2(...) M_DO_1(__VA_ARGS__) 202 | #define M_DO_3(...) M_DO_2(__VA_ARGS__) 203 | #define M_DO_4(...) M_DO_3(__VA_ARGS__) 204 | #define M_DO_5(...) M_DO_4(__VA_ARGS__) 205 | #define M_DO_6(...) M_DO_5(__VA_ARGS__) 206 | #define M_DO_7(...) M_DO_6(__VA_ARGS__) 207 | #define M_DO_8(...) M_DO_7(__VA_ARGS__) 208 | #define M_DO_9(...) M_DO_8(__VA_ARGS__) 209 | #define M_DO_10(...) M_DO_9(__VA_ARGS__) 210 | #define M_DO_11(...) M_DO_10(__VA_ARGS__) 211 | #define M_DO_12(...) M_DO_11(__VA_ARGS__) 212 | #define M_DO_13(...) M_DO_12(__VA_ARGS__) 213 | #define M_DO_14(...) M_DO_13(__VA_ARGS__) 214 | #define M_DO_15(...) M_DO_14(__VA_ARGS__) 215 | #define M_DO_16(...) M_DO_15(__VA_ARGS__) 216 | #define M_DO_17(...) M_DO_16(__VA_ARGS__) 217 | #define M_DO_18(...) M_DO_17(__VA_ARGS__) 218 | #define M_DO_19(...) M_DO_18(__VA_ARGS__) 219 | #define M_DO_20(...) M_DO_19(__VA_ARGS__) 220 | #define M_DO_21(...) M_DO_20(__VA_ARGS__) 221 | #define M_DO_22(...) M_DO_21(__VA_ARGS__) 222 | #define M_DO_23(...) M_DO_22(__VA_ARGS__) 223 | #define M_DO_24(...) M_DO_23(__VA_ARGS__) 224 | #define M_DO_25(...) M_DO_24(__VA_ARGS__) 225 | #define M_DO_26(...) M_DO_25(__VA_ARGS__) 226 | #define M_DO_27(...) M_DO_26(__VA_ARGS__) 227 | #define M_DO_28(...) M_DO_27(__VA_ARGS__) 228 | #define M_DO_29(...) M_DO_28(__VA_ARGS__) 229 | #define M_DO_30(...) M_DO_29(__VA_ARGS__) 230 | #define M_DO_31(...) M_DO_30(__VA_ARGS__) 231 | #define M_DO_32(...) M_DO_31(__VA_ARGS__) 232 | 233 | #define M_DIV2_1 0 234 | #define M_DIV2_2 1 235 | #define M_DIV2_4 2 236 | #define M_DIV2_6 3 237 | #define M_DIV2_8 4 238 | #define M_DIV2_10 5 239 | #define M_DIV2_12 6 240 | #define M_DIV2_14 7 241 | #define M_DIV2_16 8 242 | #define M_DIV2_18 9 243 | #define M_DIV2_20 10 244 | #define M_DIV2_22 11 245 | #define M_DIV2_24 12 246 | #define M_DIV2_26 13 247 | #define M_DIV2_28 14 248 | #define M_DIV2_30 15 249 | #define M_DIV2_32 16 250 | 251 | #define M_DIV3_3 1 252 | #define M_DIV3_6 2 253 | #define M_DIV3_9 3 254 | #define M_DIV3_12 4 255 | #define M_DIV3_15 5 256 | #define M_DIV3_18 6 257 | #define M_DIV3_21 7 258 | #define M_DIV3_24 8 259 | #define M_DIV3_27 9 260 | #define M_DIV3_30 10 261 | #define M_DIV3_33 11 262 | 263 | #define M_DIV4_4 1 264 | #define M_DIV4_8 2 265 | #define M_DIV4_12 3 266 | #define M_DIV4_16 4 267 | #define M_DIV4_20 5 268 | #define M_DIV4_24 6 269 | #define M_DIV4_28 7 270 | #define M_DIV4_32 8 271 | #define M_DIV4_36 9 272 | 273 | 274 | #endif 275 | 276 | -------------------------------------------------------------------------------- /pyrite-lib.c: -------------------------------------------------------------------------------- 1 | 2 | /* Pyrite: a BASIC "compiler" implemented entirely using the C 3 | * macro system 4 | * 5 | * All components are in the public domain 6 | * 7 | * This file defines Pyrite's runtime library: GC, exceptions, and a 8 | * few collection and string functions 9 | */ 10 | 11 | #include 12 | #include 13 | #include "pyrite-lib.h" 14 | 15 | struct _Frame _rootFrame; 16 | struct _Frame * _topFrame; 17 | _Object _currentException; 18 | 19 | // Maximum number of variable slots on the stack; 20 | // probably should be dynamic anyway... 21 | #define MAX_SLOTS 200000 22 | static struct _VarSlot slot[MAX_SLOTS]; // Shadow stack for tracing 23 | static struct _VarSlot * topslot; 24 | static _Object last; // List of allocated objects 25 | static enum GC_Mode gc_mode; 26 | static size_t bytes_allocated; 27 | static size_t threshold; 28 | #define DEFAULT_THRESHOLD 1000000 29 | 30 | // Preallocated exception message for out-of-memory 31 | static struct panic_ex { 32 | objdataP __meta; 33 | char msg[1024]; 34 | struct objdata data; 35 | } panic = { .msg = "GC ran out of memory trying to allocate object", 36 | .data = { .l = 1, .ttag = "GCException" } 37 | }; 38 | 39 | 40 | // GC system functions 41 | 42 | void GC_Init(void) { 43 | bytes_allocated = 0; 44 | for (int i = 0; i < MAX_SLOTS; i++) { // Set up the varslots array 45 | slot[i].nx = &slot[i] + 1; 46 | } 47 | slot[MAX_SLOTS - 1].nx = NULL; 48 | topslot = &slot[0]; 49 | _topFrame = NULL; 50 | threshold = DEFAULT_THRESHOLD; // Apply default GC settings (aggressiveness?) 51 | gc_mode = GC_ON; 52 | } 53 | 54 | void * GC_Allocate(size_t s, void(*mark)(_Object), char * ttag) { 55 | if (bytes_allocated >= threshold) { // Do a collection if necessary 56 | GC_Collect(); 57 | } 58 | size_t sz = s + sizeof(struct objdata); 59 | _Object o = calloc(1, sz); 60 | if (o == NULL) { 61 | ThrowException(&panic.__meta); // Panic, out of memory 62 | } 63 | *o = (objdataP)(((char *)o) + s); 64 | **o = (struct objdata){ 65 | .ttag = ttag, .mark = mark, .del = _gc_FreeDefault, .sz = sz 66 | }; 67 | (*o)->nx = last; // Add object to all-objects list 68 | last = o; 69 | bytes_allocated += sz; // Update GC status 70 | // printf("Allocated %d bytes as %s at %p\n", sz, ttag, o); 71 | return o; 72 | } 73 | 74 | size_t GC_Collect(void) { 75 | size_t bytes = 0; 76 | if (gc_mode == GC_ON) { 77 | for (_Object o = last; o != NULL; o = (*o)->nx) { 78 | (*o)->d = (*o)->l; 79 | } 80 | for (struct _Frame * f = _topFrame; f != NULL; f = f->prev) { 81 | for (struct _VarSlot * v = _topFrame->vars; v != NULL; v = v->nx) { 82 | _Object o = *v->var; 83 | (*o)->mark(o); 84 | } 85 | } 86 | for (_Object o = last, n, *dest = &last; o != NULL; o = n) { 87 | n = (*o)->nx; 88 | if (!(*o)->d) { 89 | *dest = n; 90 | bytes += (*o)->sz; (*o)->del(o); 91 | } else { 92 | dest = &((*o)->nx); 93 | } 94 | } 95 | bytes_allocated -= bytes; 96 | } 97 | return bytes; 98 | } 99 | 100 | void GC_SetMode(enum GC_Mode mode) { 101 | gc_mode = mode; 102 | } 103 | 104 | void GC_SetThreshold(size_t bytes) { 105 | threshold = bytes; 106 | } 107 | 108 | 109 | // Per-object GC methods 110 | 111 | void _gc_FreeDefault(_Object o) { 112 | free(o); 113 | } 114 | 115 | void _gc_Mark_primitiveArray(_Object o) { 116 | MetaArray a = (MetaArray)o; 117 | a->__meta->d = 1; 118 | } 119 | 120 | void _gc_Mark_ObjectArray(_Object o) { 121 | MetaArray a = (MetaArray)o; 122 | if (!a->__meta->d) { 123 | a->__meta->d = 1; 124 | for (int i = 0; i < a->len; i++) { 125 | o = ((_Object *)(a->data))[i]; 126 | (*o)->mark(o); 127 | } 128 | } 129 | } 130 | 131 | void _gc_SetFinaliser(_Object o, void(*del)(_Object)) { 132 | (*o)->del = del; 133 | } 134 | 135 | void _gc_LockObject(_Object o, unsigned int l) { 136 | (*o)->l = !!l; 137 | } 138 | int printf(const char *, ...); 139 | 140 | // Shadow stack management 141 | 142 | void _gc_Push_Variable(_Object * v) { 143 | struct _VarSlot * s = topslot; 144 | if (s == NULL) { 145 | fprintf(stderr, "Shadow stack overflow\n"); 146 | raise(SIGSEGV); // Panic! Shadow stack overflow 147 | } 148 | topslot = topslot->nx; 149 | s->var = v; 150 | s->nx = _topFrame->vars; 151 | _topFrame->vars = s; 152 | } 153 | 154 | static void _gc_Pop_Variable(void) { 155 | struct _VarSlot * s = _topFrame->vars; 156 | _topFrame->vars = s->nx; 157 | s->nx = topslot; 158 | topslot = s; 159 | } 160 | 161 | void _gc_Push_Frame(struct _Frame * f, enum _Frame_Type t) { 162 | f->prev = _topFrame; 163 | f->tag = t; 164 | f->vars = NULL; 165 | _topFrame = f; 166 | } 167 | 168 | void _gc_Pop_Frame(enum _Frame_Type t) { 169 | struct _Frame * f; 170 | do { 171 | while (_topFrame->vars != NULL) { 172 | _gc_Pop_Variable(); 173 | } 174 | f = _topFrame; 175 | _topFrame = _topFrame->prev; 176 | } while (t == FT_CALL_FRAME && f->tag != FT_CALL_FRAME); 177 | } 178 | 179 | 180 | // Exceptions 181 | 182 | void ThrowException(_Object ex) { 183 | while (_topFrame->tag != FT_TRY_FRAME) { 184 | _gc_Pop_Frame(FT_TRY_FRAME); 185 | } 186 | if (_topFrame == NULL) { 187 | fprintf(stderr, "Unhandled exception %p\n", ex); 188 | raise(SIGSEGV); // Seems a bit redundant really... 189 | } 190 | _currentException = ex; 191 | longjmp(*(_topFrame->bufp), 1); 192 | } 193 | 194 | _Object GetException(void) { 195 | return _currentException; 196 | } 197 | 198 | 199 | // Array access 200 | 201 | MetaArray _gc_MakeArray(_Object o, size_t len, size_t es) { 202 | MetaArray a = (MetaArray)o; 203 | a->len = len; a->es = es; a->data = ((char *)o) + sizeof(struct MetaArray); 204 | return a; 205 | } 206 | 207 | /*int _arrayBoundsCheck(int) { 208 | 209 | }*/ 210 | 211 | 212 | // Strings 213 | 214 | string str(const char * c) { 215 | string s = array(char, (strlen(c) + 1)); 216 | strcpy(&(s->data[0]), c); 217 | return s; 218 | } 219 | 220 | int len(string s) { 221 | return s->arr.len - 1; 222 | } 223 | 224 | string substr(string s, unsigned int from, unsigned int len) { 225 | if (from >= s->arr.len) { return array(char, 1); } 226 | if (from + len > s->arr.len) { len -= (from + len) - s->arr.len; } 227 | string r = array(char, (len + 1)); 228 | memcpy(r->data, &s->data[from], len); 229 | return r; 230 | } 231 | 232 | string join(string a, string b) { 233 | string r = array(char, (len(a) + len(b))); 234 | memcpy(r->data, a->data, len(a)); 235 | memcpy(&r->data[len(a)], b->data, len(b)); 236 | return r; 237 | } 238 | 239 | string upper(string a) { 240 | string r = array(char, len(a)); 241 | memcpy(r->data, a->data, len(a)); 242 | for (int i = 0; i < len(r); i++) { 243 | if (r->data[i] >= 97 && r->data[i] <= 122 ) { r->data[i] -= 32; } 244 | } 245 | return r; 246 | } 247 | 248 | string lower(string a) { 249 | string r = array(char, len(a)); 250 | memcpy(r->data, a->data, len(a)); 251 | for (int i = 0; i < len(r); i++) { 252 | if (r->data[i] >= 65 && r->data[i] <= 90 ) { r->data[i] += 32; } 253 | } 254 | return r; 255 | } 256 | 257 | void print(string s) { 258 | printf("%s\n", s->data); 259 | } 260 | 261 | void write(string s) { 262 | printf("%s", s->data); 263 | } 264 | 265 | -------------------------------------------------------------------------------- /pyrite-lib.h: -------------------------------------------------------------------------------- 1 | 2 | /* Pyrite: a BASIC "compiler" implemented entirely using the C 3 | * macro system 4 | * 5 | * All components are in the public domain 6 | * 7 | * This file declares the few functions needed by the Pyrite runtime 8 | * (mainly GC and exceptions) 9 | */ 10 | 11 | #ifndef PYRITE_LIB_H 12 | #define PYRITE_LIB_H 13 | 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | /* */ 20 | #include "cmacros.h" 21 | 22 | // TODO 23 | #define THREADLOCAL 24 | 25 | // Dummy GC markers for primitive types 26 | #define _gc_Markint(x) 27 | #define _gc_Markshort(x) 28 | #define _gc_Markchar(x) 29 | #define _gc_Markint64(x) 30 | #define _gc_Markint32(x) 31 | #define _gc_Markfloat(x) 32 | #define _gc_Markdouble(x) 33 | #define _gc_Markpointer(x) 34 | 35 | #define _gc_MarkintArray _gc_Mark_primitiveArray 36 | #define _gc_MarkshortArray _gc_Mark_primitiveArray 37 | #define _gc_MarkcharArray _gc_Mark_primitiveArray 38 | #define _gc_Markint64Array _gc_Mark_primitiveArray 39 | #define _gc_Markint32Array _gc_Mark_primitiveArray 40 | #define _gc_MarkfloatArray _gc_Mark_primitiveArray 41 | #define _gc_MarkdoubleArray _gc_Mark_primitiveArray 42 | #define _gc_MarkpointerArray _gc_Mark_primitiveArray 43 | 44 | #define _gc_Markstring _gc_Mark_primitiveArray 45 | #define _gc_MarkstringArray _gc_Mark_ObjectArray 46 | 47 | // Dummy GC pushes 48 | #define _gc_Pushint(x) 49 | #define _gc_Pushshort(x) 50 | #define _gc_Pushchar(x) 51 | #define _gc_Pushint64(x) 52 | #define _gc_Pushint32(x) 53 | #define _gc_Pushfloat(x) 54 | #define _gc_Pushdouble(x) 55 | #define _gc_Pushpointer(x) 56 | 57 | #define _gc_Pushvoid(x) 58 | 59 | #define _gc_PushintArray(A) _gc_Push_Variable((_Object *)A) 60 | #define _gc_PushshortArray(A) _gc_Push_Variable((_Object *)A) 61 | #define _gc_PushcharArray(A) _gc_Push_Variable((_Object *)A) 62 | #define _gc_Pushint64Array(A) _gc_Push_Variable((_Object *)A) 63 | #define _gc_Pushint32Array(A) _gc_Push_Variable((_Object *)A) 64 | #define _gc_PushfloatArray(A) _gc_Push_Variable((_Object *)A) 65 | #define _gc_PushdoubleArray(A) _gc_Push_Variable((_Object *)A) 66 | #define _gc_PushpointerArray(A) _gc_Push_Variable((_Object *)A) 67 | 68 | #define _gc_Pushstring(A) _gc_Push_Variable((_Object *)A) 69 | #define _gc_PushstringArray(A) _gc_Push_Variable((_Object *)A) 70 | 71 | typedef void * pointer; 72 | typedef int32_t int32; 73 | typedef int64_t int64; 74 | 75 | typedef struct objdata * objdataP; 76 | typedef objdataP * _Object; 77 | 78 | struct objdata { 79 | unsigned int d:1, l:1; 80 | char * ttag; 81 | void(*mark)(_Object); 82 | void(*del)(_Object); 83 | size_t sz; 84 | _Object nx; 85 | }; 86 | 87 | typedef struct MetaArray { objdataP __meta; size_t len, es; void * data; } * MetaArray; 88 | #define arraytype(T) typedef struct T##Array { struct MetaArray arr; T data[]; } * T##Array; 89 | #define functype(N, T) typedef M_REST T(*N)M_FIRST T; arraytype(N) \ 90 | static inline void _gc_Push##N(void * _) {} \ 91 | static void(* const _gc_Mark##N##Array)(_Object) = _gc_Mark_primitiveArray; \ 92 | static inline void _gc_Push##N##Array(N##Array * a) {_gc_Push_Variable((_Object *)a);} 93 | 94 | #define new(T) (T)GC_Allocate(sizeof(struct T), _gc_Mark##T, #T) 95 | #define array(T, S) (T##Array)_gc_MakeArray(GC_Allocate(sizeof(struct T##Array) + (S) * sizeof(T), \ 96 | _gc_Mark##T##Array, M_STR_(T##Array)), S, sizeof(T)) 97 | #define setfinaliser(OBJ) _gc_SetFinaliser(OBJ) 98 | #define arraylen(A) ((A)->arr.len) 99 | 100 | THREADLOCAL static MetaArray _ArrayTmp; 101 | //#define elem(A, E) ((_ArrayTmp = (MetaArray)(A))[_arrayBoundsCheck(E)]) 102 | #define elem(A, E) (A)->data[E] 103 | 104 | arraytype(int) 105 | arraytype(short) 106 | arraytype(char) 107 | arraytype(int64) 108 | arraytype(int32) 109 | arraytype(float) 110 | arraytype(double) 111 | arraytype(pointer) 112 | 113 | typedef charArray string; 114 | arraytype(string) 115 | 116 | struct _VarSlot { 117 | _Object * var; 118 | struct _VarSlot * nx; 119 | }; 120 | 121 | enum _Frame_Type { FT_CALL_FRAME, FT_BLK_FRAME, FT_TRY_FRAME }; 122 | struct _Frame { 123 | enum _Frame_Type tag; 124 | struct _Frame * prev; 125 | struct _VarSlot * vars; 126 | jmp_buf * bufp; 127 | }; 128 | extern struct _Frame _rootFrame; 129 | extern struct _Frame * _topFrame; 130 | extern _Object _currentException; 131 | 132 | enum GC_Mode { GC_ON, GC_OFF }; 133 | 134 | void GC_Init(void); 135 | void * GC_Allocate(size_t, void(*)(_Object), char *); 136 | size_t GC_Collect(void); 137 | void GC_SetMode(enum GC_Mode); 138 | void GC_SetThreshold(size_t); 139 | 140 | void _gc_FreeDefault(_Object); 141 | void _gc_Mark_primitiveArray(_Object); 142 | void _gc_Mark_ObjectArray(_Object); 143 | void _gc_SetFinaliser(_Object, void(*)(_Object)); 144 | void _gc_LockObject(_Object, unsigned int); 145 | 146 | void _gc_Push_Variable(_Object *); 147 | void _gc_Push_Frame(struct _Frame *, enum _Frame_Type); 148 | void _gc_Pop_Frame(enum _Frame_Type); 149 | 150 | void ThrowException(_Object); 151 | _Object GetException(void); 152 | 153 | MetaArray _gc_MakeArray(_Object, size_t, size_t); 154 | //int _arrayBoundsCheck(int); 155 | 156 | string str(const char *); 157 | int len(string); 158 | string substr(string, unsigned int, unsigned int); 159 | string join(string, string); 160 | string upper(string); 161 | string lower(string); 162 | void print(string); 163 | void write(string); 164 | 165 | 166 | #endif 167 | 168 | -------------------------------------------------------------------------------- /pyrite-project.c: -------------------------------------------------------------------------------- 1 | 2 | /* Pyrite: a BASIC "compiler" implemented entirely using the C 3 | * macro system 4 | * 5 | * All components are in the public domain 6 | * 7 | * This file defines a minimal wrapper file so that programs may appear 8 | * to have been written entirely in Pyrite code. 9 | * 10 | * To compile a Pyrite-project program with GCC, try: 11 | * gcc -std=c99 pyrite-lib.c -DPYRITE_PROJECT=\"myPyriteFile.pyr\" 12 | * pyrite-project.c -o myAppName 13 | */ 14 | 15 | #include 16 | #include "pyrite.h" 17 | 18 | 19 | #include PYRITE_PROJECT 20 | 21 | 22 | // Undefine BASIC keywords so C code will work 23 | #include "pyrite-undefs.h" 24 | 25 | int main(int argc, char ** argv) { 26 | GC_Init(); 27 | GC_SetMode(GC_OFF); 28 | 29 | stringArray avec = array(string, argc); 30 | for (int i = 0; i < argc; i++) { 31 | elem(avec, i) = str(argv[i]); 32 | } 33 | 34 | GC_SetMode(GC_ON); 35 | 36 | int r = MAIN(avec); 37 | return r; 38 | } 39 | 40 | -------------------------------------------------------------------------------- /pyrite-undefs.h: -------------------------------------------------------------------------------- 1 | 2 | /* Pyrite: a BASIC "compiler" implemented entirely using the C 3 | * macro system 4 | * 5 | * All components are in the public domain 6 | * 7 | * This file UNdefines the BASIC syntax structures again so that 8 | * normal C code will work after the point where it is included 9 | */ 10 | 11 | #undef and 12 | #undef as 13 | #undef bitand 14 | #undef bitor 15 | #undef case 16 | #undef catch 17 | #undef declare 18 | #undef default 19 | #undef do 20 | #undef downto 21 | #undef elsedo 22 | #undef elseif 23 | #undef end 24 | #undef endtype 25 | #undef equal 26 | #undef exception 27 | #undef field 28 | #undef for 29 | #undef forever 30 | #undef function 31 | #undef gc_trackvar 32 | #undef if 33 | #undef in 34 | #undef let 35 | #undef mod 36 | #undef none 37 | #undef not 38 | #undef null 39 | #undef of 40 | #undef or 41 | #undef repeat 42 | #undef return 43 | #undef select 44 | #undef shl 45 | #undef shr 46 | #undef step 47 | #undef then 48 | #undef throw 49 | #undef to 50 | #undef try 51 | #undef type 52 | #undef until 53 | #undef upto 54 | #undef var 55 | #undef while 56 | #undef with 57 | #undef xor 58 | 59 | -------------------------------------------------------------------------------- /pyrite.h: -------------------------------------------------------------------------------- 1 | 2 | /* Pyrite: a BASIC "compiler" implemented entirely using the C 3 | * macro system 4 | * 5 | * All components are in the public domain 6 | * 7 | * This file defines the BASIC syntax structures 8 | */ 9 | 10 | /* BASIC features provided: 11 | * 12 | * + Function definition 13 | * + Forward declarations 14 | * + Control blocks 15 | * + While 16 | * + For (upto, downto, to/step, in) 17 | * + Repeat 18 | * + Select 19 | * + If 20 | * + Try 21 | * + Return 22 | * + Inline If 23 | * + Variable declarations 24 | * + Let and var statements 25 | * + With blocks 26 | * + Mark variables 27 | * + Garbage collection 28 | * + observe variables 29 | * + allocate space 30 | * + mark/sweep 31 | * + Infix text operators 32 | * + andalso, orelse, not 33 | * + b_and, b_or, xor 34 | * + shr, shl (, sar), mod 35 | * + User-defined types 36 | * + Array type declaration 37 | * + String operations 38 | * - List operations 39 | */ 40 | 41 | 42 | #include "pyrite-lib.h" 43 | 44 | 45 | // Necessary for paren-less block structures to work at the top level 46 | #define BASIC(...) __VA_ARGS__ 47 | #define BASIC_0(...) __VA_ARGS__ 48 | 49 | 50 | // Function has two forms chosen by number of resolved args 51 | #define function B_FUNCTION B_LEFTP 52 | #define B_FUNCTION(...) M_CONC(B_FUNCTION_, M_NARGS(__VA_ARGS__))(__VA_ARGS__) 53 | 54 | // 2: arguments and return type declared with C-like syntax 55 | #define B_FUNCTION_2(AR, BODY) AR B_FBODY((void), M_ID BODY) 56 | // 3: arguments declared with C-like syntax 57 | #define B_FUNCTION_3(NA, R, BODY) R NA B_FBODY((void), M_ID BODY) 58 | // 4: arguments and return type declared with BASIC syntax 59 | #define B_FUNCTION_4(N, A, R, BODY) R N (B_ALIST A) B_FBODY(A, M_ID BODY) 60 | 61 | #define B_REORD(n, t, ...) (__VA_ARGS__),t n 62 | #define B_ALIST(...) M_REVERSE(M_RECUR(B_REORD, M_DIV2(M_NARGS(__VA_ARGS__)), (__VA_ARGS__))) 63 | #define B_FBODY(A, B) { B_FPUSH A B B_FPOP(CALL) } 64 | 65 | #define B_FPUSH(...) struct _Frame __fr; _gc_Push_Frame(&__fr, FT_CALL_FRAME); B_PUSHARGS(__VA_ARGS__) 66 | #define B_PUSHARGS(...) M_FOR_EACH(B_FLD2, M_RECUR(B_PUSHARG, M_DIV2(M_NARGS(__VA_ARGS__)), (__VA_ARGS__))) 67 | #define B_PUSHARG(n, t, ...) (__VA_ARGS__), B_PUSHVAR(n, t) 68 | #define B_FPOP(T) _gc_Pop_Frame(FT_##T##_FRAME); 69 | 70 | #define declare(...) M_FOR_EACH(M_ID, M_RECUR(B_DECL, M_DIV3(M_NARGS(__VA_ARGS__)), (__VA_ARGS__))) 71 | #define B_DECL(n, a, r, ...) (__VA_ARGS__), r n a; 72 | 73 | 74 | #define type B_TYPE B_LEFTP( 75 | #define B_TYPE(N, ...) typedef struct M_ID N * M_ID N; \ 76 | struct M_ID N { objdataP __meta; M_FOR_EACH(B_FLD2, \ 77 | M_FOR_EACH(B_FIELD, __VA_ARGS__)) }; arraytype N \ 78 | B_GENGCFUNC(M_ID N, __VA_ARGS__) 79 | #define B_FIELD(F) B_ALIST F; 80 | #define B_FLD2(F) F; 81 | 82 | #define field ),( 83 | #define endtype )) 84 | 85 | #define B_GENGCFUNC(N, ...) static void M_CONC(_gc_Mark, N)(_Object o) { \ 86 | N obj = (N)o; BASIC_0(if not (obj->__meta->d) do \ 87 | M_FOR_EACH(M_ID, M_FOR_EACH(B_MARKFLD, __VA_ARGS__)) obj->__meta->d = 1 end) } \ 88 | static inline void M_CONC(_gc_Push, N)(N * v) {_gc_Push_Variable((_Object *)v);} \ 89 | static void(* const M_CONC(_gc_Mark, M_CONC(N, Array)))(_Object) = _gc_Mark_ObjectArray; \ 90 | static inline void M_CONC(_gc_Push, M_CONC(N, Array))(M_CONC(N, Array) * a) \ 91 | {_gc_Push_Variable((_Object *)a);} 92 | #define B_MARKFLD(...) M_RECUR(B_MARKF2, M_DIV2(M_NARGS __VA_ARGS__), __VA_ARGS__) 93 | #define B_MARKF2(n, t, ...) (__VA_ARGS__), M_CONC(_gc_Mark, t)((_Object)obj->n); 94 | 95 | 96 | #define let ;B_LET B_LEFTP( 97 | #define B_LET(V) M_FOR_EACH(B_FLD2, B_LETLIST V) 98 | #define B_LETLIST(...) M_REVERSE(M_RECUR(B_RELET, M_DIV3(M_NARGS(__VA_ARGS__)), (__VA_ARGS__))) 99 | #define B_RELET(n, t, v, ...) (__VA_ARGS__), t n = v; B_PUSHVAR(n, t) 100 | 101 | #define var(...) ;M_FOR_EACH(B_FLD2, B_ALIST(__VA_ARGS__)) 102 | 103 | #define with B_WITH B_LEFTP 104 | #define B_WITH(...) { B_WITHV(M_HEAD(__VA_ARGS__)) M_ID2(M_TAIL(__VA_ARGS__)) B_FPOP(BLK) } 105 | #define B_WITHV(...) struct _Frame __fr; _gc_Push_Frame(&__fr, FT_BLK_FRAME); \ 106 | M_FOR_EACH(B_FLD2, M_REVERSE(M_RECUR(B_REWITH, M_DIV2(M_NARGS(__VA_ARGS__)), (__VA_ARGS__)))) 107 | #define B_REWITH(n, t, ...) (__VA_ARGS__), t n; B_PUSHVAR(n, t) 108 | 109 | // Primitive typed vars will get dummied out 110 | #define B_PUSHVAR(n, t) _gc_Push##t(&n) 111 | // Only track variables of non-primitive type! 112 | #define gc_trackvar(n) _gc_PushVariable((_Object *)n) 113 | 114 | 115 | #define while ;while B_WHILE B_LEFTP 116 | #define B_WHILE(P, CODE) ( P ) { M_ID CODE } 117 | 118 | // For loop has several forms, chosen by number of resolved args 119 | #define for ;for B_FOR B_LEFTP 120 | #define B_FOR(...) M_CONC(B_FOR_, M_NARGS(__VA_ARGS__))(__VA_ARGS__) 121 | 122 | // 6: For x = a To b Step c 123 | #define B_FOR_6(V, F, T, _, S, CODE) (V = F; V <> T; V += S) { M_ID CODE } 124 | // 5: For x = a upto/downto b 125 | #define B_FOR_5(V, S, OP, E, CODE) (V = S; OP##2(V, E); OP##1(V)) { M_ID CODE } 126 | // 4: For x = a To b 127 | #define B_FOR_4(V, F, T, CODE) (V = F; V <= T; V++) { M_ID CODE } 128 | // 3: For elem = EachIn List 129 | #define B_FOR_3(V, LIST, CODE) (B_ITERATOR B_INAME = B_GETITERATOR(LIST); \ 130 | B_HASNEXT(B_INAME);) { V = B_NEXT(B_INAME); { M_ID CODE } } 131 | 132 | #define upto ,B_UPTO_, 133 | #define downto ,B_DOWNTO_, 134 | #define B_UPTO_2(V, T) V < T 135 | #define B_UPTO_1(V) V++ 136 | #define B_DOWNTO_2(V, T) V > T 137 | #define B_DOWNTO_1(V) V-- 138 | 139 | 140 | // If chooses between inline and block by number of resolved args 141 | #define if ;if B_IF B_LEFTP 142 | #define B_IF(...) M_CONC(B_IF_, M_NARGS(__VA_ARGS__))(__VA_ARGS__) 143 | 144 | // 1: If/Then 145 | #define B_IF_1(P) (P) 146 | // 2: If/Do 147 | #define B_IF_2(P, CODE) (P) { M_ID CODE } 148 | // 3: If/Do/ElseDo 149 | #define B_IF_3(P, CODET, CODEE) (P) { M_ID CODET } else { M_ID CODEE } 150 | 151 | #define then ) 152 | #define elseif end else if 153 | #define elsedo ;),( 154 | 155 | 156 | #define select ;B_SELECT B_LEFTP (( 157 | #define B_SELECT(VAL, ...) { B_SELECTOR B_SELNAME = M_ID(M_FIRST M_ID VAL); \ 158 | M_FOR_EACH(B_CASE1, __VA_ARGS__) {} } 159 | #define B_CASE(...) (M_FOR_EACH(B_CASE0, M_HEAD(__VA_ARGS__)) 0, M_TAIL(__VA_ARGS__)) 160 | #define B_CASE0(X) X == B_SELNAME || 161 | #define B_CASE1(C) BASIC_0(if M_FIRST(M_ID C) then {M_ID M_REST(M_ID C);} else) 162 | 163 | #define case )),B_CASE B_LEFTP 164 | #define default )),B_CASE B_LEFTP B_SELNAME,() 165 | 166 | 167 | #define repeat ;{ _B_REPEAT: { 168 | #define until(P) ;} if !(P) then goto _B_REPEAT; } 169 | #define forever ;} goto _B_REPEAT; } 170 | 171 | 172 | #define try ;B_TRY B_LEFTP( 173 | #define B_TRY(...) { jmp_buf __buf; struct _Frame __fr; _gc_Push_Frame(&__fr, FT_TRY_FRAME); __fr.bufp = &__buf; \ 174 | BASIC_0(if !setjmp(__buf) then { M_ID M_FIRST(__VA_ARGS__ ); } else { \ 175 | M_FOR_EACH(B_CATCH, M_REST(__VA_ARGS__)) {B_FPOP(TRY) throw(exception)} }) B_FPOP(TRY) } 176 | #define B_CATCH(B) B_CAT(M_FIRST B, M_REST B) 177 | #define B_CAT(T, C) BASIC_0(if !strcmp(M_STR_(T), (*exception)->ttag) then { C; } else) 178 | 179 | #define throw(E) ThrowException((_Object)E); 180 | #define catch(E) ),(E, 181 | #define exception GetException() 182 | 183 | 184 | #define return ;{ return B_RET B_LEFTP( 185 | #define B_RET(R) _gc_Pop_Frame(FT_CALL_FRAME), M_ID R } 186 | 187 | 188 | #define and && 189 | #define or || 190 | #define not ! 191 | #define bitand & 192 | #define bitor | 193 | #define xor ^ 194 | #define shr >> 195 | #define shl << 196 | #define mod % 197 | 198 | 199 | #define B_LEFTP ( 200 | #define B_RIGHTP ) 201 | #define as , 202 | #define of , 203 | #define do ,( 204 | #define end ;)) 205 | #define in , 206 | #define to , 207 | #define step ,, 208 | #define equal , 209 | #define null NULL 210 | #define none ,void 211 | 212 | #define B_ITERATOR Iterator 213 | #define B_INAME _i 214 | #define B_GETITERATOR getIterator 215 | #define B_HASNEXT hasNext 216 | #define B_NEXT next 217 | 218 | #define B_SELECTOR int 219 | #define B_SELNAME _val 220 | 221 | //BASIC( 222 | /* 223 | function void f2(int argc, char * argv) do 224 | printf("Hello world!\n") 225 | end 226 | */ 227 | /* 228 | function f3 as (argc as int, argv as stringArray) of void do 229 | printf("Hello world!\n") 230 | end 231 | */ 232 | /* 233 | functype(IntF, ((int), void) ) 234 | function f4 as (x as int, f as IntF) of void do 235 | f(x) 236 | end 237 | */ 238 | /* 239 | function f5 as (none) of void do 240 | print("hello\n") 241 | end 242 | */ 243 | /* 244 | declare(f6 as (IntF, int) of void, 245 | f7 as (Vector3) of int) 246 | */ 247 | 248 | /* 249 | type Vector3 250 | field x as float, y as float 251 | field z as float 252 | endtype 253 | */ 254 | 255 | /* 256 | while x == 1 do 257 | print(x) 258 | end 259 | */ 260 | 261 | /* 262 | for a in 0 upto 10 do 263 | DoSomething(a) 264 | end 265 | */ 266 | /* 267 | for e in list do 268 | Something(e) 269 | end 270 | */ 271 | /* 272 | for x in a to b do 273 | print(x) 274 | end 275 | */ 276 | 277 | //if a then b else c 278 | /* 279 | if a do 280 | b 281 | elseif c do 282 | d 283 | elsedo 284 | e 285 | end 286 | */ 287 | 288 | /* 289 | select x in 290 | case a, b do 291 | print(a) 292 | case c, d do 293 | print(d) 294 | default 295 | print(0) 296 | end 297 | */ 298 | 299 | /* 300 | repeat 301 | print(a) 302 | //forever 303 | until(a == 2) 304 | */ 305 | 306 | /* 307 | try 308 | something() 309 | catch(Vector3) 310 | handle(exception) 311 | catch(string) 312 | handle2(exception) 313 | end 314 | */ 315 | 316 | //return foo end 317 | 318 | /* 319 | let 320 | i as int equal 10, j as int equal 20 321 | end 322 | var(a as int, b as float) 323 | */ 324 | /* 325 | with a as int, s as string do 326 | write(s, a) 327 | end 328 | */ 329 | //) 330 | 331 | -------------------------------------------------------------------------------- /test.c: -------------------------------------------------------------------------------- 1 | 2 | // Pyrite BASIC compiler test 3 | 4 | #include 5 | #include "pyrite.h" 6 | 7 | void func(int a) { printf("func: %d\n", a); } 8 | 9 | BASIC( 10 | 11 | type Vec3 12 | field x as float, y as float 13 | field z as float 14 | endtype 15 | 16 | declare(bar as () of void, 17 | baz as (int) of int) 18 | 19 | functype(IntF, ((int), void)) 20 | 21 | function foo as (i as int, j as int) of int do 22 | let 23 | o as Vec3 equal new(Vec3), a as stringArray equal array(string, 3), 24 | s as string equal str("123"), 25 | c as charArray equal array(char, 5), 26 | f as IntF equal func, 27 | fa as IntFArray equal array(IntF, 5) 28 | end 29 | elem(a, 0) = s ; elem(a, 1) = str("_456_") ; elem(a, 2) = str("789"); 30 | print(join(str("ABC"), str("DEF"))); bar() 31 | 32 | var(x as int) // 'var' doesn't link variables to the GC system 33 | for x in 0 to 2 do 34 | print(elem(a, x)) 35 | end 36 | 37 | return 2 * (i + j) end 38 | end 39 | 40 | type myEx 41 | field code as int, msg as string 42 | endtype 43 | 44 | function handle as (e as myEx) of void do 45 | printf("Handled exception with code %d and message %s\n", 46 | e->code, e->msg->data) 47 | end 48 | 49 | function bar as (none) of void do 50 | try 51 | printf("Trying baz...\n"); 52 | baz(45); 53 | printf("Tried baz!\n") 54 | catch(myEx) 55 | printf("Caught a myEx!\n"); handle((myEx)exception) 56 | end 57 | end 58 | 59 | function baz as (x as int) of int do 60 | printf("baz got: %d\n", x) 61 | let e as myEx equal new(myEx) end 62 | e->code = 67 ; e->msg = str("'a message'") ; throw(e) 63 | return x + 1 end 64 | end 65 | 66 | function printSA as (a as stringArray) of void do 67 | print(str("Printing string array:")) 68 | var(i as int) 69 | for i in 0 upto arraylen(a) do 70 | print(join(str(" "), elem(a, i))) 71 | end 72 | end 73 | 74 | ) 75 | 76 | // Undefine BASIC keywords so C code will work 77 | #include "pyrite-undefs.h" 78 | 79 | int main(int argc, char ** argv) { 80 | GC_Init(); 81 | GC_SetMode(GC_OFF); 82 | 83 | stringArray avec = array(string, argc); 84 | for (int i = 0; i < argc; i++) { 85 | elem(avec, i) = str(argv[i]); 86 | } 87 | 88 | GC_SetMode(GC_ON); 89 | 90 | int r = foo(42, 47); 91 | printf("foo: %d\n", r); 92 | printSA(avec); 93 | 94 | r = GC_Collect(); 95 | printf("Freed %d bytes\n", r); 96 | printf("All works fine..?\n"); 97 | 98 | return 0; 99 | } 100 | 101 | -------------------------------------------------------------------------------- /test.pyr: -------------------------------------------------------------------------------- 1 | 2 | BASIC( 3 | 4 | // Test of a simple Pyrite-project standalone program 5 | // Just print the command line arguments 6 | 7 | function MAIN as (args as stringArray) of int do 8 | print(str("Command line args:")) 9 | var(i as int) 10 | for i in 0 upto arraylen(args) do 11 | print(join(str(" "), elem(args, i))) 12 | end 13 | return 0 end 14 | end 15 | 16 | ) // Can't get rid of the outermost block 17 | --------------------------------------------------------------------------------