├── .gitignore ├── README.md ├── cpp.pl ├── cpp_common.pl ├── cpp_ops.pl ├── cpp_reader.pl ├── cpp_writer.pl ├── examples ├── 00_hello_world.c+p ├── 01_functions.c+p ├── 02_structs.c+p ├── 03_parser.c+p ├── 04_macros.c+p ├── 05_generics.c+p ├── 06_reflection.c+p ├── 07_directives.c+p ├── 08a_headers.c+p ├── 08b_headers.c+p ├── 09_switch.c+p ├── 10_function_pointers.c+p ├── 11_variadic.c+p └── meta_library.c+p ├── notes └── operator_precedence.tsv ├── test.ps1 └── test.sh /.gitignore: -------------------------------------------------------------------------------- 1 | output/ 2 | *.exe 3 | *.out -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # C Plus Prolog 2 | 3 | Prolog is the only good programming language. I should know, [my website](https://needleful.net) is [written in Prolog](https://github.com/needleful/nng). 4 | 5 | Unfortunately, C is the only useful programming language. 6 | 7 | Scientists have been trying to find an answer to this problem for nearly 50 years. Some make their [C more like Prolog](https://doc.rust-lang.org/book/ch19-06-macros.html). Others make their [Prolog more like C](https://prescheme.org/). 8 | 9 | I offer a new solution: simply add Prolog and C together. I call it, “C Plus Prolog”, or “C+P” for short. 10 | 11 | ```prolog 12 | :- include(stdio). 13 | 14 | int func main 15 | { 16 | puts("Hello, world!"); 17 | return 0 18 | }. 19 | ``` 20 | 21 | If you're familiar with C, you'll notice this is some sort of weird, bad C. You're mistaken, however. This is valid Prolog, using some [non-standard features of SWI-Prolog](https://www.swi-prolog.org/pldoc/man?section=ext-blockop) for the curly braces. 22 | 23 | This Prolog is read as a list of terms, and converted to valid C code. So this: 24 | 25 | ```prolog 26 | int func main(int var argc, char:ptr:array var argv) 27 | ``` 28 | 29 | Translates to this: 30 | 31 | ```C 32 | int main(int argc, char*( argv[])) 33 | ``` 34 | 35 | Beyond some obvious changes like `var` and `func` operators, which make the syntax expressible as Prolog, there are some unexpected quirks: 36 | 37 | - Symbols starting with capital letters or underscores are for use with Prolog, and won't translate to C unless wrapped in single quotes: 38 | 39 | ```prolog 40 | int:ptr var string = 'NULL'; 41 | '_Bool' var v = true; 42 | ``` 43 | 44 | - As a result of the above, character literals start with `#`: 45 | 46 | ```prolog 47 | char var c = #c; % quotes not required for lowercase letters 48 | char var c2 = #'C'; 49 | char var nl = #'\n'; 50 | ``` 51 | 52 | - Operator precedence was maintained for existing Prolog operators. For example, `=` and comparison operators like `==`, `<`, and `>` have the same precedence, so you'll need parentheses where you wouldn't in C: 53 | 54 | ```prolog 55 | greater = (a > b). 56 | ``` 57 | 58 | This is also why the arrow operator `->` was replaced with `@`, since it's used in a completely different way in Prolog and has a completely different precedence. 59 | 60 | C+P: 61 | ```prolog 62 | a = this@member; 63 | ``` 64 | 65 | C: 66 | 67 | ```C 68 | a = this->member; 69 | ``` 70 | 71 | - Complex type declarations are different. C-style declarations are perfectly expressible in Prolog, I just don't like them. 72 | 73 | ```prolog 74 | char:ptr:array var argv 75 | ``` 76 | 77 | Becomes 78 | 79 | ```C 80 | char *(argv[]) 81 | ``` 82 | 83 | It may also help you remember the difference between `const char *` and `char const *`: 84 | 85 | ```prolog 86 | const(char):ptr, 87 | char:const(ptr) 88 | ``` 89 | 90 | The examples provide a more complete picture of the syntax. 91 | In the end, it's C but more verbose and particular about semicolons. Not exactly a silver bullet. 92 | 93 | Let's introduce the `*=>` operator. 94 | 95 | ## The `*=>` Operator 96 | 97 | Take this snippet from example 04: 98 | 99 | ```prolog 100 | max(A, B) *=> A > B then A else B. 101 | ``` 102 | 103 | I said C+P does no processing beyond translating terms to C, but it does one small step. The compiler will gather all the terms defined with `*=>`, and then substitute the left-hand for the right-hand in the rest of the code until no more rules apply. 104 | 105 | Because this operates on Prolog terms, not text, we don't need any extra parentheses in our `max(A, B)` macro to prevent mishaps with operator precedence. It's inserted into code as a single term, and looks exactly like a function call in use: 106 | 107 | ```prolog 108 | float var f = max(12.3, 0) + 20; 109 | printf("Should be 32.3: %f\n", f);` 110 | ``` 111 | 112 | It's converted to the following C: 113 | ```C 114 | float f=(((12.3>0) ? 12.3 : 0)+20); 115 | printf("Should be 32.3: %f\n", f); 116 | ``` 117 | 118 | Also, I'm tired of adding `\n` at the end of all my `printf` statements. Let's defined another macro, `println`: 119 | 120 | ```prolog 121 | PrintLn *=> PrintF :- 122 | PrintLn =.. [println,Format| Args], 123 | string_concat(Format, "\n", Format2), 124 | PrintF =.. [printf,Format2| Args]. 125 | ``` 126 | 127 | We have full access to Prolog at compile time using `:-`, allowing us to do just about anything. 128 | This macro gets any instance of `println(Format, Args...)` with a string literal `Format`, and converts it to `printf` with a newline appended to `Format`. 129 | 130 | Simple enough. Let's implement poor man's generics. 131 | 132 | ## Poor Man's Generics in C Plus Prolog 133 | 134 | Example 05 defines a generic type, `list[T]`, using the following syntax: 135 | 136 | ```prolog 137 | list[T] 138 | { 139 | struct list[T] { 140 | T:ptr:ptr var items 141 | }; 142 | 143 | % Let's also define syntax for type-specific functions, in this case “list[T]:capacity” 144 | size_t:ptr func list[T]:capacity(list[T] var this) 145 | { 146 | ... 147 | }; 148 | ... 149 | }. 150 | ``` 151 | 152 | This will work similarly to C++ templates. 153 | For simplicity of implementation, we'll require the user to instantiate the template. 154 | 155 | ```prolog 156 | declare(list[int]). 157 | declare(list[const(char):ptr]). 158 | ``` 159 | 160 | We could probably do this automatically by scanning the code for any usage of `list[T]` and instantiating the template right above it, but I'll leave that as an exercise for the reader. 161 | 162 | We also have some syntax to get a function name with `list[T]:Method`: 163 | 164 | ```prolog 165 | int func main { 166 | list[int] var my_ints = list[int]:new(17); 167 | 168 | size_t var size = *(list[int]:capacity(my_ints)); 169 | for(int var i = 0; i < size; i += 1) 170 | { 171 | list[int]:append(my_ints, i*i) 172 | }; 173 | for(int var i = 0; i < size; i+= 1) 174 | { 175 | printf("%d squared = %d.\n", i, list[int]:get(my_ints, i)) 176 | }; 177 | return 0 178 | }. 179 | ``` 180 | 181 | Not exactly C++, but it keeps the namespaces clear. 182 | 183 | Let's read the macro. 184 | 185 | It matches on our template as you might have expected: 186 | 187 | ```prolog 188 | Name[T] {Body} *=> Comment :- 189 | \+ground(T), atom(Name), 190 | atom_concat('//Defined template type: ', Name, Comment), 191 | ``` 192 | We have a template with a name `Name`, type parameterss `T`, and the body `Body`. 193 | The macro removes this code and inserts a comment. Everything else is handled in the Prolog world. 194 | 195 | ```prolog 196 | assertz( 197 | declare(Name[Z]) *=> NewBody 198 | ``` 199 | By God. Our macro `assert`s another macro, `declare(Name[Z])`. It also has conditions: 200 | 201 | ```prolog 202 | :- ( 203 | ground(Z), 204 | T=Z, 205 | Body=NewBody, 206 | ``` 207 | Those three lines are the bulk of the macro. It unifies the template type `T` with the real (ground) type `Z`, then returns the body of the template. This is what turns `declare(list[int])` into the code for the type. 208 | 209 | But that's not all it does, the macro we're asserting itself asserts more macros: 210 | ```prolog 211 | ('*mangled_name'(Name[Z]) *=> ZName), 212 | assertz(Name[Z] *=> ZName), 213 | assertz((ZName:Method *=> ZMethod :- 214 | % Crudely hacking some shorthand for method names 215 | Method \= ptr, Method \= array, 216 | ( atom(Method) 217 | -> MName = Method, 218 | ZArgs = [] 219 | ; Method =.. [MName|ZArgs] 220 | ), 221 | ('*mangled_name'(ZName:MName) *=> MZ_Name), 222 | ZMethod =.. [MZ_Name|ZArgs] 223 | )) 224 | )). 225 | ``` 226 | 227 | This generates the C names for things like `list[int]` and `list[int]:function`. `'*mangled_name'` is just another macro, but this example is long enough and it's all in example 05. The `*` and single quotes are nothing special, they just prevent this macro from accidentally colliding with a user-defined `mangled_name` function. 228 | 229 | C+P provides no type information we could use for method syntax, to do something like `my_list.append(...)`, instead of `list[int]:append(my_list, ...)`. 230 | 231 | We could, of course, use macros to gather the types of every variable in a function body and swap out method calls for the appropriate function, but at a certain point I'm just writing a full-on XLang-to-C compiler in the macro system, which is an interesting idea, but I'm employed. 232 | 233 | I've provided several other examples of the wonders of C Plus Prolog: 234 | - Example 06 overloads the `struct` keyword to add compile-time reflection. 235 | - Example 08a compiles different code if the target file ends in `.h` or `.c`, to declare a typical header and implementation in a single file. 236 | 237 | I find it strangely compelling to write in C+P, adding features that sound impossible. It's like a puzzle game, which is why I reach for Prolog so frequently. 238 | 239 | ## Installation and Usage 240 | 241 | C Plus Prolog is easy to install. All you need is a C compiler, SWI-Prolog, and this repository. You can figure it out. 242 | 243 | Then you can run `cpp.pl` from this repository with the following syntax: 244 | `swipl -s cpp.pl -- ` 245 | 246 | By convention, C Plus Prolog files end in the extension `.c+p`. 247 | 248 | Check `test.sh` and `test.ps1` for more example usage, plus a fast way to run all the tests (which are probably not rootkits). 249 | 250 | ## What is the point of this? 251 | 252 | C Plus Prolog is a half-serious exploration of macros in a systems programming language. 253 | In the process of making this, it became clear I prefer the compile-time evaluation and reflection offered by languages like D and Zig over syntactic macros. 254 | 255 | Sure, with enough work you can do everything and more with the macro system, but what does it actually *add* over a separate code generator, or a DSL? A nicer interface, maybe. 256 | 257 | In Common Lisp, you have the full codebase at compile time, but the real value is recompiling code at runtime, not manipulating a list of lists that sort of looks like code, if you squint through all the backquotes and commas. 258 | 259 | Rust's procedural macros save maybe 40 lines of code, since you don't have to write your own tokenizer, but everything else is up to you and whatever libraries you want to bring in. 260 | 261 | Most of my metaprogramming wants involve reflecting on information the compiler already knows, like the types and annotations in the code, not the raw syntax tree. For a language-within-a-language, which syntactic macros are best at, I'd usually rather go the extra mile and make an entirely separate DSL with a purpose-built syntax, rather than contort the problem to, say, S expressions or Prolog terms. 262 | 263 | Despite that, C+P is dangerously close to being useful. 264 | The biggest advantage it has is the fact it generates plain C by default, allowing for performant cross-platform builds where less popular languages don't have support, or have to generate much more complicated C due to different abstractions. 265 | Prolog's been around for 50 years, as well. If the SWI-Prolog extensions were removed, swapping `func F {Body}` with `func F => Body`, for example, it could be built on a huge swath of hardware. 266 | 267 | But I wouldn't want to use it. There's absolutely no validation or error messaging, so any mistakes lead to broken C or silent failures. I thought using `var` as an operator would be nice, but it's a lot of visual noise. And if you thought C's semicolons were annoying, in C Plus Prolog, semicolons are operators. Having two in a row breaks things. Having one at the start breaks things. Having one at the *end* breaks things. 268 | 269 | If someone wanted to use C+P, there are many better alternatives. 270 | - The [Nim](https://nim-lang.org/) and [Haxe](https://haxe.org/) languages can compile to C and/or C++, and they offer a good user experience out of the box, though I can't say how directly their code translates to C. 271 | - [cmacro](https://github.com/eudoxia0/cmacro) offers a similar level of syntax manipulation to C+P in a nicer format, powered by Common Lisp. 272 | - [The D language](https://dlang.org/) Has compilers backed by GCC and LLVM, so it should work in most places C will. 273 | 274 | I don't know what the conclusion is. -------------------------------------------------------------------------------- /cpp.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env swipl 2 | 3 | :- module(cpp, [ 4 | compile/2 5 | ]). 6 | 7 | :- use_module(cpp_reader). 8 | :- use_module(cpp_writer). 9 | 10 | :- initialization(main, main). 11 | 12 | main([In, Out]) :- 13 | compile(In, Out). 14 | main(_) :- 15 | write("Usage: cpp "). 16 | 17 | compile(In, Out) :- 18 | retractall(cpp_reader:custom_data(_)), 19 | retractall(cpp_reader:consulted(_)), 20 | retractall(cpp_reader:'*=>'(_,_)), 21 | read_file(In, Terms), 22 | write_file(Terms, Out). -------------------------------------------------------------------------------- /cpp_common.pl: -------------------------------------------------------------------------------- 1 | :- module(cpp_common, [ 2 | expect/2 3 | ]). 4 | 5 | expect(Term, Message) :- 6 | ( call(Term) 7 | -> true 8 | ; !, writeln(Message), fail). 9 | -------------------------------------------------------------------------------- /cpp_ops.pl: -------------------------------------------------------------------------------- 1 | :- module(cpp_ops, [ 2 | c_op/2, 3 | c_standalone/1, 4 | c_type_block/1, 5 | op_rename/2 6 | ]). 7 | 8 | c_op('+', math). 9 | c_op('-', math). 10 | c_op('*', math). 11 | c_op('/', math). 12 | c_op('%', math). 13 | 14 | c_op('==', cmp). 15 | c_op('!=', cmp). 16 | c_op('>', cmp). 17 | c_op('<', cmp). 18 | c_op('>=', cmp). 19 | c_op('<=', cmp). 20 | 21 | c_op('!', bool). 22 | c_op('&&', bool). 23 | c_op('||', bool). 24 | 25 | c_op('~', bin). 26 | c_op('|', bin). 27 | c_op('&', bin). 28 | c_op('^', bin). 29 | c_op('<<', bin). 30 | c_op('>>', bin). 31 | 32 | c_op('=', assign). 33 | c_op('+=', assign). 34 | c_op('-=', assign). 35 | c_op('*=', assign). 36 | c_op('/=', assign). 37 | c_op('%=', assign). 38 | c_op('&=', assign). 39 | c_op('|=', assign). 40 | c_op('^=', assign). 41 | c_op('<<=', assign). 42 | c_op('>>=', assign). 43 | 44 | c_op(',', control). 45 | c_op(';', control). 46 | 47 | c_op('.', access). 48 | c_op('->', access). 49 | c_op('*', access). 50 | 51 | c_type_block(enum). 52 | c_type_block(struct). 53 | c_type_block(union). 54 | 55 | c_standalone(return). 56 | c_standalone(break). 57 | c_standalone(continue). 58 | 59 | op_rename('/?', '%'). 60 | op_rename('or', '||'). 61 | op_rename('and', '&&'). 62 | op_rename('=<', '<='). 63 | op_rename('@', '->'). 64 | op_rename(\=, '!='). -------------------------------------------------------------------------------- /cpp_reader.pl: -------------------------------------------------------------------------------- 1 | :- module(cpp_reader, [ 2 | read_file/2, 3 | '*=>'/2, 4 | custom_data/1, 5 | consult/1, 6 | consulted/1, 7 | this_file/2 8 | ]). 9 | 10 | :- dynamic '*=>'/2. 11 | :- dynamic consulted/1. 12 | :- dynamic custom_data/1. 13 | :- dynamic this_file/2. 14 | 15 | :- op(1150, xfx, '*=>'). 16 | :- op(975, fx, return). 17 | :- op(950, xfy, then). 18 | :- op(950, xfy, else). 19 | :- op(800, yfx, or). 20 | :- op(750, yfx, and). 21 | :- op(700, xfx, +=). 22 | :- op(700, xfx, -=). 23 | :- op(700, xfx, *=). 24 | :- op(700, xfx, '/='). 25 | :- op(700, xfx, '&='). 26 | :- op(700, xfx, '|='). 27 | :- op(700, xfx, ^=). 28 | :- op(700, xfx, '!='). 29 | :- op(690, yfx, as). 30 | :- op(680, xfx, var). 31 | :- op(680, fx, var). 32 | :- op(675, yfx, @). 33 | :- op(660, yf, {}). 34 | :- op(650, xfx, func). 35 | :- op(650, fx, func). 36 | :- op(650, fx, struct). 37 | :- op(650, fx, union). 38 | :- op(650, fx, enum). 39 | :- op(610, fx, '&'). 40 | :- op(400, yfx, '/?'). 41 | :- op(150, fx, !). 42 | :- op(110, yf, []). 43 | :- op(105, fx, *). 44 | :- op(105, xfy, @). 45 | :- op(1, fx, #). 46 | 47 | read_file(Name, Terms) :- \+ consulted(Name), 48 | assert(consulted(Name)), 49 | read_file_to_terms(Name, InTerms, [module(cpp_reader)]), 50 | find_macros(InTerms, Terms). 51 | 52 | consult(Name) :- 53 | ( atom(Name) 54 | -> atom_concat(Name, '.c+p', Path) 55 | ; Path = Name 56 | ), 57 | read_file(Path, _), 58 | format("Consulted ~w~n", [Name]). 59 | 60 | find_macros([], []). 61 | find_macros([M|Terms], Result) :- 62 | ( find_macro(M) 63 | -> find_macros(Terms, Result) 64 | ; find_macros(Terms, Next), 65 | Result = [M|Next] 66 | ). 67 | 68 | find_macro('*=>'(A, B)) :- !, 69 | assertz('*=>'(A, B)). 70 | find_macro('*=>'(A, B) :- C) :- !, 71 | assertz('*=>'(A, B) :- C). 72 | -------------------------------------------------------------------------------- /cpp_writer.pl: -------------------------------------------------------------------------------- 1 | :- module(writer, [ 2 | write_file/2 3 | ]). 4 | 5 | :- use_module(library(lists)). 6 | :- use_module(cpp_common). 7 | :- use_module(cpp_reader). 8 | :- use_module(cpp_ops). 9 | 10 | write_file(Terms, File) :- 11 | retractall(this_file), 12 | consult_files(Terms), 13 | expand_macros(Terms, NewTerms), 14 | setup_call_cleanup( 15 | open(File, write, S, [encoding(utf8), create([write])]), 16 | ( asserta(this_file(File, S)), 17 | write_lines((S,""), NewTerms) 18 | ), 19 | close(S) 20 | ), !. 21 | 22 | consult_files([]). 23 | consult_files([:- consult(File)|X]) :- 24 | consult(File), 25 | consult_files(X). 26 | consult_files([_|X]) :- consult_files(X). 27 | 28 | expand_macros(Var, Var) :- var(Var). 29 | 30 | expand_macros(Term, NewTerm) :- 31 | % Expand sub-items 32 | ( atomic(Term), 33 | Term2 = Term, 34 | ! 35 | ; is_list(Term), 36 | !, 37 | maplist(expand_macros, Term, Term2) 38 | ; compound_name_arguments(Term, Fn, Args), 39 | !, 40 | expand_macros(Fn, NewFn), 41 | expand_macros(Args, NewArgs), 42 | compound_name_arguments(Term2, NewFn, NewArgs) 43 | ; Term2 = Term, 44 | ! 45 | ), 46 | % Apply Macros until they no longer apply 47 | ( \+ var(Term2), 48 | '*=>'(Term2, Term3) 49 | -> expand_macros(Term3, NewTerm) 50 | ; NewTerm = Term2 51 | ). 52 | 53 | write_lines(_, []). 54 | write_lines((S,I), [Term|Others]) :- 55 | expand_macros(Term, ETerm), 56 | indented_line((S,I), ETerm), 57 | write_lines((S,I), Others). 58 | 59 | indented_line(SS, A;B) :- 60 | indented_line(SS, A), 61 | indented_line(SS, B). 62 | 63 | indented_line((S,I), Term) :- 64 | write(S, I), 65 | plain_line((S,I), Term), 66 | nl(S). 67 | 68 | plain_line(SS, (:- Directive)) :- !, 69 | write_directive(SS, Directive). 70 | 71 | plain_line((S,I), {}({Body}, Head)) :- 72 | block_head((S,I), Head), 73 | functor(Head, Type, _), 74 | block((S,I), Type, Body), 75 | block_tail((S,I), Head). 76 | 77 | plain_line((S,I), else(T, F)) :- 78 | plain_line((S,I), T), 79 | format(S, "~n~welse ", [I]), 80 | plain_line((S,I), F). 81 | 82 | plain_line((S, I), goto(case(Label))) :- 83 | write(S, "goto case"), 84 | exp((S,I), Label), 85 | write(S, ";"). 86 | 87 | plain_line((S, _), goto(Label)) :- 88 | format(S, "goto ~w;", [Label]). 89 | 90 | plain_line((S,I), return(Value)) :- 91 | write(S, "return "), 92 | exp((S,I), Value), 93 | write(S, ";"). 94 | 95 | plain_line((S,_), Atom) :- atom(Atom), 96 | write(S, Atom), 97 | % Standalone atoms are either a keyword or a function 98 | % taking zero arguments. 99 | ( c_standalone(Atom) 100 | -> write(S, ";") 101 | ; true 102 | ). 103 | 104 | plain_line(SS, {}(Val)) :- 105 | block(SS, base, Val). 106 | 107 | plain_line((S,I), Functor) :- 108 | ( block_head((S,I), Functor), 109 | !, 110 | block_tail((S,I), Functor) 111 | ; exp((S,I), Functor)), 112 | write(S, ";"). 113 | 114 | plain_line(_, Unknown) :- !, 115 | format("ERROR: {~W} is not valid C+P code.~n", 116 | [Unknown, [character_escapes(true), quoted(true)]]), 117 | fail. 118 | 119 | block_head((S, _), default) :- !, 120 | write(S, "default: "). 121 | block_head((S, _), A) :- atom(A), !, 122 | write(S, A). 123 | 124 | block_head((S,I), func(Name)) :- 125 | block_head((S,I), func(void, Name)). 126 | 127 | block_head((S,I), func(Type, Fn)) :- 128 | type((S,I), Type), 129 | write(S, " "), 130 | ( atom(Fn) 131 | -> format(S, "~w()", [Fn]) 132 | ; compound_name_arguments(Fn, Name, Args), 133 | write(S, Name), 134 | in_parens(S, 135 | args((S,I), Args)) 136 | ). 137 | 138 | block_head((S, I), switch(Ex)) :- 139 | write(S, "switch "), 140 | in_parens(S, 141 | exp((S, I), Ex)). 142 | 143 | block_head((S,_), label(A)) :- 144 | format(S, "~w: ", [A]). 145 | block_head((S, I), Cases) :- compound_name_arguments(Cases, case, Cs), 146 | case_labels((S, I), Cs, ('', '')). 147 | 148 | block_head((S,_), Head) :- compound_name_arguments(Head, Type, [Name]), 149 | c_type_block(Type), !, 150 | format(S, "typedef ~w ~w_t", [Type, Name]). 151 | 152 | block_head((S, I), Head) :- compound_name_arguments(Head, Type, [Exp]), 153 | format(S, "~w ", [Type]), 154 | in_parens(S, 155 | exp((S,I), Exp)). 156 | 157 | block_head((S,_), Head) :- c_type_block(Head), 158 | write(S, Head). 159 | 160 | block_tail(_, Head) :- atom(Head). 161 | block_tail((S,_), Head) :- 162 | compound_name_arguments(Head, Type, [Name]), 163 | c_type_block(Type), 164 | format(S, " ~w;", Name). 165 | block_tail(_,_). 166 | 167 | block((S,I), Type, Block) :- 168 | format(S, "~n~w{~n", [I]), 169 | string_concat(I, "\t", I2), 170 | ( Type=enum 171 | -> write_enum((S,I2), Block) 172 | ; write_func_lines((S,I2), Block) 173 | ), 174 | write(S, I), 175 | write(S, "}"). 176 | 177 | write_directive((S,I), Directive) :- 178 | compound_name_arguments(Directive, Name, Args), 179 | ( directive((S,I), Name, Args) 180 | ; call(Directive) 181 | ; this_file(F, _), 182 | format("WARNING: Directive failed: `~w` [~w]~n", [Directive, F])). 183 | 184 | directive(_, include, []). 185 | directive((S,I), include, [Name|Names]) :- 186 | ( Name = local(LocalName) 187 | -> format(S, "#include \"~w.h\"~n", [LocalName]) 188 | ; format(S, "#include <~w.h>~n", [Name]) 189 | ), 190 | directive((S,I), include, Names). 191 | 192 | write_func_lines(SS, First;Next) :- 193 | indented_line(SS, First), 194 | write_func_lines(SS, Next). 195 | 196 | write_func_lines(SS, Final) :- 197 | indented_line(SS, Final). 198 | 199 | write_enum((S,I), First;Next) :- 200 | write_enum_value((S,I), First), 201 | write(S, ",\n"), 202 | write_enum((S,I), Next). 203 | 204 | write_enum((S,I), Final) :- 205 | write_enum_value((S,I), Final), 206 | nl(S). 207 | 208 | write_enum_value((S,I), Name) :- atom(Name), 209 | write(S, I), 210 | write(S, Name). 211 | write_enum_value((S,I), Name=Value) :- atom(Name), 212 | write_enum_value((S,I), Name), 213 | write(S, " = "), 214 | exp(Value). 215 | 216 | args(_, [], _). 217 | args((S,I), [V|A]) :- !, 218 | ( V = var(Type, Args) 219 | -> var((S,I), Type, Args, ", ") 220 | ; type((S,I), V) 221 | ), 222 | ( A = [_|_] 223 | -> write(S, ", "), 224 | args((S,I), A) 225 | ; true 226 | ). 227 | 228 | type((S,I), Type) :- 229 | var((S,I), Type, [], " "). 230 | 231 | var((S,I), Type, (Name,Names), Sep) :- 232 | var((S,I), Type, Name, _), 233 | write(S, Sep), 234 | var((S,I), Type, Names, Sep). 235 | var(SS, Type, Name, _) :- 236 | single_var(SS, Type, Name). 237 | 238 | single_var(SS, (Base:Type):Sp, Name) :- !, 239 | single_var(SS, Base:Type:Sp, Name). 240 | single_var(SS, (A->B):Special, Name) :- !, 241 | special_var(SS, (A->B):Special, Name). 242 | single_var(SS, (A->B), Name) :- 243 | special_var(SS, (A->B), Name). 244 | single_var(SS, BaseType:Special, Name) :- !, 245 | qual_type(SS, BaseType, false), 246 | special_var(SS, Special, Name). 247 | single_var(SS, Type, []) :- !, 248 | qual_type(SS, Type, false). 249 | single_var((S,I), Type, Name) :- !, 250 | qual_type((S,I), Type, false), 251 | write(S, " "), 252 | exp((S,I), Name). 253 | 254 | 255 | qual_type((S,_), ptr, CouldBePointer) :- 256 | ( CouldBePointer = true 257 | -> write(S, "*") 258 | ; write(S, ptr) 259 | ). 260 | qual_type((S,_), Atom, _) :- atom(Atom), 261 | write(S, Atom). 262 | qual_type((S,I), typeof(Exp), _) :- 263 | write(S, 'typeof'), 264 | in_parens(S, 265 | exp((S,I), Exp)). 266 | qual_type(SS, atomic(Type), CBP) :- 267 | qual_type(SS, '_Atomic'(Type), CBP). 268 | qual_type(SS, {}(Body, Head), _) :- 269 | plain_line(SS, {}(Body, Head)). 270 | qual_type((S,I), Type, CBP) :- Type =.. [Qual,SubType], 271 | write(S, Qual), 272 | write(S, " "), 273 | qual_type((S,I), SubType, CBP). 274 | qual_type(_, Type, _) :- !, 275 | throw(error(domain_error('A valid C+P Type Term', Type), qual_type/3)). 276 | 277 | special_var(SS, (A:B):C,Name) :- 278 | special_var(SS, A:B:C, Name). 279 | 280 | special_var((S,I), First:Next, Name) :- 281 | type_prefix((S,I), First), 282 | ( Name = [] 283 | -> special_var((S,I), Next, Name) 284 | ; in_parens(S, 285 | special_var((S,I), Next, Name)) 286 | ), 287 | type_suffix((S,I), First). 288 | 289 | special_var((S,I), Last, Name) :- 290 | type_prefix((S,I), Last), 291 | ( Name = [] 292 | ; write(S, " "), 293 | write(S, Name)), 294 | type_suffix((S,I), Last). 295 | 296 | type_prefix((S,_), ptr) :- write(S, "*"). 297 | type_prefix(_, array). 298 | type_prefix(_, array(_)). 299 | type_prefix(SS, (_ -> Out)) :- 300 | type(SS, Out). 301 | type_prefix(SS, Qual) :- qual_type(SS, Qual, true). 302 | type_prefix(_,_). 303 | 304 | type_suffix((S, _), array) :- write(S, "[]"). 305 | type_suffix((S, _), array(Len)) :- format(S, "[~w]", [Len]). 306 | type_suffix((S, I), (In -> _)) :- 307 | comma_fold(In, InList), 308 | in_parens(S, 309 | args((S, I), InList) 310 | ). 311 | type_suffix(_, _). 312 | 313 | case_labels(_, [], _). 314 | case_labels((S, I), [C|Cs], (NL, I2)) :- 315 | format(S, "~w~wcase ", [I2, NL]), 316 | exp((S, I), C), 317 | write(S, ": "), 318 | case_labels((S,I), Cs, ('\n', I)). 319 | 320 | list(_, [], _). 321 | list(S, [Last], _) :- 322 | write(S, Last). 323 | list(S, [H|T], Sep) :- 324 | write(S, H), 325 | write(S, Sep), 326 | list(S, T, Sep). 327 | 328 | exp((S,_), A) :- atom(A), 329 | write(S, A). 330 | 331 | exp((S,_), A) :- atomic(A), 332 | write_term(S, A, [quoted(true)]). 333 | 334 | exp(SS, Type:Attrib) :- 335 | type(SS, Type:Attrib). 336 | 337 | exp((S,I), [](Index, Array)) :- 338 | exp((S,I), Array), 339 | write(S, "["), 340 | exp_list((S,I), Index), 341 | write(S, "]"). 342 | 343 | exp(SS, var(Type, Name)) :- 344 | var(SS, Type, Name, "; "). 345 | 346 | exp((S,I), then(Cond, else(IfThen, IfElse))) :- 347 | in_parens(S, 348 | ( exp((S,I), Cond), 349 | write(S, " ? "), 350 | exp((S,I), IfThen), 351 | write(S, " : "), 352 | exp((S,I), IfElse))). 353 | 354 | exp((S,I), as(Exp, Type)) :- 355 | in_parens(S, ( 356 | in_parens(S, 357 | type((S,I), Type)), 358 | exp((S,I), Exp))). 359 | 360 | exp((S,I), {Val}) :- 361 | write(S, "{"), 362 | struct_literal((S,I), Val), 363 | write(S, "}"). 364 | 365 | exp((S, _), #(A)) :- 366 | ( atom(A), 367 | char_code(A, N) 368 | ; integer(A), 369 | N = A), 370 | format(S, "'\\~8r'", N). 371 | 372 | exp((S,I), Fn) :- compound_name_arguments(Fn, Name, Args), 373 | cpp_functor((S,I), Name, Args). 374 | 375 | struct_literal((S,I), (A,B)) :- 376 | struct_field((S,I), A), 377 | write(S, ", "), 378 | struct_literal((S,I), B). 379 | struct_literal((S,I), A) :- 380 | struct_field((S,I), A). 381 | 382 | struct_field((S,I), Name=Value) :- 383 | format(S, ".~w = ", [Name]), 384 | exp((S,I), Value). 385 | 386 | struct_field(SS, V) :- 387 | exp(SS, V). 388 | 389 | cpp_functor(SS, Op, [A]) :- c_op(Op, Type), 390 | unary_op(SS, Op, Type, A). 391 | 392 | cpp_functor(SS, Op, [A, B]) :- c_op(Op, Type), 393 | bin_op(SS, Type, Op, A, B). 394 | 395 | cpp_functor(SS, Op, [A]) :- op_rename(Op, COp), 396 | c_op(COp, Type), 397 | unary_op(SS, COp, Type, A). 398 | 399 | cpp_functor(SS, Op, [A, B]) :- op_rename(Op, COp), 400 | c_op(COp, Type), 401 | bin_op(SS, Type, COp, A, B). 402 | 403 | cpp_functor((S,I), Fn, Args) :- 404 | write(S, Fn), 405 | in_parens(S, 406 | exp_list((S,I), Args)). 407 | 408 | bin_op((S,I), Type, Op, A, B) :- 409 | ( (Type = assign; Type = control) 410 | -> exp((S,I), A), 411 | write(S, Op), 412 | exp((S,I), B) 413 | ; 414 | in_parens(S, 415 | ( exp((S,I), A), 416 | write(S, Op), 417 | exp((S,I), B))) 418 | ). 419 | 420 | unary_op((S, I), Op, _, A) :- 421 | in_parens(S, 422 | ( write(S, Op), 423 | exp((S, I), A))). 424 | 425 | exp_list(_, []). 426 | exp_list((S,I), [A]) :- 427 | exp((S,I), A). 428 | exp_list((S,I), [A|Args]) :- 429 | exp((S,I), A), 430 | write(S, ", "), 431 | exp_list((S,I), Args). 432 | 433 | in_parens(S, Format) :- 434 | write(S, "("), 435 | call(Format), 436 | write(S, ")"). 437 | 438 | comma_fold((A,B), [A|List]) :- 439 | comma_fold(B, List). 440 | comma_fold(A, [A]). -------------------------------------------------------------------------------- /examples/00_hello_world.c+p: -------------------------------------------------------------------------------- 1 | :- include(stdio). 2 | 3 | int func main 4 | { 5 | puts("Hello, world!"); 6 | return 0 7 | }. -------------------------------------------------------------------------------- /examples/01_functions.c+p: -------------------------------------------------------------------------------- 1 | :- include(stdio, stdlib). 2 | 3 | int func add(int var (a, b)) 4 | { 5 | return a+b 6 | }. 7 | 8 | func nop 9 | { 10 | return 11 | }. 12 | 13 | func even_check(int var x) 14 | { 15 | nop(); 16 | printf("%d is %s\n", x, 17 | x /? 2 then "odd" else "even") 18 | }. 19 | 20 | int func main(int var argc, char:ptr:array var argv) 21 | { 22 | if(argc < 3) 23 | { 24 | printf("Usage: `%s `\n", argv[0]); 25 | return 1 26 | }; 27 | int var 28 | a = atoi(argv[1]), 29 | b = atoi(argv[2]); 30 | even_check(a); 31 | even_check(b); 32 | printf("%d + %d = %d!\n", a, b, add(a,b)); 33 | return 0 34 | }. 35 | -------------------------------------------------------------------------------- /examples/02_structs.c+p: -------------------------------------------------------------------------------- 1 | :- include(stdbool, stdio). 2 | 3 | struct my_struct 4 | { 5 | int var a, b, c 6 | }. 7 | 8 | union my_union { 9 | int var integer; 10 | float var real_num; 11 | bool:array(4) var bools 12 | }. 13 | 14 | func print_struct(my_struct var ms) 15 | { 16 | printf("{a=%d, b=%d, c=%d}\n", ms.a, ms.b, ms.c) 17 | }. 18 | 19 | int func main { 20 | my_struct var m1 = {12, 13, 16} as my_struct; 21 | my_struct var m2 = {a = 12, c = 32, b = 44}; 22 | print_struct(m1); 23 | print_struct(m2); 24 | return 0 25 | }. -------------------------------------------------------------------------------- /examples/03_parser.c+p: -------------------------------------------------------------------------------- 1 | :- include(ctype, stdbool, stdio, stdlib, string). 2 | 3 | % Some types from a lisp parser 4 | 5 | enum type 6 | { 7 | type_NIL; 8 | type_SYMBOL; 9 | type_BOOL; 10 | type_INT; 11 | type_FLOAT; 12 | type_LIST; 13 | type_TEXT 14 | }. 15 | 16 | struct item. 17 | 18 | struct list { item:ptr:ptr var items}. 19 | 20 | struct span {int var (start, length)}. 21 | 22 | struct item 23 | { 24 | (union 25 | { 26 | list var list; 27 | char:ptr var text; 28 | long var integer; 29 | double var real; 30 | unsigned(long) var uinteger 31 | }) var data; 32 | span var span; 33 | type var type 34 | }. 35 | 36 | int func main 37 | { 38 | return 0 39 | }. -------------------------------------------------------------------------------- /examples/04_macros.c+p: -------------------------------------------------------------------------------- 1 | :- include(stdio). 2 | 3 | max(A, B) *=> A > B then A else B. 4 | 5 | func max_check(int var (a, b)) 6 | { 7 | println("Max of %d and %d: %d", a, b, max(a, b)) 8 | }. 9 | 10 | PrintLn *=> PrintF :- 11 | PrintLn =.. [println,Format| Args], 12 | string_concat(Format, "\n", Format2), 13 | PrintF =.. [printf,Format2| Args]. 14 | 15 | int func main 16 | { 17 | max_check(12, 14); 18 | float var f = max(12.3, 0) + 20; 19 | println("Should be 32.3: %f", f); 20 | return 0 21 | }. -------------------------------------------------------------------------------- /examples/05_generics.c+p: -------------------------------------------------------------------------------- 1 | :- include(ctype, stdbool, stdio, stdlib, string). 2 | 3 | % Poor man's generics 4 | list[T] 5 | { 6 | struct list[T] { 7 | T:ptr:ptr var items 8 | }; 9 | 10 | size_t:ptr func list[T]:capacity(list[T] var this) 11 | { 12 | return this.items 13 | then (*this.items as size_t:ptr) - 2 14 | else 0 15 | }; 16 | 17 | size_t:ptr func list[T]:length(list[T] var this) 18 | { 19 | return this.items 20 | then (*this.items as size_t:ptr) - 1 21 | else 0 22 | }; 23 | 24 | list[T] func list[T]:new(size_t var capacity) 25 | { 26 | list[T] var new_list; 27 | new_list.items = malloc(sizeof(T:ptr:ptr)) as T:ptr:ptr; 28 | void:ptr var raw_data = malloc(sizeof(T)*capacity + sizeof(size_t)*2); 29 | *new_list.items = & (raw_data as size_t:ptr)[2] as T:ptr; 30 | *(list[T]:capacity(new_list)) = capacity; 31 | *(list[T]:length(new_list)) = 0; 32 | return new_list 33 | }; 34 | 35 | func list[T]:resize(list[T] var this, size_t var new_capacity) 36 | { 37 | void:ptr var raw_data = realloc( 38 | list[T]:capacity(this) as void:ptr, 39 | sizeof(T)*new_capacity + sizeof(size_t)*2); 40 | *this.items = (raw_data as size_t:ptr) + 2 as T:ptr; 41 | *(list[T]:capacity(this)) = new_capacity 42 | }; 43 | 44 | func list[T]:clear(list[T]:ptr var this) 45 | { 46 | free(list[T]:capacity(*this)); 47 | free(this@items); 48 | this@items = 'NULL' 49 | }; 50 | 51 | T:ptr func list[T]:at(list[T] var this, size_t var id) 52 | { 53 | return *this.items+id 54 | }; 55 | 56 | T func list[T]:get(list[T] var this, size_t var id) 57 | { 58 | return *(list[T]:at(this, id)) 59 | }; 60 | 61 | func list[T]:append(list[T] var this, T var item) 62 | { 63 | if(!this.items) 64 | { 65 | puts("Internal error. List was not allocated properly."); 66 | return 67 | } 68 | else if(!*this.items) 69 | { 70 | *this.items = *((list[T]:new(1)).items) 71 | }; 72 | size_t var length = *(list[T]:length(this)); 73 | size_t var capacity = *(list[T]:capacity(this)); 74 | if(length >= capacity) 75 | { 76 | list[T]:resize(this, capacity*2) 77 | }; 78 | *(list[T]:at(this, length)) = item; 79 | *(list[T]:length(this)) += 1 80 | }; 81 | 82 | T func list[T]:pop(list[T] var this) 83 | { 84 | if(!this.items or !*this.items or *(list[T]:length(this)) =< 0) 85 | { 86 | puts("Error: Popped empty list."); 87 | T var junk = 0 as T; 88 | return junk 89 | } 90 | else { 91 | size_t:ptr var l = list[T]:length(this); 92 | *l -= 1; 93 | return (*this.items)[*l] 94 | } 95 | } 96 | }. 97 | 98 | % We still need to declare the structs 99 | declare(list[int]). 100 | declare(list[const(char):ptr]). 101 | 102 | strings *=> list[const(char):ptr]. 103 | 104 | int func main { 105 | list[int] var int_list = list[int]:new(17); 106 | strings var str_list = strings:new(10); 107 | printf("Lists created with capacities %ld and %ld\n", 108 | *(list[int]:capacity(int_list)), 109 | *(strings:capacity(str_list))); 110 | 111 | size_t var size = *(list[int]:capacity(int_list)); 112 | for(int var i = 0; i < size; i += 1) 113 | { 114 | list[int]:append(int_list, i*i) 115 | }; 116 | for(int var i = 0; i < size; i+= 1) 117 | { 118 | printf("%d squared = %d.\n", i, list[int]:get(int_list, i)) 119 | }; 120 | return 0 121 | }. 122 | 123 | % The templates macro. It generates no code. 124 | % Instead, it asserts a new macro in order to generate code for each declaration. 125 | % The generating macro itself asserts macros for renaming things. 126 | Name[T] {Body} *=> Comment :- 127 | atom_concat('//Defined template type: ', Name, Comment), 128 | \+ground(T), atom(Name), 129 | assertz( 130 | declare(Name[Z]) *=> NewBody 131 | :- ( 132 | ground(Z), 133 | T=Z, 134 | Body=NewBody, 135 | ('*mangled_name'(Name[Z]) *=> ZName), 136 | assertz(Name[Z] *=> ZName), 137 | assertz((ZName:Method *=> ZMethod :- 138 | % Crudely hacking some shorthand for method names 139 | Method \= ptr, Method \= array, 140 | ( atom(Method) 141 | -> MName = Method, 142 | ZArgs = [] 143 | ; Method =.. [MName|ZArgs] 144 | ), 145 | ('*mangled_name'(ZName:MName) *=> MZ_Name), 146 | ZMethod =.. [MZ_Name|ZArgs] 147 | )) 148 | )). 149 | 150 | % Prefixing these macros with * to prevent name collisions with user functions and macros 151 | '*mangled_name'(Type) *=> Renamed :- 152 | ('*unfold_type'(Type) *=> TypeList), 153 | atomic_list_concat(TypeList, '_', Renamed). 154 | 155 | '*unfold_type'(A) *=> [A] :- atom(A), !. 156 | '*unfold_type'(A:B) *=> AB :- !, 157 | ('*unfold_type'(A) *=> AL), 158 | ('*unfold_type'(B) *=> BL), 159 | A \= [], B \= [], 160 | append(AL, BL, AB). 161 | '*unfold_type'(Name[T]) *=> NT :- !, 162 | ('*unfold_type'(Name:T:gen) *=> NT). 163 | '*unfold_type'(Fn) *=> QA :- Fn =.. [Q,A], !, 164 | ('*unfold_type'(Q:A) *=> QA). 165 | '*unfold_type'(C) *=> invalid_type(C) :- !, 166 | this_file(F, _), 167 | format("ERROR: Invalid Type: `~w` [~w]~n", [C, F]). -------------------------------------------------------------------------------- /examples/06_reflection.c+p: -------------------------------------------------------------------------------- 1 | :- include(stdio). 2 | :- consult('examples/meta_library'). 3 | 4 | struct position 5 | { 6 | float var (x, y) 7 | }. 8 | struct size 9 | { 10 | float var (w, h) 11 | }. 12 | struct my_struct 13 | { 14 | int var cars; 15 | float var distance; 16 | size var m_size; 17 | position var pos 18 | }. 19 | int func main 20 | { 21 | my_struct var this = 22 | { 23 | cars = 12, 24 | distance = 12, 25 | m_size = {w = 100, h = 30}, 26 | pos = {x = 3009.9, y = -13.2} 27 | }; 28 | print(my_struct, this); 29 | return 0 30 | }. 31 | 32 | % We can't print unbound variables or they get instantiated, 33 | % and that breaks nested expansion. 34 | print(V, _) *=> V :- var(V), !, fail. 35 | print(int, I) *=> printf("%d\n", I). 36 | print(float, F) *=> printf("%f\n", F). 37 | 38 | print(Struct, S) *=> 39 | printf("%s:\n", nameof(Struct)); 40 | forall((FType, FName): Struct.fields) 41 | { 42 | printf("\t%s: ", nameof(FName)); 43 | print(FType, S.FName) 44 | }; 45 | printf("end %s.\n", nameof(Struct)) 46 | :- custom_data(inspected_struct(Struct)). 47 | 48 | print(_, S) *=> printf("%s (can't print)\n", nameof(S)). 49 | 50 | % A macro that returns the same syntax as before, but records the type info. 51 | % Uses a compiler-provided dynamic predicate custom_data/1. 52 | struct Name {Body} *=> struct Name {Body} :- 53 | % Now we have to prevent this macro from running again 54 | % otherwise it loops infinitely because the syntax is the same. 55 | \+ custom_data(inspected_struct(Name)), 56 | format("Inspecting: ~w~n", [Name]), 57 | (var_tuples(Body, []) *=> FieldTuples), 58 | assertz(custom_data(inspected_struct(Name))), 59 | assertz(Name.fields *=> FieldTuples). 60 | 61 | var_tuples(A;B, List) *=> Result :- 62 | (var_tuple(A, List) *=> List2), 63 | (var_tuples(B, List2) *=> Result). 64 | var_tuples(Final, List) *=> var_tuple(Final, List). 65 | 66 | var_tuple(T var N, List) *=> List2 :- 67 | ( atom(N), 68 | R = [(T, N)] 69 | ; N = (Name = _), 70 | R = [(T, Name)] 71 | ; N = (_,_), 72 | (var_tuple_multi(T, N, []) *=> R) 73 | ), 74 | append(List, R, List2). 75 | % Ignore anything that's not a variable. 76 | var_tuple(_, List) *=> List. 77 | 78 | var_tuple_multi(T, (V,Next), List) *=> var_tuple_multi(T, Next, List2) :- 79 | !, 80 | append(List, [(T, V)], List2). 81 | var_tuple_multi(T, V, List) *=> var_tuple(T var V, List). 82 | -------------------------------------------------------------------------------- /examples/07_directives.c+p: -------------------------------------------------------------------------------- 1 | 2 | % Let's leave some comments in the C source 3 | comment: Comment *=> ( 4 | % If a directive isn't recognized (such as :- include(...)), 5 | % it's executed directly by the compiler. 6 | :- this_file(F, S), format(S, "//~w [~w]~n", [Comment, F]) 7 | ). 8 | 9 | comment: "I can leave messages for myself". 10 | 11 | int func main 12 | { 13 | return 0 14 | }. 15 | -------------------------------------------------------------------------------- /examples/08a_headers.c+p: -------------------------------------------------------------------------------- 1 | module('08a_headers') 2 | { 3 | (:- include(stdint)); 4 | private {:- include(stdio)}; 5 | 6 | struct this_struct 7 | { 8 | int32_t var (a, b) 9 | }; 10 | 11 | private 12 | { 13 | int var counter; 14 | static(int32_t) func add(this_struct var s) 15 | { 16 | counter += 1; 17 | return s.a + s.b + counter 18 | } 19 | }; 20 | 21 | this_struct var this_var = {a=1, b=2}; 22 | 23 | func this_func(this_struct var s) 24 | { 25 | printf("this func {%d, %d}: %d\n", s.a, s.b, add(s)) 26 | } 27 | }. 28 | 29 | module(Name) {Body} *=> NewBody :- 30 | this_file(Path, _), 31 | ( atom_concat(_, '.h', Path) 32 | -> (module_header(Name) {Body} *=> NewBody) 33 | ; (module_body(Name){Body} *=> NewBody) 34 | ). 35 | 36 | module_header(Name) {Body} *=> 37 | \+ ['#ifndef h', Name, '_h']; 38 | \+ ['#define h', Name, '_h']; 39 | PublicBody; 40 | \+ ['#endif //h', Name, '_h'] 41 | :- 42 | (remove_impl(Body) *=> PublicBody). 43 | 44 | module_body(Name) {Body} *=> 45 | (:- include(local(Name))); 46 | ImplBody 47 | :- 48 | (only_impl(Body) *=> ImplBody). 49 | 50 | remove_impl(A;B) *=> remove_impl_item(A); remove_impl(B). 51 | remove_impl(Other) *=> remove_impl_item(Other). 52 | 53 | remove_impl_item(func X {Body}) *=> func X. 54 | remove_impl_item(T func X {Body}) *=> T func X. 55 | remove_impl_item(T var X = Exp) *=> extern(T) var X. 56 | remove_impl_item(private {_}) *=> ''. 57 | remove_impl_item(Other) *=> Other. 58 | 59 | only_impl(A;B) *=> only_impl_item(A); only_impl(B). 60 | only_impl(Other) *=> only_impl_item(Other). 61 | 62 | only_impl_item(func X {Body}) *=> func X {Body}. 63 | only_impl_item(T func X {Body}) *=> T func X {Body}. 64 | only_impl_item(T var X = Exp) *=> T var X = Exp. 65 | only_impl_item(private {Body}) *=> Body. 66 | only_impl_item(Other) *=> ''. 67 | 68 | \+ Atoms *=> Concat :- 69 | atomic_list_concat(Atoms, Concat). 70 | -------------------------------------------------------------------------------- /examples/08b_headers.c+p: -------------------------------------------------------------------------------- 1 | :- include(local('08a_headers')). 2 | 3 | int func main { 4 | this_struct var ms = {a = 1, b = 200}; 5 | this_func(ms); 6 | this_func(this_var) 7 | }. -------------------------------------------------------------------------------- /examples/09_switch.c+p: -------------------------------------------------------------------------------- 1 | :- include(stdio, stdlib, string). 2 | 3 | int func main { 4 | puts("How many chickens do you have?"); 5 | char:ptr var line = 'NULL'; 6 | while(1) 7 | { 8 | size_t var line_len; 9 | getline(&line, &line_len, stdin); 10 | int var chickens = atoi(line); 11 | switch(chickens) { 12 | case(0) { 13 | puts("No chickens? How sad!"); 14 | goto(failure) 15 | }; 16 | case(1) { 17 | puts("A single one? That's no way to live life."); 18 | break 19 | }; 20 | case(6) { 21 | puts("Six is my lucky number!") 22 | % Fallthrough 23 | }; 24 | case(2, 3, 4, 5) { 25 | puts("Alright, you've got a few. That's good."); 26 | break 27 | }; 28 | default { 29 | puts("Alright, you have quite a few chickens, actually") 30 | } 31 | } 32 | }; 33 | return 0; 34 | 35 | label(failure) { 36 | free(line); 37 | return 1 38 | } 39 | }. -------------------------------------------------------------------------------- /examples/10_function_pointers.c+p: -------------------------------------------------------------------------------- 1 | :- include(stdio). 2 | 3 | % A rather inelegant syntax for function pointers 4 | func show(const(char):ptr var name, (int, int -> int):ptr var fn) { 5 | int var a = 20, b = 32; 6 | printf("%s(%d, %d) = %d\n", name, a, b, fn(a, b)) 7 | }. 8 | 9 | struct callback { 10 | (const(char):ptr -> int):ptr var fn; 11 | const(char):ptr var arg 12 | }. 13 | 14 | func repeat(unsigned(int) var times, callback var f) { 15 | for(int var i = 0; i < times; i += 1) { 16 | f.fn(f.arg) 17 | } 18 | }. 19 | 20 | int func mul(int var a, int var b) { 21 | return a*b 22 | }. 23 | 24 | int func add(int var a, int var b) { 25 | return a+b 26 | }. 27 | 28 | int func main { 29 | show("add", &add); 30 | show("mul", &mul); 31 | callback var c = {fn = &puts, arg = "Hello"}; 32 | repeat(5, c) 33 | }. -------------------------------------------------------------------------------- /examples/11_variadic.c+p: -------------------------------------------------------------------------------- 1 | :- include(stdarg, stdbool, stdio). 2 | 3 | % I spent 20 minutes trying to figure out why this code wasn't working. 4 | % It was because I forgot to put break. 5 | % So here's a macro to do it for me. 6 | X -> {Body} *=> case(X) {Body;break}. 7 | 8 | func print_line(const(char):ptr var format, ...) 9 | { 10 | va_list var args; 11 | va_start(args, format); 12 | 13 | bool var format_char = false; 14 | '// Useful to note: atoms are inserted literally'; 15 | for('';*format \= #0; format += 1) { 16 | if(format_char) { 17 | switch(*format) { 18 | #i -> { 19 | long var i = va_arg(args, long); 20 | printf("%ld", i) 21 | }; 22 | #f -> { 23 | double var f = va_arg(args, double); 24 | printf("%f", f) 25 | }; 26 | #'%' -> { 27 | putc(#'%',stdout) 28 | }; 29 | default { 30 | putc(*format,stdout) 31 | } 32 | }; 33 | format_char = false 34 | } 35 | else if(*format == #'%') { 36 | format_char = true 37 | } 38 | else { 39 | putc(*format,stdout) 40 | } 41 | }; 42 | putc(#'\n',stdout); 43 | va_end(args) 44 | }. 45 | 46 | int func main 47 | { 48 | print_line("Print: %i %f", 109, 2.5) 49 | }. -------------------------------------------------------------------------------- /examples/meta_library.c+p: -------------------------------------------------------------------------------- 1 | % A file with some common meta stuff. 2 | 3 | % Compile-time loop. 4 | % Usage could be something like 5 | % forall(Name: [a, b, c]) { Name += 1 }. 6 | % It uses `findall`, which filters out terms that don't match the Bind, 7 | % so it's ripe with opportunity for some big-brain filtering. 8 | forall(Bind: List) {Body} *=> statement_fold(S, NList) :- 9 | findall(Body, member(Bind, List), [S|NList]). 10 | 11 | % Turn a Prolog list into C statements 12 | statement_fold(S, []) *=> S. 13 | statement_fold(S, [N|NList]) *=> statement_fold(S;N, NList). 14 | 15 | % Good old 'nameof'. Everybody needs it. 16 | nameof(Exp) *=> String :- ground(Exp), sformat(String, "~w", Exp). -------------------------------------------------------------------------------- /notes/operator_precedence.tsv: -------------------------------------------------------------------------------- 1 | (C operators) (Prolog Operators) C+P Custom Operators Notes 2 | x++ x-- () [] . -> (Type){list} $x . [] {} x@y (for ->) #x (for characters) 3 | +x -x --x ++x !x ~x (cast) *x &x +x -x \x ++x --x *x &x !x ~x 4 | ^ 5 | ** 6 | * / % * /, etc /? (for modulo) 7 | ?x 8 | + - + -, etc 9 | << >> << >> 10 | : Use : for casts? 11 | < > etc > < = == as is, etc 12 | == != (Ouch. Just going to have slightly different precedence here) 13 | & ^ | \+ x \+ \* \- (binary ops) 14 | && and 15 | || or 16 | x:y?z x then y else z 17 | = += -=, etc := += -= |=, etc 18 | , , 19 | func struct, etc 20 | var 21 | -> *-> Can I use these somewhere? 22 | ; Use as statement separator 23 | | :: (something for bodies?) 24 | volatile public dynamic, etc probably don't use these 25 | :- ?- => --> etc => for macros, maybe? 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /test.ps1: -------------------------------------------------------------------------------- 1 | param([string] $cc) 2 | 3 | swipl -s cpp.pl -- .\examples\00_hello_world.c+p .\output\00_hello_world.c 4 | swipl -s cpp.pl -- .\examples\01_functions.c+p .\output\01_functions.c 5 | swipl -s cpp.pl -- .\examples\02_structs.c+p .\output\02_structs.c 6 | swipl -s cpp.pl -- .\examples\03_parser.c+p .\output\03_parser.c 7 | swipl -s cpp.pl -- .\examples\04_macros.c+p .\output\04_macros.c 8 | swipl -s cpp.pl -- .\examples\05_generics.c+p .\output\05_generics.c 9 | swipl -s cpp.pl -- .\examples\06_reflection.c+p .\output\06_reflection.c 10 | swipl -s cpp.pl -- .\examples\07_directives.c+p .\output\07_directives.c 11 | swipl -s cpp.pl -- .\examples\08a_headers.c+p .\output\08a_headers.c 12 | swipl -s cpp.pl -- .\examples\08a_headers.c+p .\output\08a_headers.h 13 | swipl -s cpp.pl -- .\examples\08b_headers.c+p .\output\08b_headers.c 14 | swipl -s cpp.pl -- .\examples\09_switch.c+p .\output\09_switch.c 15 | swipl -s cpp.pl -- .\examples\10_function_pointers.c+p .\output\10_function_pointers.c 16 | swipl -s cpp.pl -- .\examples\11_variadic.c+p .\output\11_variadic.c 17 | 18 | echo "Compiling C..." 19 | iex "$($cc) -Wall .\output\00_hello_world.c -o .\output\00_hello_world.exe" 20 | iex "$($cc) -Wall .\output\01_functions.c -o .\output\01_functions.exe" 21 | iex "$($cc) -Wall .\output\02_structs.c -o .\output\02_structs.exe" 22 | iex "$($cc) -Wall ./output/03_parser.c -o ./output/03_parser.exe" 23 | iex "$($cc) -Wall ./output/04_macros.c -o ./output/04_macros.exe" 24 | iex "$($cc) -Wall ./output/05_generics.c -o ./output/05_generics.exe" 25 | iex "$($cc) -Wall ./output/06_reflection.c -o ./output/06_reflection.exe" 26 | iex "$($cc) -Wall ./output/07_directives.c -o ./output/07_directives.exe" 27 | iex "$($cc) -Wall ./output/08a_headers.c -c -o ./output/08a_headers.o" 28 | iex "$($cc) -Wall ./output/08b_headers.c -c -o ./output/08b_headers.o" 29 | iex "$($cc) -Wall ./output/08a_headers.o ./output/08b_headers.o -o ./output/08_headers.exe" 30 | iex "$($cc) -Wall ./output/09_switch.c -o ./output/09_switch.exe" 31 | iex "$($cc) -Wall ./output/10_function_pointers.c -o ./output/10_function_pointers.exe" 32 | iex "$($cc) -Wall ./output/11_variadic.c -o ./output/11_variadic.exe" 33 | 34 | echo "Running code..." 35 | .\output\00_hello_world.exe 36 | .\output\01_functions.exe 45 62 37 | .\output\02_structs.exe 38 | .\output\03_parser.exe 39 | .\output\04_macros.exe 40 | .\output\05_generics.exe 41 | .\output\06_reflection.exe 42 | .\output\07_directives.exe 43 | .\output\08_headers.exe 44 | .\output\09_switch.exe 45 | .\output\10_function_pointers.exe 46 | .\output\11_variadic.exe -------------------------------------------------------------------------------- /test.sh: -------------------------------------------------------------------------------- 1 | if [ ! -d "./output" ]; then 2 | echo Creating output folder. 3 | mkdir output 4 | fi 5 | rm output/*.c 6 | rm output/*.out 7 | 8 | echo "Converting to C..." 9 | swipl -s cpp.pl -- ./examples/00_hello_world.c+p ./output/00_hello_world.c 10 | swipl -s cpp.pl -- ./examples/01_functions.c+p ./output/01_functions.c 11 | swipl -s cpp.pl -- ./examples/02_structs.c+p ./output/02_structs.c 12 | swipl -s cpp.pl -- ./examples/03_parser.c+p ./output/03_parser.c 13 | swipl -s cpp.pl -- ./examples/04_macros.c+p ./output/04_macros.c 14 | swipl -s cpp.pl -- ./examples/05_generics.c+p ./output/05_generics.c 15 | swipl -s cpp.pl -- ./examples/06_reflection.c+p ./output/06_reflection.c 16 | swipl -s cpp.pl -- ./examples/07_directives.c+p ./output/07_directives.c 17 | swipl -s cpp.pl -- ./examples/08a_headers.c+p ./output/08a_headers.c 18 | swipl -s cpp.pl -- ./examples/08a_headers.c+p ./output/08a_headers.h 19 | swipl -s cpp.pl -- ./examples/08b_headers.c+p ./output/08b_headers.c 20 | swipl -s cpp.pl -- ./examples/09_switch.c+p ./output/09_switch.c 21 | swipl -s cpp.pl -- ./examples/10_function_pointers.c+p ./output/10_function_pointers.c 22 | swipl -s cpp.pl -- ./examples/11_variadic.c+p ./output/11_variadic.c 23 | 24 | echo "Compiling C..." 25 | gcc -Wall ./output/00_hello_world.c -g -o ./output/00_hello_world.out 26 | gcc -Wall ./output/01_functions.c -g -o ./output/01_functions.out 27 | gcc -Wall ./output/02_structs.c -g -o ./output/02_structs.out 28 | gcc -Wall ./output/03_parser.c -g -o ./output/03_parser.out 29 | gcc -Wall ./output/04_macros.c -g -o ./output/04_macros.out 30 | gcc -Wall ./output/05_generics.c -g -o ./output/05_generics.out 31 | gcc -Wall ./output/06_reflection.c -g -o ./output/06_reflection.out 32 | gcc -Wall ./output/07_directives.c -g -o ./output/07_directives.out 33 | gcc -Wall ./output/08a_headers.c -c -g -o ./output/08a_headers.o 34 | gcc -Wall ./output/08b_headers.c -c -g -o ./output/08b_headers.o 35 | gcc -Wall ./output/08a_headers.o ./output/08b_headers.o -g -o ./output/08_headers.out 36 | gcc -Wall ./output/09_switch.c -g -o ./output/09_switch.out 37 | gcc -Wall ./output/10_function_pointers.c -g -o ./output/10_function_pointers.out 38 | gcc -Wall ./output/11_variadic.c -g -o ./output/11_variadic.out 39 | 40 | echo "Running code..." 41 | ./output/00_hello_world.out 42 | ./output/01_functions.out 3 4 43 | ./output/02_structs.out 44 | ./output/03_parser.out 45 | ./output/04_macros.out 46 | ./output/05_generics.out 47 | ./output/06_reflection.out 48 | ./output/07_directives.out 49 | ./output/08_headers.out 50 | ./output/09_switch.out 51 | ./output/10_function_pointers.out 52 | ./output/11_variadic.out --------------------------------------------------------------------------------