├── .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
--------------------------------------------------------------------------------