migrate substitution keywords to SVN
[reactos.git] / reactos / lib / kjs / jswrap / process.js
1 /*
2 * Process the jswrap input files and generate output.
3 * Copyright (c) 1998 New Generation Software (NGS) Oy
4 *
5 * Author: Markku Rossi <mtr@ngs.fi>
6 */
7
8 /*
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
18 *
19 * You should have received a copy of the GNU Library General Public
20 * License along with this library; if not, write to the Free
21 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
22 * MA 02111-1307, USA
23 */
24
25 /*
26 * $Source: /cygdrive/c/RCVS/CVS/ReactOS/reactos/lib/kjs/jswrap/process.js,v $
27 * $Id$
28 */
29
30 /*
31 * Constants and definitions.
32 */
33
34 /* Tokens. */
35
36 tEOF = 1000;
37
38 tFUNCTION = 1001;
39 tIDENTIFIER = 1002;
40
41 tCSTRING = 2001;
42 tDOUBLE = 2002;
43 tINT = 2003;
44 tSTRING = 2004;
45 tVOID = 2005;
46
47 tIN = 2010;
48 tOUT = 2011;
49 tINOUT = 2012;
50
51 tSTATIC = 2020;
52
53 function is_type_token (token)
54 {
55 return (token == tCSTRING || token == tDOUBLE || token == tINT
56 || token == tSTRING || token == tVOID);
57 }
58
59 function is_copy_type_token (token)
60 {
61 return (token == tIN || token == tOUT || token == tINOUT);
62 }
63
64 function typename (token)
65 {
66 if (token == tCSTRING)
67 return "cstring";
68 else if (token == tDOUBLE)
69 return "double";
70 else if (token == tINT)
71 return "int";
72 else if (token == tSTRING)
73 return "string";
74 else if (token == tVOID)
75 return "void";
76 else if (token == tIN)
77 return "in";
78 else if (token == tOUT)
79 return "out";
80 else if (token == tINOUT)
81 return "inout";
82 else
83 return "";
84 }
85
86 function c_typename (token)
87 {
88 if (token == tCSTRING)
89 return "char *";
90 else if (token == tDOUBLE)
91 return "double ";
92 else if (token == tINT)
93 return "long ";
94 else if (token == tVOID)
95 return "void ";
96 else if (token == tOUT || token == tINOUT)
97 return "*";
98 else
99 return "";
100 }
101
102 function jsint_typename (token)
103 {
104 if (token == tCSTRING)
105 return "JS_STRING";
106 else if (token == tDOUBLE)
107 return "JS_FLOAT";
108 else if (token == tINT)
109 return "JS_INTEGER";
110 else if (token == tSTRING)
111 return "JS_STRING";
112 else
113 return "";
114 }
115
116 valid_c_identifier_re = new RegExp ("^[A-Za-z_][A-Za-z_0-9]*$");
117
118 end_brace_re = new RegExp ("^}[ \t]*$");
119
120 /*
121 * Variables.
122 */
123
124 /* The input file line number. */
125 linenum = 1;
126
127 token_linenum = 1;
128
129 token_value = null;
130
131 /*
132 * Functions.
133 */
134
135 function process ()
136 {
137 if (false)
138 System.print (program, ": processing file `", input_file,
139 "', header=`", header_file,
140 "', output=`", output_file,
141 "', reentrant=", opt_reentrant, "\n");
142
143 linenum = 1;
144
145 /* Open input file. */
146 ifp = new File (input_file);
147 if (!ifp.open ("r"))
148 {
149 System.error (program, ": couldn't open input file `", input_file,
150 "': ", System.strerror (System.errno), "\n");
151 System.exit (1);
152 }
153
154 /* Create output files. */
155
156 ofp_c = new File (output_file);
157 if (!ofp_c.open ("w"))
158 {
159 System.error (program, ": couldn't create output file `", output_file,
160 "': ", System.strerror (System.errno), "\n");
161 System.exit (1);
162 }
163
164 ofp_h = new File (header_file);
165 if (!ofp_h.open ("w"))
166 {
167 ofp_c.close ();
168 File.remove (output_file);
169
170 System.error (program, ": couldn't create header file `", header_file,
171 "': ", System.strerror (System.errno), "\n");
172 System.exit (1);
173 }
174
175 if (false)
176 {
177 ofp_js = new File (js_file);
178 if (!ofp_js.open ("w"))
179 {
180 ofp_c.close ();
181 ofp_h.close ();
182 File.remove (output_file);
183 File.remove (header_file);
184
185 System.error (program, ": couldn't create JS file `", js_file,
186 "': ", System.strerror (System.errno), "\n");
187 System.exit (1);
188 }
189 }
190
191 /* Parse the input. */
192 parse ();
193
194 /* Cleanup. */
195
196 ofp_c.close ();
197 ofp_h.close ();
198 }
199
200 \f
201 /*
202 * The Func class to hold function definitions.
203 */
204
205 function Func ()
206 {
207 }
208
209 new Func ();
210
211 function Func$generate_js ()
212 {
213 this.code = new String ("function " + this.name + " (");
214 var i;
215 for (i = 0; i < this.args.length; i++)
216 {
217 this.code.append (this.args[i].name);
218 if (i + 1 < this.args.length)
219 this.code.append (", ");
220 }
221 this.code.append (")\n{");
222 this.code.append (this.body);
223 this.code.append ("}\n");
224 }
225 Func.prototype.generate_js = Func$generate_js;
226
227 function Func$print_js (stream)
228 {
229 stream.write (this.code);
230 }
231 Func.prototype.print_js = Func$print_js;
232
233 function Func$print_h (stream, header)
234 {
235 stream.write ("\n");
236 stream.write (c_typename (this.return_type) + this.name + " (");
237
238 if (opt_reentrant)
239 {
240 /* The first argument is the interpreter handle. */
241 stream.write ("\n\tJSInterpPtr interp");
242 if (this.args.length > 0)
243 stream.write (",");
244 }
245
246 var i;
247 for (i = 0; i < this.args.length; i++)
248 {
249 stream.write ("\n\t");
250 if (this.args[i].type == tSTRING)
251 {
252 stream.write ("unsigned char *"
253 + c_typename (this.args[i].copy_type)
254 + this.args[i].name + ",\n\t"
255 + "unsigned int "
256 + c_typename (this.args[i].copy_type)
257 + this.args[i].name + "_len");
258 }
259 else
260 {
261 stream.write (c_typename (this.args[i].type)
262 + c_typename (this.args[i].copy_type));
263 stream.write (this.args[i].name);
264 }
265
266 if (i + 1 < this.args.length)
267 stream.write (", ");
268 }
269 stream.write ("\n\t)");
270
271 if (header)
272 stream.write (";");
273
274 stream.write ("\n");
275 }
276 Func.prototype.print_h = Func$print_h;
277
278 function Func$print_c (stream)
279 {
280 this.compile ();
281 this.dump_bc (stream);
282
283 this.print_h (stream, false);
284
285 stream.write ("{\n");
286
287 if (!opt_reentrant)
288 stream.write (" JSInterpPtr interp = jswrap_interp;\n");
289
290 stream.write (" JSVirtualMachine *vm = interp->vm;\n");
291
292 stream.write (" JSNode argv["
293 + (this.args.length + 1).toString ()
294 + "];\n");
295 stream.write (" int result;\n");
296
297 if (this.return_type == tCSTRING)
298 stream.write (" char *cp;\n");
299
300 stream.write ("\n");
301
302 /* Argument count. */
303 stream.write (" argv[0].type = JS_INTEGER;\n");
304 stream.write (" argv[0].u.vinteger = "
305 + this.args.length.toString ()
306 + ";\n");
307
308 /* Arguments. */
309
310 /* Construct the argv array. */
311 for (i = 0; i < this.args.length; i++)
312 {
313 var arg = this.args[i];
314 var cspec = "";
315
316 stream.write ("\n");
317
318 var argnum = (i + 1).toString ();
319
320 if (arg.copy_type == tOUT)
321 {
322 stream.write (" argv[" + argnum + "].type = JS_UNDEFINED;\n");
323 }
324 else
325 {
326 if (arg.copy_type == tINOUT)
327 cspec = "*";
328
329 if (arg.type == tCSTRING)
330 {
331 stream.write (" js_vm_make"
332 + (arg.staticp ? "_static" : "")
333 + "_string (vm, &argv["
334 + argnum + "], "
335 + cspec + arg.name + ", strlen ("
336 + cspec + arg.name + "));\n");
337 }
338 else if (arg.type == tDOUBLE)
339 {
340 stream.write (" argv[" + argnum + "].type = JS_FLOAT;\n");
341 stream.write (" argv[" + argnum + "].u.vfloat = "
342 + cspec + arg.name + ";\n");
343 }
344 else if (arg.type == tINT)
345 {
346 stream.write (" argv[" + argnum + "].type = JS_INTEGER;\n");
347 stream.write (" argv[" + argnum + "].u.vinteger = "
348 + cspec + arg.name + ";\n");
349 }
350 else if (arg.type == tSTRING)
351 {
352 stream.write (" js_vm_make_static_string (vm, &argv["
353 + argnum + "], "
354 + cspec + arg.name + ", "
355 + cspec + arg.name + "_len);\n");
356 }
357 else
358 VM.stackTrace ();
359 }
360 }
361
362 /* Call the function. */
363 stream.write ("\
364
365 retry:
366
367 result = js_vm_apply (vm, \"" + this.name + "\", NULL, "
368 + (i + 1).toString () + ", argv);
369 if (result == 0)
370 {
371 JSNode *n = &vm->globals[js_vm_intern (vm, \"" + this.name + "\")];
372
373 if (n->type != JS_FUNC)
374 {
375 JSByteCode *bc = js_bc_read_data (" + this.name + "_bc,
376 sizeof (" + this.name + "_bc));
377 result = js_vm_execute (vm, bc);
378 js_bc_free (bc);
379
380 if (result == 0)
381 jswrap_error (interp, \"initialization of function`"
382 + this.name + "' failed\");
383 goto retry;
384 }
385 jswrap_error (interp, \"execution of function `"
386 + this.name + "' failed\");
387 }
388 ");
389
390 /* Handle out and inout parameters. */
391 for (i = 0; i < this.args.length; i++)
392 {
393 var arg = this.args[i];
394 if (arg.copy_type == tIN)
395 continue;
396
397 var spos = (this.args.length - i).toString ();
398
399 /* Check that we have there a correct type. */
400 stream.write ("
401 if ((vm->sp - " + spos + ")->type != " + jsint_typename (arg.type) + ")
402 jswrap_error (interp,
403 \"wrong return type for argument `"
404 + arg.name + "' of function `" + this.name + "'\");
405 ");
406
407 /* Handle the different types. */
408 if (arg.type == tCSTRING)
409 {
410 stream.write ("\
411 *" + arg.name + " = (char *) js_vm_alloc (vm, (vm->sp - "
412 + spos + ")->u.vstring->len + 1);
413 memcpy (*" + arg.name + ", (vm->sp - " + spos + ")->u.vstring->data,
414 (vm->sp - " + spos + ")->u.vstring->len);
415 " + arg.name + "[(vm->sp - " + spos + ")->u.vstring->len] = '\\0';
416 ");
417 }
418 else if (arg.type == tDOUBLE)
419 {
420 stream.write ("\
421 *" + arg.name + " = (vm->sp - " + spos + ")->u.vfloat;\n");
422 }
423 else if (arg.type == tINT)
424 {
425 stream.write ("\
426 *" + arg.name + " = (vm->sp - " + spos + ")->u.vinteger;\n");
427 }
428 else if (arg.type == tSTRING)
429 {
430 stream.write ("\
431 *" + arg.name + " = (vm->sp - " + spos + ")->u.vstring->data;
432 *" + arg.name + "_len = (vm->sp - " + spos + ")->u.vstring->len;
433 ");
434 }
435 }
436
437 /* Handle the function return value. */
438 if (this.return_type != tVOID)
439 {
440 /* Check that the code returned correct type. */
441 stream.write ("
442 if (vm->exec_result.type != " + jsint_typename (this.return_type) + ")
443 jswrap_error (interp, \"return type mismatch in function `"
444 + this.name + "'\");
445
446 ");
447
448 /* Handle the different types. */
449 if (this.return_type == tCSTRING)
450 {
451 stream.write ("\
452 cp = (char *) js_vm_alloc (vm, vm->exec_result.u.vstring->len + 1);
453 memcpy (cp, vm->exec_result.u.vstring->data, vm->exec_result.u.vstring->len);
454 cp[vm->exec_result.u.vstring->len] = '\\0';
455
456 return cp;
457 ");
458 }
459 else if (this.return_type == tDOUBLE)
460 stream.write (" return vm->exec_result.u.vfloat;\n");
461 else if (this.return_type == tINT)
462 stream.write (" return vm->exec_result.u.vinteger;\n");
463 }
464
465 stream.write ("}\n");
466 }
467 Func.prototype.print_c = Func$print_c;
468
469 function Func$compile ()
470 {
471 /* Create the byte-code for this function. */
472
473 var flags = 0;
474
475 if (false)
476 flags |= JSC$FLAG_VERBOSE;
477
478 if (opt_debug)
479 flags |= JSC$FLAG_GENERATE_DEBUG_INFO;
480
481 flags |= (JSC$FLAG_OPTIMIZE_PEEPHOLE
482 | JSC$FLAG_OPTIMIZE_JUMPS
483 | JSC$FLAG_OPTIMIZE_BC_SIZE);
484
485 flags |= JSC$FLAG_WARN_MASK;
486
487 try
488 {
489 this.bc = JSC$compile_string (this.code, flags, null, null);
490 }
491 catch (error)
492 {
493 System.error (error, "\n");
494 System.exit (1);
495 }
496 }
497 Func.prototype.compile = Func$compile;
498
499 function Func$dump_bc (stream)
500 {
501 stream.write ("\nstatic unsigned char " + this.name + "_bc[] = {");
502
503 var i;
504 for (i = 0; i < this.bc.length; i++)
505 {
506 if ((i % 12) == 0)
507 stream.write ("\n ");
508
509 var item = this.bc[i].toString (16);
510 if (item.length == 1)
511 item = "0" + item;
512
513 stream.write (" 0x" + item + ",");
514 }
515
516 stream.write ("\n};\n");
517 }
518 Func.prototype.dump_bc = Func$dump_bc;
519
520 /*
521 * The Argument class to hold function argument definitions.
522 */
523
524 function Argument ()
525 {
526 this.type = false;
527 this.copy_type = tIN;
528 this.staticp = false;
529 }
530
531 new Argument ();
532
533 function Argument$print ()
534 {
535 System.print (typename (this.copy_type), " ", typename (this.type),
536 " ", this.name);
537 }
538 Argument.prototype.print = Argument$print;
539
540
541 \f
542 /*
543 * The .jsw file parsing.
544 */
545
546 function parse ()
547 {
548 var token;
549 var func;
550
551 headers ();
552
553 while ((token = get_token ()) != tEOF)
554 {
555 if (token != tFUNCTION)
556 syntax_error ();
557
558 func = new Func ();
559
560 /* Possible return type. */
561 token = get_token ();
562 if (is_type_token (token))
563 {
564 if (token == tSTRING)
565 {
566 System.error (input_file, ":", linenum,
567 ": the function return value can't be `string'\n");
568 System.exit (1);
569 }
570
571 func.return_type = token;
572
573 token = get_token ();
574 }
575 else
576 func.return_type = tVOID;
577
578 /* The name of the function. */
579 if (token != tIDENTIFIER)
580 syntax_error ();
581
582 if (!valid_c_identifier_re.test (token_value))
583 {
584 System.error (input_file, ":", linenum,
585 ": function name is not a valid C identifier `",
586 token_value, "'\n");
587 System.exit (1);
588 }
589
590 func.name = token_value;
591
592 /* Arguments. */
593 token = get_token ();
594 if (token != #'(')
595 syntax_error ();
596
597 var args = new Array ();
598
599 token = get_token ();
600 while (token != #')')
601 {
602 var arg = new Argument ();
603
604 while (token != tIDENTIFIER)
605 {
606 if (token == tEOF)
607 syntax_error ();
608
609 if (is_type_token (token))
610 arg.type = token;
611 else if (is_copy_type_token (token))
612 arg.copy_type = token;
613 else if (token == tSTATIC)
614 arg.staticp = true;
615 else
616 syntax_error ();
617
618 token = get_token ();
619 }
620
621 if (!valid_c_identifier_re.test (token_value))
622 {
623 System.error (input_file, ":", linenum,
624 ": argument name is not a valid C identifier `",
625 token_value, "'\n");
626 System.exit (1);
627 }
628 arg.name = token_value;
629
630 /* Check some validity conditions. */
631 if (!arg.type)
632 {
633 System.error (input_file, ":", linenum,
634 ": no type specified for argument `",
635 arg.name, "'\n");
636 System.exit (1);
637 }
638 if (arg.staticp)
639 {
640 if (arg.type != tCSTRING && arg.type != tSTRING)
641 {
642 System.error (input_file, ":", linenum,
643 ": type `static' can only be used with `cstring' and `string' arguments\n");
644 System.exit (1);
645 }
646 if (arg.copy_type == tOUT)
647 System.error (input_file, ":", linenum,
648 ": warning: type `static' is meaningful only with `in' and `inout' arguments\n");
649 }
650
651 args.push (arg);
652
653 token = get_token ();
654 if (token == #',')
655 token = get_token ();
656 }
657
658 func.args = args;
659
660 /* The opening '{' of the function body. */
661 token = get_token ();
662 if (token != #'{')
663 syntax_error ();
664
665 /* Get the function body. */
666 var code = new String ("");
667 while (true)
668 {
669 var line = ifp.readln ();
670 if (!line)
671 syntax_error ();
672 linenum++;
673
674 if (end_brace_re.test (line))
675 break;
676
677 code.append (line + "\n");
678 }
679
680 func.body = code;
681 func.generate_js ();
682
683 // func.print_js (ofp_js);
684 func.print_h (ofp_h, true);
685 func.print_c (ofp_c);
686 }
687
688 trailers ();
689 }
690
691 function get_token ()
692 {
693 var ch;
694
695 while ((ch = ifp.readByte ()) != -1)
696 {
697 if (ch == #' ' || ch == #'\t' || ch == #'\v' || ch == #'\r'
698 || ch == #'\f')
699 continue;
700
701 if (ch == #'\n')
702 {
703 linenum++;
704 continue;
705 }
706
707 token_linenum = linenum;
708
709 if (ch == #'/' && peek_char () == #'*')
710 {
711 /* Multi line comment. */
712 ifp.readByte ();
713 while ((ch = ifp.readByte ()) != -1
714 && (ch != #'*' || peek_char () != #'/'))
715 if (ch == #'\n')
716 linenum++;
717
718 /* Consume the peeked #'/' character. */
719 ifp.readByte ();
720 }
721
722 /* Identifiers and keywords. */
723 else if (JSC$lexer_is_identifier_letter (ch))
724 {
725 /* An identifier. */
726 var id = String.fromCharCode (ch);
727
728 while ((ch = ifp.readByte ()) != -1
729 && (JSC$lexer_is_identifier_letter (ch)
730 || JSC$lexer_is_decimal_digit (ch)))
731 id.append (File.byteToString (ch));
732 ifp.ungetByte (ch);
733
734 /* Keywords. */
735 if (id == "function")
736 return tFUNCTION;
737
738 /* Types. */
739 else if (id == "cstring")
740 return tCSTRING;
741 else if (id == "double")
742 return tDOUBLE;
743 else if (id == "int")
744 return tINT;
745 else if (id == "string")
746 return tSTRING;
747 else if (id == "void")
748 return tVOID;
749 else if (id == "in")
750 return tIN;
751 else if (id == "out")
752 return tOUT;
753 else if (id == "inout")
754 return tINOUT;
755 else if (id == "static")
756 return tSTATIC;
757
758 else
759 {
760 /* It really is an identifier. */
761 token_value = id;
762 return tIDENTIFIER;
763 }
764 }
765
766 /* Just return the character as-is. */
767 else
768 return ch;
769 }
770
771 /* EOF reached. */
772 return tEOF;
773 }
774
775 function peek_char ()
776 {
777 var ch = ifp.readByte ();
778 ifp.ungetByte (ch);
779
780 return ch;
781 }
782
783 function syntax_error ()
784 {
785 System.error (input_file, ":", linenum, ": syntax error\n");
786 System.exit (1);
787 }
788
789 function headers ()
790 {
791 var stream;
792
793 /* The header file. */
794
795 stream = ofp_h;
796 header_banner (stream);
797
798 var ppname = path_to_ppname (header_file);
799 stream.write ("\n#ifndef " + ppname
800 + "\n#define " + ppname + "\n");
801
802 /* The C file. */
803 stream = ofp_c;
804 header_banner (stream);
805 stream.write ("\n#include <jsint.h>\n");
806
807 if (!opt_reentrant)
808 {
809 stream.write ("
810 /*
811 * This global interpreter handle can be removed with " + program + "'s
812 * option -r, --reentrant.
813 */
814 ");
815 stream.write ("extern JSInterpPtr jswrap_interp;\n");
816 }
817
818 if (opt_no_error_handler)
819 {
820 stream.write ("
821 /* Prototype for the error handler of the JS runtime errors. */
822 ");
823 stream.write ("void jswrap_error (JSInterpPtr interp, char *error);\n");
824 }
825 else
826 {
827 /* Define the default jswrap_error() function. */
828 stream.write ("
829 /*
830 * This is the default error handler for the JS runtime errors. The default
831 * handler can be removed with " + program + "'s option -n, --no-error-handler.
832 * In this case, your code must define the handler function.
833 */
834 ");
835 stream.write ("\
836 static void
837 jswrap_error (JSInterpPtr interp, char *error)
838 {
839 const char *cp;
840
841 fprintf (stderr, \"JS runtime error: %s\", error);
842
843 cp = js_error_message (interp);
844 if (cp[0])
845 fprintf (stderr, \": %s\", cp);
846 fprintf (stderr, \"\\n\");
847
848 exit (1);
849 }
850 ");
851 }
852
853 stream.write ("
854
855 /*
856 * The global function definitions.
857 */
858 ");
859 }
860
861 function header_banner (stream)
862 {
863 stream.write ("/* This file is automatically generated from `"
864 + input_file + "' by " + program + ". */\n");
865 }
866
867 path_to_ppname_re = new RegExp ("[^A-Za-z_0-9]", "g");
868
869 function path_to_ppname (path)
870 {
871 var str = path.toUpperCase ().replace (path_to_ppname_re, "_");
872 if (#'0' <= str[0] && str[0] <= #'9')
873 str = "_" + str;
874
875 return str;
876 }
877
878 function trailers ()
879 {
880 /* The header file. */
881 var stream = ofp_h;
882 stream.write ("\n#endif /* not " + path_to_ppname (header_file) + " */\n");
883 }
884
885 \f
886 /*
887 Local variables:
888 mode: c
889 End:
890 */