make 'method main' work
[p5sagit/Devel-Declare.git] / Declare.xs
1 #define PERL_CORE
2 #define PERL_NO_GET_CONTEXT
3 #include "EXTERN.h"
4 #include "perl.h"
5 #include "XSUB.h"
6 #undef printf
7 #include "stolen_chunk_of_toke.c"
8 #include <stdio.h>
9 #include <string.h>
10
11 #ifndef Newx
12 # define Newx(v,n,t) New(0,v,n,t)
13 #endif /* !Newx */
14
15 #if 1
16 #define DD_HAS_TRAITS
17 #endif
18
19 #if 0
20 #define DD_DEBUG
21 #endif
22
23 #define DD_HANDLE_NAME 1
24 #define DD_HANDLE_PROTO 2
25 #define DD_HANDLE_PACKAGE 8
26
27 #ifdef DD_DEBUG
28 #define DD_DEBUG_S printf("Buffer: %s\n", s);
29 #else
30 #define DD_DEBUG_S
31 #endif
32
33 #define LEX_NORMAL    10
34 #define LEX_INTERPNORMAL   9
35
36 /* flag to trigger removal of temporary declaree sub */
37
38 static int in_declare = 0;
39
40 /* replacement PL_check rv2cv entry */
41
42 STATIC OP *(*dd_old_ck_rv2cv)(pTHX_ OP *op);
43
44 STATIC OP *dd_ck_rv2cv(pTHX_ OP *o) {
45   OP* kid;
46   char* s;
47   char* save_s;
48   char tmpbuf[sizeof PL_tokenbuf];
49   char found_name[sizeof PL_tokenbuf];
50   char* found_proto = NULL, *found_traits = NULL;
51   STRLEN len = 0;
52   HV *stash;
53   HV* is_declarator;
54   SV** is_declarator_pack_ref;
55   HV* is_declarator_pack_hash;
56   SV** is_declarator_flag_ref;
57   int dd_flags;
58   char* cb_args[6];
59   dSP; /* define stack pointer for later call stuff */
60   char* retstr;
61   STRLEN n_a; /* for POPpx */
62
63   o = dd_old_ck_rv2cv(aTHX_ o); /* let the original do its job */
64
65   if (in_declare) {
66     cb_args[0] = NULL;
67 #ifdef DD_DEBUG
68     printf("Deconstructing declare\n");
69     printf("PL_bufptr: %s\n", PL_bufptr);
70     printf("bufend at: %i\n", PL_bufend - PL_bufptr);
71     printf("linestr: %s\n", SvPVX(PL_linestr));
72     printf("linestr len: %i\n", PL_bufend - SvPVX(PL_linestr));
73 #endif
74     call_argv("Devel::Declare::done_declare", G_VOID|G_DISCARD, cb_args);
75     in_declare--;
76 #ifdef DD_DEBUG
77     printf("PL_bufptr: %s\n", PL_bufptr);
78     printf("bufend at: %i\n", PL_bufend - PL_bufptr);
79     printf("linestr: %s\n", SvPVX(PL_linestr));
80     printf("linestr len: %i\n", PL_bufend - SvPVX(PL_linestr));
81     printf("actual len: %i\n", strlen(PL_bufptr));
82 #endif
83     return o;
84   }
85
86   kid = cUNOPo->op_first;
87
88   if (kid->op_type != OP_GV) /* not a GV so ignore */
89     return o;
90
91   if (PL_lex_state != LEX_NORMAL && PL_lex_state != LEX_INTERPNORMAL)
92     return o; /* not lexing? */
93
94   stash = GvSTASH(kGVOP_gv);
95
96 #ifdef DD_DEBUG
97   printf("Checking GV %s -> %s\n", HvNAME(stash), GvNAME(kGVOP_gv));
98 #endif
99
100   is_declarator = get_hv("Devel::Declare::declarators", FALSE);
101
102   if (!is_declarator)
103     return o;
104
105   is_declarator_pack_ref = hv_fetch(is_declarator, HvNAME(stash),
106                              strlen(HvNAME(stash)), FALSE);
107
108   if (!is_declarator_pack_ref || !SvROK(*is_declarator_pack_ref))
109     return o; /* not a hashref */
110
111   is_declarator_pack_hash = (HV*) SvRV(*is_declarator_pack_ref);
112
113   is_declarator_flag_ref = hv_fetch(is_declarator_pack_hash, GvNAME(kGVOP_gv),
114                                 strlen(GvNAME(kGVOP_gv)), FALSE);
115
116   /* requires SvIOK as well as TRUE since flags not being an int is useless */
117
118   if (!is_declarator_flag_ref
119         || !SvIOK(*is_declarator_flag_ref) 
120         || !SvTRUE(*is_declarator_flag_ref))
121     return o;
122
123   dd_flags = SvIVX(*is_declarator_flag_ref);
124
125 #ifdef DD_DEBUG
126   printf("dd_flags are: %i\n", dd_flags);
127 #endif
128
129   s = PL_bufptr; /* copy the current buffer pointer */
130
131   DD_DEBUG_S
132
133 #ifdef DD_DEBUG
134   printf("PL_tokenbuf: %s\n", PL_tokenbuf);
135 #endif
136
137   /*
138    *   buffer will be at the beginning of the declarator, -unless- the
139    *   declarator is at EOL in which case it'll be the next useful line
140    *   so we don't short-circuit out if we don't find the declarator
141    */
142
143   while (s < PL_bufend && isSPACE(*s)) s++;
144   if (memEQ(s, PL_tokenbuf, strlen(PL_tokenbuf)))
145     s += strlen(PL_tokenbuf);
146
147   DD_DEBUG_S
148
149   if (dd_flags & DD_HANDLE_NAME) {
150
151     /* find next word */
152
153     s = skipspace(s);
154
155     DD_DEBUG_S
156
157     /* kill the :: added in the ck_const */
158     if (*s == ':')
159       *s++ = ' ';
160     if (*s == ':')
161       *s++ = ' ';
162
163     /* arg 4 is allow_package */
164
165     s = scan_word(s, tmpbuf, sizeof tmpbuf, dd_flags & DD_HANDLE_PACKAGE, &len);
166
167     DD_DEBUG_S
168
169     if (len) {
170       strcpy(found_name, tmpbuf);
171 #ifdef DD_DEBUG
172       printf("Found %s\n", found_name);
173 #endif
174     }
175   }
176
177   if (dd_flags & DD_HANDLE_PROTO) {
178
179     s = skipspace(s);
180
181     if (*s == '(') { /* found a prototype-ish thing */
182       save_s = s;
183       s = scan_str(s, FALSE, FALSE); /* no keep_quoted, no keep_delims */
184 #ifdef DD_HAS_TRAITS
185       {
186           char *traitstart = s = skipspace(s);
187
188           while (*s && *s != '{') ++s;
189           if (*s) {
190               int tlen = s - traitstart;
191               Newx(found_traits, tlen+1, char);
192               Copy(traitstart, found_traits, tlen, char);
193               found_traits[tlen] = 0;
194 #ifdef DD_DEBUG
195               printf("found traits..... (%s)\n", found_traits);
196 #endif
197           }
198       }
199 #endif
200       
201       if (SvPOK(PL_lex_stuff)) {
202 #ifdef DD_DEBUG
203         printf("Found proto %s\n", SvPVX(PL_lex_stuff));
204 #endif
205         found_proto = SvPVX(PL_lex_stuff);
206         if (len) /* foo name () => foo name  X, only foo parsed so works */
207           *save_s++ = ' ';
208         else /* foo () => foo =X, TOKEN('&') won't handle foo X */
209           *save_s++ = '=';
210         *save_s++ = 'X';
211         while (save_s < s) {
212           *save_s++ = ' ';
213         }
214 #ifdef DD_DEBUG
215         printf("Curbuf %s\n", PL_bufptr);
216 #endif
217       }
218     }
219   }
220
221   if (!len)
222     found_name[0] = 0;
223
224 #ifdef DD_DEBUG
225   printf("Calling init_declare\n");
226 #endif
227   cb_args[0] = HvNAME(stash);
228   cb_args[1] = GvNAME(kGVOP_gv);
229   cb_args[2] = HvNAME(PL_curstash);
230   cb_args[3] = found_name;
231   cb_args[4] = found_proto;
232   cb_args[5] = found_traits;
233   cb_args[6] = NULL;
234
235   if (len && found_proto)
236     in_declare = 2;
237   else if (len || found_proto)
238     in_declare = 1;
239   if (found_proto)
240     PL_lex_stuff = Nullsv;
241   s = skipspace(s);
242 #ifdef DD_DEBUG
243   printf("cur buf: %s\n", s);
244   printf("bufend at: %i\n", PL_bufend - s);
245   printf("linestr: %s\n", SvPVX(PL_linestr));
246   printf("linestr len: %i\n", PL_bufend - SvPVX(PL_linestr));
247 #endif
248   if (*s++ == '{') {
249     call_argv("Devel::Declare::init_declare", G_SCALAR, cb_args);
250     SPAGAIN;
251     retstr = POPpx;
252     PUTBACK;
253     if (retstr && strlen(retstr)) {
254       const char* old_start = SvPVX(PL_linestr);
255       int start_diff;
256       const int old_len = SvCUR(PL_linestr);
257 #ifdef DD_DEBUG
258       printf("Got string %s\n", retstr);
259 #endif
260       SvGROW(PL_linestr, (STRLEN)(old_len + strlen(retstr)));
261       if (start_diff = SvPVX(PL_linestr) - old_start) {
262         Perl_croak(aTHX_ "forced to realloc PL_linestr for line %s, bailing out before we crash harder", SvPVX(PL_linestr));
263       }
264       memmove(s+strlen(retstr), s, (PL_bufend - s)+1);
265       memmove(s, retstr, strlen(retstr));
266       SvCUR_set(PL_linestr, old_len + strlen(retstr));
267       PL_bufend += strlen(retstr);
268 #ifdef DD_DEBUG
269   printf("cur buf: %s\n", s);
270   printf("PL_bufptr: %s\n", PL_bufptr);
271   printf("bufend at: %i\n", PL_bufend - s);
272   printf("linestr: %s\n", SvPVX(PL_linestr));
273   printf("linestr len: %i\n", PL_bufend - SvPVX(PL_linestr));
274   printf("tokenbuf now: %s\n", PL_tokenbuf);
275 #endif
276     }
277   } else {
278     call_argv("Devel::Declare::init_declare", G_VOID|G_DISCARD, cb_args);
279   }
280   return o;
281 }
282
283 STATIC OP *(*dd_old_ck_entereval)(pTHX_ OP *op);
284
285 OP* dd_pp_entereval(pTHX) {
286   dSP;
287   dPOPss;
288   STRLEN len;
289   const char* s;
290   if (SvPOK(sv)) {
291 #ifdef DD_DEBUG
292     printf("mangling eval sv\n");
293 #endif
294     if (SvREADONLY(sv))
295       sv = sv_2mortal(newSVsv(sv));
296     s = SvPVX(sv);
297     len = SvCUR(sv);
298     if (!len || s[len-1] != ';') {
299       if (!(SvFLAGS(sv) & SVs_TEMP))
300         sv = sv_2mortal(newSVsv(sv));
301       sv_catpvn(sv, "\n;", 2);
302     }
303     SvGROW(sv, 8192);
304   }
305   PUSHs(sv);
306   return PL_ppaddr[OP_ENTEREVAL](aTHX);
307 }
308
309 STATIC OP *dd_ck_entereval(pTHX_ OP *o) {
310   o = dd_old_ck_entereval(aTHX_ o); /* let the original do its job */
311   if (o->op_ppaddr == PL_ppaddr[OP_ENTEREVAL])
312     o->op_ppaddr = dd_pp_entereval;
313   return o;
314 }
315
316 static I32 dd_filter_realloc(pTHX_ int idx, SV *sv, int maxlen)
317 {
318   const I32 count = FILTER_READ(idx+1, sv, maxlen);
319   SvGROW(sv, 8192); /* please try not to have a line longer than this :) */
320   /* filter_del(dd_filter_realloc); */
321   return count;
322 }
323
324 STATIC OP *(*dd_old_ck_const)(pTHX_ OP*op);
325
326 STATIC OP *dd_ck_const(pTHX_ OP *o) {
327   HV* is_declarator;
328   SV** is_declarator_pack_ref;
329   HV* is_declarator_pack_hash;
330   SV** is_declarator_flag_ref;
331   int dd_flags;
332   char* s;
333   char tmpbuf[sizeof PL_tokenbuf];
334   char found_name[sizeof PL_tokenbuf];
335   STRLEN len = 0;
336
337   o = dd_old_ck_const(aTHX_ o); /* let the original do its job */
338
339   is_declarator = get_hv("Devel::Declare::declarators", FALSE);
340
341   is_declarator_pack_ref = hv_fetch(is_declarator, HvNAME(PL_curstash),
342                              strlen(HvNAME(PL_curstash)), FALSE);
343
344   if (!is_declarator_pack_ref || !SvROK(*is_declarator_pack_ref))
345     return o; /* not a hashref */
346
347   is_declarator_pack_hash = (HV*) SvRV(*is_declarator_pack_ref);
348
349   /* don't try and look this up if it's not a string const */
350   if (!SvPOK(cSVOPo->op_sv))
351     return o;
352
353   is_declarator_flag_ref = hv_fetch(
354     is_declarator_pack_hash, SvPVX(cSVOPo->op_sv),
355     strlen(SvPVX(cSVOPo->op_sv)), FALSE
356   );
357
358   /* requires SvIOK as well as TRUE since flags not being an int is useless */
359
360   if (!is_declarator_flag_ref
361         || !SvIOK(*is_declarator_flag_ref) 
362         || !SvTRUE(*is_declarator_flag_ref))
363     return o;
364
365   dd_flags = SvIVX(*is_declarator_flag_ref);
366
367   if (!(dd_flags & DD_HANDLE_NAME))
368     return o; /* if we're not handling name, method intuiting not an issue */
369
370 #ifdef DD_DEBUG
371   printf("Think I found a declarator %s\n", PL_tokenbuf);
372   printf("linestr: %s\n", SvPVX(PL_linestr));
373 #endif
374
375   s = PL_bufptr;
376
377   while (s < PL_bufend && isSPACE(*s)) s++;
378   if (memEQ(s, PL_tokenbuf, strlen(PL_tokenbuf)))
379     s += strlen(PL_tokenbuf);
380
381   DD_DEBUG_S
382
383   /* find next word */
384
385   s = skipspace(s);
386
387   DD_DEBUG_S
388
389   /* arg 4 is allow_package */
390
391   s = scan_word(s, tmpbuf, sizeof tmpbuf, dd_flags & DD_HANDLE_PACKAGE, &len);
392
393   DD_DEBUG_S
394
395   if (len) {
396     const char* old_start = SvPVX(PL_linestr);
397     int start_diff;
398     const int old_len = SvCUR(PL_linestr);
399
400     strcpy(found_name, tmpbuf);
401 #ifdef DD_DEBUG
402     printf("Found %s\n", found_name);
403 #endif
404
405     s -= len;
406     SvGROW(PL_linestr, (STRLEN)(old_len + 2));
407     if (start_diff = SvPVX(PL_linestr) - old_start) {
408       Perl_croak(aTHX_ "forced to realloc PL_linestr for line %s, bailing out before we crash harder", SvPVX(PL_linestr));
409     }
410     memmove(s+2, s, (PL_bufend - s)+1);
411     *s = ':';
412     s++;
413     *s = ':';
414     SvCUR_set(PL_linestr, old_len + 2);
415     PL_bufend += 2;
416   }
417   return o;  
418 }
419
420 static int initialized = 0;
421
422 MODULE = Devel::Declare  PACKAGE = Devel::Declare
423
424 PROTOTYPES: DISABLE
425
426 void
427 setup()
428   CODE:
429   if (!initialized++) {
430     dd_old_ck_rv2cv = PL_check[OP_RV2CV];
431     PL_check[OP_RV2CV] = dd_ck_rv2cv;
432     dd_old_ck_entereval = PL_check[OP_ENTEREVAL];
433     PL_check[OP_ENTEREVAL] = dd_ck_entereval;
434     dd_old_ck_const = PL_check[OP_CONST];
435     PL_check[OP_CONST] = dd_ck_const;
436   }
437   filter_add(dd_filter_realloc, NULL);