/*
* Process the jswrap input files and generate output.
* Copyright (c) 1998 New Generation Software (NGS) Oy
*
* Author: Markku Rossi <mtr@ngs.fi>
*/
/*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA
*/
/*
* $Source: /usr/local/cvsroot/ngs/js/jswrap/process.js,v $
* $Id: process.js,v 1.9 1998/10/27 07:40:40 mtr Exp $
*/
/*
* Constants and definitions.
*/
/* Tokens. */
tEOF = 1000;
tFUNCTION = 1001;
tIDENTIFIER = 1002;
tCSTRING = 2001;
tDOUBLE = 2002;
tINT = 2003;
tSTRING = 2004;
tVOID = 2005;
tIN = 2010;
tOUT = 2011;
tINOUT = 2012;
tSTATIC = 2020;
function is_type_token (token)
{
return (token == tCSTRING || token == tDOUBLE || token == tINT
|| token == tSTRING || token == tVOID);
}
function is_copy_type_token (token)
{
return (token == tIN || token == tOUT || token == tINOUT);
}
function typename (token)
{
if (token == tCSTRING)
return "cstring";
else if (token == tDOUBLE)
return "double";
else if (token == tINT)
return "int";
else if (token == tSTRING)
return "string";
else if (token == tVOID)
return "void";
else if (token == tIN)
return "in";
else if (token == tOUT)
return "out";
else if (token == tINOUT)
return "inout";
else
return "";
}
function c_typename (token)
{
if (token == tCSTRING)
return "char *";
else if (token == tDOUBLE)
return "double ";
else if (token == tINT)
return "long ";
else if (token == tVOID)
return "void ";
else if (token == tOUT || token == tINOUT)
return "*";
else
return "";
}
function jsint_typename (token)
{
if (token == tCSTRING)
return "JS_STRING";
else if (token == tDOUBLE)
return "JS_FLOAT";
else if (token == tINT)
return "JS_INTEGER";
else if (token == tSTRING)
return "JS_STRING";
else
return "";
}
valid_c_identifier_re = new RegExp ("^[A-Za-z_][A-Za-z_0-9]*$");
end_brace_re = new RegExp ("^}[ \t]*$");
/*
* Variables.
*/
/* The input file line number. */
linenum = 1;
token_linenum = 1;
token_value = null;
/*
* Functions.
*/
function process ()
{
if (false)
System.print (program, ": processing file `", input_file,
"', header=`", header_file,
"', output=`", output_file,
"', reentrant=", opt_reentrant, "\n");
linenum = 1;
/* Open input file. */
ifp = new File (input_file);
if (!ifp.open ("r"))
{
System.error (program, ": couldn't open input file `", input_file,
"': ", System.strerror (System.errno), "\n");
System.exit (1);
}
/* Create output files. */
ofp_c = new File (output_file);
if (!ofp_c.open ("w"))
{
System.error (program, ": couldn't create output file `", output_file,
"': ", System.strerror (System.errno), "\n");
System.exit (1);
}
ofp_h = new File (header_file);
if (!ofp_h.open ("w"))
{
ofp_c.close ();
File.remove (output_file);
System.error (program, ": couldn't create header file `", header_file,
"': ", System.strerror (System.errno), "\n");
System.exit (1);
}
if (false)
{
ofp_js = new File (js_file);
if (!ofp_js.open ("w"))
{
ofp_c.close ();
ofp_h.close ();
File.remove (output_file);
File.remove (header_file);
System.error (program, ": couldn't create JS file `", js_file,
"': ", System.strerror (System.errno), "\n");
System.exit (1);
}
}
/* Parse the input. */
parse ();
/* Cleanup. */
ofp_c.close ();
ofp_h.close ();
}
/*
* The Func class to hold function definitions.
*/
function Func ()
{
}
new Func ();
function Func$generate_js ()
{
this.code = new String ("function " + this.name + " (");
var i;
for (i = 0; i < this.args.length; i++)
{
this.code.append (this.args[i].name);
if (i + 1 < this.args.length)
this.code.append (", ");
}
this.code.append (")\n{");
this.code.append (this.body);
this.code.append ("}\n");
}
Func.prototype.generate_js = Func$generate_js;
function Func$print_js (stream)
{
stream.write (this.code);
}
Func.prototype.print_js = Func$print_js;
function Func$print_h (stream, header)
{
stream.write ("\n");
stream.write (c_typename (this.return_type) + this.name + " (");
if (opt_reentrant)
{
/* The first argument is the interpreter handle. */
stream.write ("\n\tJSInterpPtr interp");
if (this.args.length > 0)
stream.write (",");
}
var i;
for (i = 0; i < this.args.length; i++)
{
stream.write ("\n\t");
if (this.args[i].type == tSTRING)
{
stream.write ("unsigned char *"
+ c_typename (this.args[i].copy_type)
+ this.args[i].name + ",\n\t"
+ "unsigned int "
+ c_typename (this.args[i].copy_type)
+ this.args[i].name + "_len");
}
else
{
stream.write (c_typename (this.args[i].type)
+ c_typename (this.args[i].copy_type));
stream.write (this.args[i].name);
}
if (i + 1 < this.args.length)
stream.write (", ");
}
stream.write ("\n\t)");
if (header)
stream.write (";");
stream.write ("\n");
}
Func.prototype.print_h = Func$print_h;
function Func$print_c (stream)
{
this.compile ();
this.dump_bc (stream);
this.print_h (stream, false);
stream.write ("{\n");
if (!opt_reentrant)
stream.write (" JSInterpPtr interp = jswrap_interp;\n");
stream.write (" JSVirtualMachine *vm = interp->vm;\n");
stream.write (" JSNode argv["
+ (this.args.length + 1).toString ()
+ "];\n");
stream.write (" int result;\n");
if (this.return_type == tCSTRING)
stream.write (" char *cp;\n");
stream.write ("\n");
/* Argument count. */
stream.write (" argv[0].type = JS_INTEGER;\n");
stream.write (" argv[0].u.vinteger = "
+ this.args.length.toString ()
+ ";\n");
/* Arguments. */
/* Construct the argv array. */
for (i = 0; i < this.args.length; i++)
{
var arg = this.args[i];
var cspec = "";
stream.write ("\n");
var argnum = (i + 1).toString ();
if (arg.copy_type == tOUT)
{
stream.write (" argv[" + argnum + "].type = JS_UNDEFINED;\n");
}
else
{
if (arg.copy_type == tINOUT)
cspec = "*";
if (arg.type == tCSTRING)
{
stream.write (" js_vm_make"
+ (arg.staticp ? "_static" : "")
+ "_string (vm, &argv["
+ argnum + "], "
+ cspec + arg.name + ", strlen ("
+ cspec + arg.name + "));\n");
}
else if (arg.type == tDOUBLE)
{
stream.write (" argv[" + argnum + "].type = JS_FLOAT;\n");
stream.write (" argv[" + argnum + "].u.vfloat = "
+ cspec + arg.name + ";\n");
}
else if (arg.type == tINT)
{
stream.write (" argv[" + argnum + "].type = JS_INTEGER;\n");
stream.write (" argv[" + argnum + "].u.vinteger = "
+ cspec + arg.name + ";\n");
}
else if (arg.type == tSTRING)
{
stream.write (" js_vm_make_static_string (vm, &argv["
+ argnum + "], "
+ cspec + arg.name + ", "
+ cspec + arg.name + "_len);\n");
}
else
VM.stackTrace ();
}
}
/* Call the function. */
stream.write ("\
retry:
result = js_vm_apply (vm, \"" + this.name + "\", NULL, "
+ (i + 1).toString () + ", argv);
if (result == 0)
{
JSNode *n = &vm->globals[js_vm_intern (vm, \"" + this.name + "\")];
if (n->type != JS_FUNC)
{
JSByteCode *bc = js_bc_read_data (" + this.name + "_bc,
sizeof (" + this.name + "_bc));
result = js_vm_execute (vm, bc);
js_bc_free (bc);
if (result == 0)
jswrap_error (interp, \"initialization of function`"
+ this.name + "' failed\");
goto retry;
}
jswrap_error (interp, \"execution of function `"
+ this.name + "' failed\");
}
");
/* Handle out and inout parameters. */
for (i = 0; i < this.args.length; i++)
{
var arg = this.args[i];
if (arg.copy_type == tIN)
continue;
var spos = (this.args.length - i).toString ();
/* Check that we have there a correct type. */
stream.write ("
if ((vm->sp - " + spos + ")->type != " + jsint_typename (arg.type) + ")
jswrap_error (interp,
\"wrong return type for argument `"
+ arg.name + "' of function `" + this.name + "'\");
");
/* Handle the different types. */
if (arg.type == tCSTRING)
{
stream.write ("\
*" + arg.name + " = (char *) js_vm_alloc (vm, (vm->sp - "
+ spos + ")->u.vstring->len + 1);
memcpy (*" + arg.name + ", (vm->sp - " + spos + ")->u.vstring->data,
(vm->sp - " + spos + ")->u.vstring->len);
" + arg.name + "[(vm->sp - " + spos + ")->u.vstring->len] = '\\0';
");
}
else if (arg.type == tDOUBLE)
{
stream.write ("\
*" + arg.name + " = (vm->sp - " + spos + ")->u.vfloat;\n");
}
else if (arg.type == tINT)
{
stream.write ("\
*" + arg.name + " = (vm->sp - " + spos + ")->u.vinteger;\n");
}
else if (arg.type == tSTRING)
{
stream.write ("\
*" + arg.name + " = (vm->sp - " + spos + ")->u.vstring->data;
*" + arg.name + "_len = (vm->sp - " + spos + ")->u.vstring->len;
");
}
}
/* Handle the function return value. */
if (this.return_type != tVOID)
{
/* Check that the code returned correct type. */
stream.write ("
if (vm->exec_result.type != " + jsint_typename (this.return_type) + ")
jswrap_error (interp, \"return type mismatch in function `"
+ this.name + "'\");
");
/* Handle the different types. */
if (this.return_type == tCSTRING)
{
stream.write ("\
cp = (char *) js_vm_alloc (vm, vm->exec_result.u.vstring->len + 1);
memcpy (cp, vm->exec_result.u.vstring->data, vm->exec_result.u.vstring->len);
cp[vm->exec_result.u.vstring->len] = '\\0';
return cp;
");
}
else if (this.return_type == tDOUBLE)
stream.write (" return vm->exec_result.u.vfloat;\n");
else if (this.return_type == tINT)
stream.write (" return vm->exec_result.u.vinteger;\n");
}
stream.write ("}\n");
}
Func.prototype.print_c = Func$print_c;
function Func$compile ()
{
/* Create the byte-code for this function. */
var flags = 0;
if (false)
flags |= JSC$FLAG_VERBOSE;
if (opt_debug)
flags |= JSC$FLAG_GENERATE_DEBUG_INFO;
flags |= (JSC$FLAG_OPTIMIZE_PEEPHOLE
| JSC$FLAG_OPTIMIZE_JUMPS
| JSC$FLAG_OPTIMIZE_BC_SIZE);
flags |= JSC$FLAG_WARN_MASK;
try
{
this.bc = JSC$compile_string (this.code, flags, null, null);
}
catch (error)
{
System.error (error, "\n");
System.exit (1);
}
}
Func.prototype.compile = Func$compile;
function Func$dump_bc (stream)
{
stream.write ("\nstatic unsigned char " + this.name + "_bc[] = {");
var i;
for (i = 0; i < this.bc.length; i++)
{
if ((i % 12) == 0)
stream.write ("\n ");
var item = this.bc[i].toString (16);
if (item.length == 1)
item = "0" + item;
stream.write (" 0x" + item + ",");
}
stream.write ("\n};\n");
}
Func.prototype.dump_bc = Func$dump_bc;
/*
* The Argument class to hold function argument definitions.
*/
function Argument ()
{
this.type = false;
this.copy_type = tIN;
this.staticp = false;
}
new Argument ();
function Argument$print ()
{
System.print (typename (this.copy_type), " ", typename (this.type),
" ", this.name);
}
Argument.prototype.print = Argument$print;
/*
* The .jsw file parsing.
*/
function parse ()
{
var token;
var func;
headers ();
while ((token = get_token ()) != tEOF)
{
if (token != tFUNCTION)
syntax_error ();
func = new Func ();
/* Possible return type. */
token = get_token ();
if (is_type_token (token))
{
if (token == tSTRING)
{
System.error (input_file, ":", linenum,
": the function return value can't be `string'\n");
System.exit (1);
}
func.return_type = token;
token = get_token ();
}
else
func.return_type = tVOID;
/* The name of the function. */
if (token != tIDENTIFIER)
syntax_error ();
if (!valid_c_identifier_re.test (token_value))
{
System.error (input_file, ":", linenum,
": function name is not a valid C identifier `",
token_value, "'\n");
System.exit (1);
}
func.name = token_value;
/* Arguments. */
token = get_token ();
if (token != #'(')
syntax_error ();
var args = new Array ();
token = get_token ();
while (token != #')')
{
var arg = new Argument ();
while (token != tIDENTIFIER)
{
if (token == tEOF)
syntax_error ();
if (is_type_token (token))
arg.type = token;
else if (is_copy_type_token (token))
arg.copy_type = token;
else if (token == tSTATIC)
arg.staticp = true;
else
syntax_error ();
token = get_token ();
}
if (!valid_c_identifier_re.test (token_value))
{
System.error (input_file, ":", linenum,
": argument name is not a valid C identifier `",
token_value, "'\n");
System.exit (1);
}
arg.name = token_value;
/* Check some validity conditions. */
if (!arg.type)
{
System.error (input_file, ":", linenum,
": no type specified for argument `",
arg.name, "'\n");
System.exit (1);
}
if (arg.staticp)
{
if (arg.type != tCSTRING && arg.type != tSTRING)
{
System.error (input_file, ":", linenum,
": type `static' can only be used with `cstring' and `string' arguments\n");
System.exit (1);
}
if (arg.copy_type == tOUT)
System.error (input_file, ":", linenum,
": warning: type `static' is meaningful only with `in' and `inout' arguments\n");
}
args.push (arg);
token = get_token ();
if (token == #',')
token = get_token ();
}
func.args = args;
/* The opening '{' of the function body. */
token = get_token ();
if (token != #'{')
syntax_error ();
/* Get the function body. */
var code = new String ("");
while (true)
{
var line = ifp.readln ();
if (!line)
syntax_error ();
linenum++;
if (end_brace_re.test (line))
break;
code.append (line + "\n");
}
func.body = code;
func.generate_js ();
// func.print_js (ofp_js);
func.print_h (ofp_h, true);
func.print_c (ofp_c);
}
trailers ();
}
function get_token ()
{
var ch;
while ((ch = ifp.readByte ()) != -1)
{
if (ch == #' ' || ch == #'\t' || ch == #'\v' || ch == #'\r'
|| ch == #'\f')
continue;
if (ch == #'\n')
{
linenum++;
continue;
}
token_linenum = linenum;
if (ch == #'/' && peek_char () == #'*')
{
/* Multi line comment. */
ifp.readByte ();
while ((ch = ifp.readByte ()) != -1
&& (ch != #'*' || peek_char () != #'/'))
if (ch == #'\n')
linenum++;
/* Consume the peeked #'/' character. */
ifp.readByte ();
}
/* Identifiers and keywords. */
else if (JSC$lexer_is_identifier_letter (ch))
{
/* An identifier. */
var id = String.fromCharCode (ch);
while ((ch = ifp.readByte ()) != -1
&& (JSC$lexer_is_identifier_letter (ch)
|| JSC$lexer_is_decimal_digit (ch)))
id.append (File.byteToString (ch));
ifp.ungetByte (ch);
/* Keywords. */
if (id == "function")
return tFUNCTION;
/* Types. */
else if (id == "cstring")
return tCSTRING;
else if (id == "double")
return tDOUBLE;
else if (id == "int")
return tINT;
else if (id == "string")
return tSTRING;
else if (id == "void")
return tVOID;
else if (id == "in")
return tIN;
else if (id == "out")
return tOUT;
else if (id == "inout")
return tINOUT;
else if (id == "static")
return tSTATIC;
else
{
/* It really is an identifier. */
token_value = id;
return tIDENTIFIER;
}
}
/* Just return the character as-is. */
else
return ch;
}
/* EOF reached. */
return tEOF;
}
function peek_char ()
{
var ch = ifp.readByte ();
ifp.ungetByte (ch);
return ch;
}
function syntax_error ()
{
System.error (input_file, ":", linenum, ": syntax error\n");
System.exit (1);
}
function headers ()
{
var stream;
/* The header file. */
stream = ofp_h;
header_banner (stream);
var ppname = path_to_ppname (header_file);
stream.write ("\n#ifndef " + ppname
+ "\n#define " + ppname + "\n");
/* The C file. */
stream = ofp_c;
header_banner (stream);
stream.write ("\n#include <jsint.h>\n");
if (!opt_reentrant)
{
stream.write ("
/*
* This global interpreter handle can be removed with " + program + "'s
* option -r, --reentrant.
*/
");
stream.write ("extern JSInterpPtr jswrap_interp;\n");
}
if (opt_no_error_handler)
{
stream.write ("
/* Prototype for the error handler of the JS runtime errors. */
");
stream.write ("void jswrap_error (JSInterpPtr interp, char *error);\n");
}
else
{
/* Define the default jswrap_error() function. */
stream.write ("
/*
* This is the default error handler for the JS runtime errors. The default
* handler can be removed with " + program + "'s option -n, --no-error-handler.
* In this case, your code must define the handler function.
*/
");
stream.write ("\
static void
jswrap_error (JSInterpPtr interp, char *error)
{
const char *cp;
fprintf (stderr, \"JS runtime error: %s\", error);
cp = js_error_message (interp);
if (cp[0])
fprintf (stderr, \": %s\", cp);
fprintf (stderr, \"\\n\");
exit (1);
}
");
}
stream.write ("
/*
* The global function definitions.
*/
");
}
function header_banner (stream)
{
stream.write ("/* This file is automatically generated from `"
+ input_file + "' by " + program + ". */\n");
}
path_to_ppname_re = new RegExp ("[^A-Za-z_0-9]", "g");
function path_to_ppname (path)
{
var str = path.toUpperCase ().replace (path_to_ppname_re, "_");
if (#'0' <= str[0] && str[0] <= #'9')
str = "_" + str;
return str;
}
function trailers ()
{
/* The header file. */
var stream = ofp_h;
stream.write ("\n#endif /* not " + path_to_ppname (header_file) + " */\n");
}
/*
Local variables:
mode: c
End:
*/
syntax highlighted by Code2HTML, v. 0.9.1