72d84c7520cb8e2707c69d5ddc670d79a822ca11
[apps/madmutt.git] / lib-lua / luapkg2c.pl
1 #!/usr/bin/perl -w
2 #  This program is free software; you can redistribute it and/or modify
3 #  it under the terms of the GNU General Public License as published by
4 #  the Free Software Foundation; either version 2 of the License, or (at
5 #  your option) any later version.
6 #
7 #  This program is distributed in the hope that it will be useful, but
8 #  WITHOUT ANY WARRANTY; without even the implied warranty of
9 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
10 #  General Public License for more details.
11 #
12 #  You should have received a copy of the GNU General Public License
13 #  along with this program; if not, write to the Free Software
14 #  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
15 #  MA 02110-1301, USA.
16 #
17 #  Copyright © 2007 Pierre Habouzit
18
19 use strict;
20
21 my %types;
22 my %pkgs;
23
24 #{{{ stream functions
25
26 sub stream_open($) {
27     my $name = shift;
28     open FILE, $name;
29     return (file => $name, line => 0, f => \*FILE);
30 }
31
32 sub stream_close($) {
33     my $stream = shift;
34     close $stream->{f};
35 }
36
37 sub stream_getline($) {
38     my $stream = shift;
39     my $file   = $stream->{f};
40     while (<$file>) {
41         ${$stream}{line}++;
42         return $_;
43     }
44     return;
45 }
46
47 #}}}
48
49 #{{{ file:line functions
50
51 sub fatal($$) {
52     my ($stream, $msg) = @_;
53     printf STDERR "%s:%d: %s\n", $stream->{file}, $stream->{line}, $msg;
54     exit 1;
55 }
56
57 sub get_pos($$) {
58     my ($h,$k) = @_;
59     return $h->{$k}{file}.':'.$h->{$k}{line};
60 }
61
62 sub put_line($$) {
63     my ($src,$inc) = @_;
64     printf "#line %d \"%s\"\n", $src->{line} + $inc, $src->{file};
65 }
66
67 #}}}
68
69 #{{{ parsers
70
71 sub parse_type($$) {
72     my ($src, $type) = @_;
73     my %t = (
74         name => $type,
75         file => $src->{file},
76         line => $src->{line},
77     );
78
79     if (defined $types{$type}) {
80         my $pos = get_pos(\%types, $type);
81         fatal($src, "already defined type: `$type' at $pos");
82     }
83
84     while (stream_getline($src)) {
85         last if (/^\s*\}\s*;\s*/);
86         if (/^\s*\.(push|kind|ctype|check)\s*=\s*(.*?)\s*;\s*$/) {
87             $t{$1} = $2;
88             if ($1 eq "kind") {
89                 if ($2 =~ /^'([bis])'$/) {
90                     $t{kind} = $1;
91                 } else {
92                     fatal($src, "error: .kind should be [ibs] got $2)");
93                 }
94             }
95         } elsif (!/^\s*$/) {
96             fatal($src, "syntax error: unknown type directive");
97         }
98     }
99
100     for my $i ('kind', 'ctype') {
101         fatal($src, "incomplete type $type: missing .$i") unless defined $t{$i};
102     }
103     unless (defined $t{check}) {
104         if ($t{kind} eq 'b') {
105             $t{check} = '(luaL_checkint($L, $$) == 0)';
106         }
107
108         if ($t{kind} eq 'i') {
109             $t{check} = 'luaL_checkint($L, $$)';
110         }
111
112         if ($t{kind} eq 's') {
113             $t{check} = 'luaL_checkstring($L, $$)';
114         }
115     }
116
117     $t{ctypefmt} .= ' %s' unless (($t{ctypefmt} = $t{ctype}) =~ s/:/ \%s :/);
118     $t{ctype} =~ s/:.*//;
119
120     return $types{$type} = \%t;
121 }
122
123 sub parse_package($$) {
124     my ($src, $pkg) = @_;
125     my %p = (
126         name => $pkg,
127         file => $src->{file},
128         line => $src->{line},
129     );
130
131     if (defined $pkgs{$pkg}) {
132         my $pos = get_pos(\%pkgs, $pkg);
133         fatal($src, "already defined package: `$pkg' at $pos");
134     }
135
136     while (stream_getline($src)) {
137         if (/^\s*\}\s*(\w+)\s*;\s*$/) {
138             $p{cname} = $1;
139             last;
140         }
141
142         if (/^\s*(.*?)\s*=\s*(.*?)\s*;\s*$/) {
143             my ($lhs, $rhs) = ($1, $2);
144             my %m = ( file => $src->{file}, line => $src->{line} );
145
146             if ($lhs =~ /^((?:const\s+)?\w+?)\s*(\w+)$/) {
147                 $m{type} = $1;
148                 $m{name} = $2;
149                 $m{init} = $rhs;
150                 push @{$p{props}}, $2;
151             } elsif ($lhs =~ /^((?:const\s+)?\w+?)\s*(\w*)\((.*)\)$/) {
152                 $m{type}  = $1;
153                 $m{name}  = $2;
154                 $m{proto} = $3;
155                 $m{cfun}  = $rhs;
156                 push @{$p{meths}}, $2;
157             } else {
158                 fatal($src, "syntax error");
159             }
160
161             if (defined $p{members}{$m{name}}) {
162                 my $pos = get_pos($p{members}{$m{name}}, 0);
163                 fatal($src, "already defined package member: `$m{name}' at $pos");
164             }
165
166             $p{members}{$m{name}} = \%m;
167             next;
168         }
169
170         if (!/^\s*$/) {
171             fatal($src, "syntax error");
172         }
173     }
174
175     return $pkgs{$pkg} = \%p;
176 }
177
178 sub find_type($$) {
179     my ($ref, $typ) = @_;
180
181     if ($typ =~ /^const\s+(\w*)$/) {
182         fatal($ref, "undefined type `$1'") unless defined $types{$1};
183         return (const => 1, type => $types{$1});
184     }
185
186     fatal($ref, "undefined type `$typ'") unless defined $types{$typ};
187     return (const => 0, type => $types{$typ});
188 }
189
190 #}}}
191
192 #{{{ dump_fun
193
194 sub dump_fun($$) {
195     my ($pkg, $f) = @_;
196     my %t    = find_type($f, $f->{type});
197     my $call = $f->{cfun};
198     $call =~ s/\$/var/;
199
200     print "static int luaM_${pkg}_$f->{name}(lua_State *L) {\n";
201
202     if ($f->{proto} ne "void") {
203         my $i = 1;
204         for my $tmp (split /,/,$f->{proto}) {
205             s/^\s+//;
206             s/\s+$//;
207
208             my %pt = find_type($f, $tmp);
209             my $check = $pt{type}->{check};
210             $check =~ s/\$L/L/;
211             $check =~ s/\$\$/\%d/;
212
213             put_line($pt{type}, 0);
214             printf "    $pt{type}->{ctype} var$i = $check\n", $i++;
215         }
216     }
217
218     if (defined $t{type}->{push}) {
219         $call =~ s/\$\$/$t{type}->{push}/;
220     }
221
222     if ($t{type}->{kind} eq 'b') {
223         put_line($f, 0);
224         print "    lua_pushboolean(L, $call);\n";
225     }
226
227     if ($t{type}->{kind} eq 'i') {
228         put_line($f, 0);
229         print "    lua_pushint(L, $call);\n";
230     }
231
232     if ($t{type}->{kind} eq 's') {
233         if ($t{const}) {
234             put_line($f, 0);
235             print "    lua_pushstring(L, $call);\n";
236         } else {
237             print "    {\n";
238             put_line($f, 0);
239             print "        char *s = $call;\n";
240             print "        lua_pushstring(L, s);\n";
241             print "        p_delete(&s);\n";
242             print "    }\n";
243         }
244     }
245
246     printf "    return %d;\n", ($f->{type} ne "void");
247     print "}\n";
248 }
249
250 #}}}
251
252 #{{{ dump_struct
253
254 sub dump_struct_full($) {
255     my ($pkg) = @_;
256
257     put_line($pkg, 0);
258     print "struct luaM_$pkg->{name}_t $pkg->{cname} = {\n";
259
260     foreach (@{$pkg->{props}}) {
261         my $p = $pkg->{members}{$_};
262         my %t = find_type($p, $p->{type});
263
264         if ($t{const}) {
265             put_line($p, 0);
266             print "    $p->{init},\n";
267         } else {
268             print "    0,\n"    if ($t{type}->{kind} eq 'b');
269             print "    0,\n"    if ($t{type}->{kind} eq 'i');
270             print "    NULL,\n" if ($t{type}->{kind} eq 's');
271         }
272     }
273
274     print <<EOF;
275 };
276
277 static void $pkg->{cname}_init(void)
278 {
279 EOF
280
281     foreach (@{$pkg->{props}}) {
282         my $p   = $pkg->{members}{$_};
283         my %t   = find_type($p, $p->{type});
284         my $var = $pkg->{cname}.".".$p->{name};
285
286         next if $t{const};
287
288         put_line($p, 0);
289         print "        $var = $p->{init};\n"               if ($t{type}->{kind} eq 'b');
290         print "        $var = $p->{init};\n"               if ($t{type}->{kind} eq 'i');
291         print "        m_strreplace(&$var, $p->{init});\n" if ($t{type}->{kind} eq 's');
292     }
293     print "};\n";
294 };
295
296 sub dump_struct_short($) {
297     my ($pkg) = @_;
298
299     print "struct luaM_$pkg->{name}_t {\n";
300     foreach (@{$pkg->{props}}) {
301         my $p = $pkg->{members}{$_};
302         my %t = find_type($p, $p->{type});
303         printf "    $t{type}->{ctypefmt};\n", $p->{name};
304     }
305     put_line($pkg, 0);
306     print <<EOF;
307 };
308
309 extern struct luaM_$pkg->{name}_t $pkg->{cname};
310 EOF
311 }
312
313 #}}}
314
315 #{{{ dump package
316
317 sub dump_package_full($) {
318     my $pkg = shift;
319
320     dump_struct_full($pkg);
321     map { print "\n"; dump_fun($pkg->{name}, $pkg->{members}{$_}); } @{$pkg->{meths}};
322     print <<EOF;
323
324 static const luaL_reg luaM_$pkg->{name}_methods[] = {
325 EOF
326     map { print "    { \"$_\", luaM_$pkg->{name}_$_ },\n"; } @{$pkg->{meths}};
327     print <<EOF;
328     { NULL, NULL }
329 };
330
331 static int luaM_$pkg->{name}_index(lua_State *L)
332 {
333     const char *idx = luaL_checkstring(L, 2);
334
335     switch (mlua_which_token(idx, -1)) {
336 EOF
337
338     foreach (@{$pkg->{props}}) {
339         my $p = $pkg->{members}{$_};
340         my %t = find_type($p, $p->{type});
341         my $call = $pkg->{cname}.".".$p->{name};
342
343         my $tok = $p->{name};
344         $tok =~ tr/a-z/A-Z/;
345
346         if ($t{type}->{kind} eq 'b') {
347             $call = "lua_pushboolean(L, $call)";
348         }
349
350         if ($t{type}->{kind} eq 'i') {
351             $call = "lua_pushinteger(L, $call)";
352         }
353
354         if ($t{type}->{kind} eq 's') {
355             $call = "lua_pushstring(L, $call)";
356         }
357
358         if (defined $t{type}->{push}) {
359             $call =~ s/\$\$/$t{type}->{push}/;
360         }
361
362         put_line($p, 0);
363         print  "      case LTK_$tok:\n";
364         printf "        $call;\n";
365         print  "        return 1;\n";
366     }
367
368 print <<EOF;
369       default:
370         lua_rawget(L, lua_upvalueindex(2));       /* try methods       */
371         return 1;
372     }
373 }
374
375 static int luaM_$pkg->{name}_newindex(lua_State *L)
376 {
377     const char *idx = luaL_checkstring(L, 2);
378
379     switch (mlua_which_token(idx, -1)) {
380 EOF
381
382     foreach (@{$pkg->{props}}) {
383         my $p = $pkg->{members}{$_};
384         my %t = find_type($p, $p->{type});
385
386         next if ($t{const});
387
388         my $tok = $p->{name};
389         my $var = $pkg->{cname}.".".$p->{name};
390         my $check = $t{type}->{check};
391         $tok   =~ tr/a-z/A-Z/;
392         $check =~ s/\$L/L/;
393         $check =~ s/\$\$/3/;
394
395         put_line($p, 0);
396         print  "      case LTK_$tok: \n";
397         print "        $var = $check"               if ($t{type}->{kind} eq 'b');
398         print "        $var = $check"               if ($t{type}->{kind} eq 'i');
399         print "        m_strreplace(&$var, $check)" if ($t{type}->{kind} eq 's');
400
401         print  ";\n        return 1;\n";
402     }
403
404 print <<EOF;
405       default:
406         return 1;
407     }
408 }
409
410 int luaopen_$pkg->{name}(lua_State *L)
411 {
412     int mt, methods;
413
414     $pkg->{cname}_init();
415
416     /* create methods table, add it the the table of globals */
417     luaL_openlib(L, "$pkg->{name}", luaM_$pkg->{name}_methods, 0);
418     methods = lua_gettop(L);
419
420     /* create metatable for $pkg->{name}, add it to the registry */
421     luaL_newmetatable(L, "$pkg->{name}");
422     mt = lua_gettop(L);
423
424     lua_pushliteral(L, "__index");
425     lua_pushvalue(L, mt);                       /* upvalue 1         */
426     lua_pushvalue(L, methods);                  /* upvalue 2         */
427     lua_pushcclosure(L, &luaM_$pkg->{name}_index, 2);
428     lua_rawset(L, mt);                          /* set mt.__index    */
429
430     lua_pushliteral(L, "__newindex");
431     lua_newtable(L);                            /* for new members   */
432     lua_pushcclosure(L, &luaM_$pkg->{name}_newindex, 1);
433     lua_rawset(L, mt);                          /* set mt.__newindex */
434
435     lua_pushliteral(L, "__metatable");
436     lua_pushvalue(L, methods);                  /* dup methods table */
437     lua_rawset(L, mt);                          /* hide metatable    */
438
439     lua_setmetatable(L, methods);
440
441     lua_pop(L, 1);                              /* drop mt           */
442     return 1;                                   /* return methods    */
443 }
444
445 EOF
446 }
447
448 sub dump_package_short($) {
449     my $pkg = shift;
450     my $upp = $pkg->{name};
451     $upp =~ tr/a-z/A-Z/;
452
453     print <<EOF;
454 #ifndef MUTT_LUA_${upp}_H
455 #define MUTT_LUA_${upp}_H
456
457 EOF
458     dump_struct_short($pkg);
459     print <<EOF
460
461 int luaopen_$pkg->{name}(lua_State *L);
462
463 #endif /* MUTT_LUA_${upp}_H */
464 EOF
465 }
466
467 #}}}
468
469 sub do_c($) {
470     my %src = stream_open(shift);
471     my $resync = 1;
472
473     while (stream_getline(\%src)) {
474         if (/^\s*\@type\s+([a-zA-Z]\w*)\s*=\s*{\s*$/) {
475             parse_type(\%src, $1);
476             $resync = 1;
477         } elsif (/^\s*\@package\s+([a-zA-Z]\w*)\s+{\s*$/) {
478             dump_package_full(parse_package(\%src, $1));
479             $resync = 1;
480         } elsif (/^\s*(\@\w*)/) {
481             fatal(\%src, "syntax error: unknown directive `$1'");
482         } else {
483             next if ($resync && /^\s+$/);
484             if ($resync) {
485                 put_line(\%src, 0);
486                 $resync = 0;
487             }
488             print;
489         }
490     }
491
492     stream_close(\%src);
493 }
494
495 sub do_h($) {
496     my %src = stream_open(shift);
497     my $resync = 1;
498
499     while (stream_getline(\%src)) {
500         if (/^\s*\@type\s+([a-zA-Z]\w*)\s*=\s*{\s*$/) {
501             parse_type(\%src, $1);
502         } elsif (/^\s*\@package\s+([a-zA-Z]\w*)\s+{\s*$/) {
503             dump_package_short(parse_package(\%src, $1));
504         } elsif (/^\s*(\@\w*)/) {
505             fatal(\%src, "syntax error: unknown directive `$1'");
506         }
507     }
508
509     stream_close(\%src);
510 }
511
512 if ($#ARGV < 1 || ($ARGV[0] ne "-h" && $ARGV[0] ne "-c")) {
513     print <<EOF;
514 usage: mluapkg (-h | -c) file.pkg
515 EOF
516     exit 1;
517 }
518
519 print <<EOF;
520 /*****     THIS FILE IS AUTOGENERATED DO NOT MODIFY DIRECTLY !    *****/
521 EOF
522
523 map { do_h($ARGV[$_]); } (1 .. $#ARGV) if ($ARGV[0] eq '-h');
524 map { do_c($ARGV[$_]); } (1 .. $#ARGV) if ($ARGV[0] eq '-c');