📄 gram.js
字号:
/*
* Grammar components.
* 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: /cygdrive/c/RCVS/CVS/ReactOS/reactos/lib/kjs/jsc/gram.js,v $
* $Id: gram.js 21681 2006-04-21 15:00:24Z peterw $
*/
/* General helpers. */
function JSC$gram_reset ()
{
JSC$label_count = 1;
JSC$cont_break = new JSC$ContBreak ();
}
function JSC$alloc_label (num_labels)
{
JSC$label_count += num_labels;
return JSC$label_count - num_labels;
}
function JSC$format_label (num)
{
return ".L" + num.toString ();
}
function JSC$count_locals_from_stmt_list (list)
{
var i;
/* First, count how many variables we need at the toplevel. */
var lcount = 0;
for (i = 0; i < list.length; i++)
lcount += list[i].count_locals (false);
/* Second, count the maximum amount needed by the nested blocks. */
var rmax = 0;
for (i = 0; i < list.length; i++)
{
var rc = list[i].count_locals (true);
if (rc > rmax)
rmax = rc;
}
return lcount + rmax;
}
/*
* The handling of the `continue' and `break' labels for looping
* constructs. The variable `JSC$cont_break' holds an instance of
* JSC$ContBreak class. The instance contains a valid chain of
* looping constructs and the currently active with and try testing
* levels. The actual `continue', `break', and `return' statements
* investigate the chain and generate appropriate `with_pop' and
* `try_pop' operands.
*
* If the instance variable `inswitch' is true, the continue statement
* is inside a switch statement. In this case, the continue statement
* must pop one item from the stack. That item is the value of the
* case expression.
*/
function JSC$ContBreakFrame (loop_break, loop_continue, inswitch, label, next)
{
this.loop_break = loop_break;
this.loop_continue = loop_continue;
this.inswitch = inswitch;
this.label = label;
this.next = next;
this.with_nesting = 0;
this.try_nesting = 0;
}
function JSC$ContBreak ()
{
this.top = new JSC$ContBreakFrame (null, null, false, null);
}
new JSC$ContBreak ();
function JSC$ContBreak$push (loop_break, loop_continue, inswitch, label)
{
this.top = new JSC$ContBreakFrame (loop_break, loop_continue, inswitch,
label, this.top);
}
JSC$ContBreak.prototype.push = JSC$ContBreak$push;
function JSC$ContBreak$pop ()
{
if (this.top == null)
error ("jsc: internal error: continue-break stack underflow");
this.top = this.top.next;
}
JSC$ContBreak.prototype.pop = JSC$ContBreak$pop;
/*
* Count the currently active `try' nesting that should be removed on
* `return' statement.
*/
function JSC$ContBreak$try_return_nesting ()
{
var f;
var count = 0;
for (f = this.top; f; f = f.next)
count += f.try_nesting;
return count;
}
JSC$ContBreak.prototype.try_return_nesting = JSC$ContBreak$try_return_nesting;
/*
* Count currently active `with' nesting that should be removed on
* `continue' or `break' statement.
*/
function JSC$ContBreak$count_with_nesting (label)
{
var f;
var count = 0;
for (f = this.top; f; f = f.next)
{
count += f.with_nesting;
if (label)
{
if (f.label == label)
break;
}
else
if (f.loop_continue)
break;
}
return count;
}
JSC$ContBreak.prototype.count_with_nesting = JSC$ContBreak$count_with_nesting;
/*
* Count the currently active `try' nesting that should be removed on
* `continue' or `break' statement.
*/
function JSC$ContBreak$count_try_nesting (label)
{
var f;
var count = 0;
for (f = this.top; f; f = f.next)
{
count += f.try_nesting;
if (label)
{
if (f.label == label)
break;
}
else
if (f.loop_continue)
break;
}
return count;
}
JSC$ContBreak.prototype.count_try_nesting = JSC$ContBreak$count_try_nesting;
function JSC$ContBreak$count_switch_nesting (label)
{
var f;
var count = 0;
for (f = this.top; f; f = f.next)
{
if (f.inswitch)
count++;
if (label)
{
if (f.label == label)
break;
}
else
if (f.loop_continue)
break;
}
return count;
}
JSC$ContBreak.prototype.count_switch_nesting
= JSC$ContBreak$count_switch_nesting;
function JSC$ContBreak$get_continue (label)
{
var f;
for (f = this.top; f; f = f.next)
if (label)
{
if (f.label == label)
return f.loop_continue;
}
else
if (f.loop_continue)
return f.loop_continue;
return null;
}
JSC$ContBreak.prototype.get_continue = JSC$ContBreak$get_continue;
function JSC$ContBreak$get_break (label)
{
var f;
for (f = this.top; f; f = f.next)
if (label)
{
if (f.label == label)
return f.loop_break;
}
else
if (f.loop_break)
return f.loop_break;
return null;
}
JSC$ContBreak.prototype.get_break = JSC$ContBreak$get_break;
function JSC$ContBreak$is_unique_label (label)
{
var f;
for (f = this.top; f; f = f.next)
if (f.label == label)
return false;
return true;
}
JSC$ContBreak.prototype.is_unique_label = JSC$ContBreak$is_unique_label;
JSC$cont_break = null;
/* Function declaration. */
function JSC$function_declaration (ln, lbrace_ln, name, name_given, args,
block, use_arguments_prop)
{
this.linenum = ln;
this.lbrace_linenum = lbrace_ln;
this.name = name;
this.name_given = name_given;
this.args = args;
this.block = block;
this.use_arguments_prop = use_arguments_prop;
this.asm = JSC$function_declaration_asm;
}
function JSC$function_declaration_asm ()
{
var i, a;
/* Define arguments. */
JSC$ns.push_frame ();
for (i = 0, a = 2; i < this.args.length; i++, a++)
JSC$ns.define_symbol (this.args[i], JSC$SCOPE_ARG, a, this.linenum);
/* Define the function name to be a global symbol. */
new JSC$ASM_symbol (this.linenum, this.name).link ();
/* Check that function gets the required amount of arguments. */
new JSC$ASM_load_arg (this.lbrace_linenum, 1).link ();
new JSC$ASM_add_2_i (this.lbrace_linenum).link ();
new JSC$ASM_min_args (this.lbrace_linenum, this.args.length + 2).link ();
/* Count how many local variables we need. */
var num_locals = JSC$count_locals_from_stmt_list (this.block);
/* Is the `arguments' property of function instance used? */
if (this.use_arguments_prop)
num_locals++;
if (num_locals > 0)
{
new JSC$ASM_locals (this.lbrace_linenum, num_locals).link ();
if (this.use_arguments_prop)
{
/*
* Create an array for the arguments array and store it to
* the first local variable.
*/
var ln = this.lbrace_linenum;
var locn = JSC$ns.alloc_local ();
JSC$ns.define_symbol ("arguments", JSC$SCOPE_LOCAL, locn, ln);
new JSC$ASM_const_i0 (ln).link ();
new JSC$ASM_load_global (ln, "Array").link ();
new JSC$ASM_new (ln).link ();
new JSC$ASM_swap (ln).link ();
new JSC$ASM_apop (ln, 2).link ();
new JSC$ASM_store_local (ln, locn).link ();
/* Push individual argumens to the array. */
/* Init the loop counter. */
new JSC$ASM_const_i0 (ln).link ();
var l_loop = new JSC$ASM_label ();
var l_out = new JSC$ASM_label ();
l_loop.link ();
/* Check if we'r done. */
new JSC$ASM_dup (ln).link ();
new JSC$ASM_load_arg (ln, 1).link ();
new JSC$ASM_cmp_ge (ln).link ();
new JSC$ASM_iftrue_b (ln, l_out).link ();
/* Load the nth argument to the top of the stack. */
new JSC$ASM_dup (ln).link ();
new JSC$ASM_add_2_i (ln).link ();
new JSC$ASM_load_nth_arg (ln).link ();
/* Push it to the array. */
new JSC$ASM_const_i1 (ln).link ();
new JSC$ASM_load_local (ln, locn).link ();
new JSC$ASM_call_method (ln, "push").link ();
new JSC$ASM_pop_n (ln, 4).link ();
/* Increment loop counter and continue. */
new JSC$ASM_add_1_i (ln).link ();
new JSC$ASM_jmp (ln, l_loop).link ();
/* We'r done. */
l_out.link ();
/* Pop the loop counter. */
new JSC$ASM_pop (ln).link ();
}
}
/* Assembler for our body. */
for (i = 0; i < this.block.length; i++)
this.block[i].asm ();
/*
* Every function must return something. We could check if all
* control flows in this function ends to a return, but that would
* bee too hard... Just append a return const_undefined. The optimizer
* will remove it if it is not needed.
*/
var ln;
if (this.block.length > 0)
ln = this.block[this.block.length - 1].linenum;
else
ln = this.linenum;
new JSC$ASM_const_undefined (ln).link ();
new JSC$ASM_return (ln).link ();
/* Pop our namespace. */
JSC$ns.pop_frame ();
}
function JSC$zero_function ()
{
return 0;
}
/*
* Statements.
*/
/* Block. */
function JSC$stmt_block (ln, list)
{
this.stype = JSC$STMT_BLOCK;
this.linenum = ln;
this.stmts = list;
this.asm = JSC$stmt_block_asm;
this.count_locals = JSC$stmt_block_count_locals;
}
function JSC$stmt_block_asm ()
{
JSC$ns.push_frame ();
/* Assembler for our stmts. */
var i;
for (i = 0; i < this.stmts.length; i++)
this.stmts[i].asm ();
JSC$ns.pop_frame ();
}
function JSC$stmt_block_count_locals (recursive)
{
if (!recursive)
return 0;
return JSC$count_locals_from_stmt_list (this.stmts);
}
/* Function declaration. */
function JSC$stmt_function_declaration (ln, container_id, function_id,
given_id)
{
this.stype = JSC$STMT_FUNCTION_DECLARATION;
this.linenum = ln;
this.container_id = container_id;
this.function_id = function_id;
this.given_id = given_id;
this.asm = JSC$stmt_function_declaration_asm;
this.count_locals = JSC$zero_function;
}
function JSC$stmt_function_declaration_asm ()
{
new JSC$ASM_load_global (this.linenum, this.function_id).link ();
new JSC$ASM_load_global (this.linenum, this.container_id).link ();
new JSC$ASM_store_property (this.linenum, this.given_id).link ();
}
/* Empty */
function JSC$stmt_empty (ln)
{
this.stype = JSC$STMT_EMPTY;
this.linenum = ln;
this.asm = JSC$stmt_empty_asm;
this.count_locals = JSC$zero_function;
}
function JSC$stmt_empty_asm ()
{
/* Nothing here. */
}
/* Continue. */
function JSC$stmt_continue (ln, label)
{
this.stype = JSC$STMT_CONTINUE;
this.linenum = ln;
this.label = label;
this.asm = JSC$stmt_continue_asm;
this.count_locals = JSC$zero_function;
}
function JSC$stmt_continue_asm ()
{
var l_cont = JSC$cont_break.get_continue (this.label);
if (l_cont == null)
{
if (this.label)
error (JSC$filename + ":" + this.linenum.toString ()
+ ": label `" + this.label
+ "' not found for continue statement");
else
error (JSC$filename + ":" + this.linenum.toString ()
+ ": continue statement not within a loop");
}
var nesting = JSC$cont_break.count_with_nesting (this.label);
if (nesting > 0)
new JSC$ASM_with_pop (this.linenum, nesting).link ();
nesting = JSC$cont_break.count_try_nesting (this.label);
if (nesting > 0)
new JSC$ASM_try_pop (this.linenum, nesting).link ();
nesting = JSC$cont_break.count_switch_nesting (this.label);
if (nesting > 0)
{
/* Pop the value of the switch expression. */
if (nesting == 1)
new JSC$ASM_pop (this.linenum).link ();
else
new JSC$ASM_pop_n (this.linenum, nesting).link ();
}
new JSC$ASM_jmp (this.linenum, l_cont).link ();
}
/* Break. */
function JSC$stmt_break (ln, label)
{
this.stype = JSC$STMT_BREAK;
this.linenum = ln;
this.label = label;
this.asm = JSC$stmt_break_asm;
this.count_locals = JSC$zero_function;
}
function JSC$stmt_break_asm ()
{
var l_break = JSC$cont_break.get_break (this.label);
if (l_break == null)
{
if (this.label)
error (JSC$filename + ":" + this.linenum.toString ()
+ ": label `" + this.label
+ "' not found for break statement");
else
error (JSC$filename + ":" + this.linenum.toString()
+ ": break statement not within a loop or switch");
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -