Files
CMLeonOS/interpreter/UniLua/Parser.cs

1946 lines
44 KiB
C#
Raw Permalink Normal View History

2026-02-03 02:44:58 +08:00
using System;
using System.IO;
using System.Collections.Generic;
using ULDebug = UniLua.Tools.ULDebug;
namespace UniLua
{
using InstructionPtr = Pointer<Instruction>;
public class FuncState
{
public FuncState Prev;
public BlockCnt Block;
public LuaProto Proto;
public LuaState State;
public LLex Lexer;
public Dictionary<int, int> H;
public int NumActVar;
public int FreeReg;
public int Pc;
public int LastTarget;
public int Jpc;
public int FirstLocal;
public FuncState()
{
Proto = new LuaProto();
H = new Dictionary<int, int>();
NumActVar = 0;
FreeReg = 0;
}
public InstructionPtr GetCode( ExpDesc e )
{
return new InstructionPtr( Proto.Code, e.Info );
}
}
public class BlockCnt
{
public BlockCnt Previous;
public int FirstLabel;
public int FirstGoto;
public int NumActVar;
public bool HasUpValue;
public bool IsLoop;
}
public class ConstructorControl
{
public ExpDesc ExpLastItem;
public ExpDesc ExpTable;
public int NumRecord;
public int NumArray;
public int NumToStore;
public ConstructorControl()
{
ExpLastItem = new ExpDesc();
}
}
public enum ExpKind
{
VVOID, /* no value */
VNIL,
VTRUE,
VFALSE,
VK, /* info = index of constant in `k' */
VKNUM, /* nval = numerical value */
VNONRELOC, /* info = result register */
VLOCAL, /* info = local register */
VUPVAL, /* info = index of upvalue in 'upvalues' */
VINDEXED, /* t = table register/upvalue; idx = index R/K */
VJMP, /* info = instruction pc */
VRELOCABLE, /* info = instruction pc */
VCALL, /* info = instruction pc */
VVARARG /* info = instruction pc */
}
public static class ExpKindUtl
{
public static bool VKIsVar( ExpKind k )
{
return ((int)ExpKind.VLOCAL <= (int)k &&
(int)k <= (int)ExpKind.VINDEXED);
}
public static bool VKIsInReg( ExpKind k )
{
return k == ExpKind.VNONRELOC || k == ExpKind.VLOCAL;
}
}
public enum BinOpr
{
ADD,
SUB,
MUL,
DIV,
MOD,
POW,
CONCAT,
EQ,
LT,
LE,
NE,
GT,
GE,
AND,
OR,
NOBINOPR,
}
public enum UnOpr
{
MINUS,
NOT,
LEN,
NOUNOPR,
}
public class ExpDesc
{
public ExpKind Kind;
public int Info;
public struct IndData
{
public int T;
public int Idx;
public ExpKind Vt;
}
public IndData Ind;
public double NumberValue;
public int ExitTrue;
public int ExitFalse;
public void CopyFrom( ExpDesc e )
{
this.Kind = e.Kind;
this.Info = e.Info;
// this.Ind.T = e.Ind.T;
// this.Ind.Idx = e.Ind.Idx;
// this.Ind.Vt = e.Ind.Vt;
this.Ind = e.Ind;
this.NumberValue = e.NumberValue;
this.ExitTrue = e.ExitTrue;
this.ExitFalse = e.ExitFalse;
}
}
public class VarDesc
{
public int Index;
}
public class LabelDesc
{
public string Name; // label identifier
public int Pc; // position in code
public int Line; // line where it appear
public int NumActVar; // local level where it appears in current block
}
public class LHSAssign
{
public LHSAssign Prev;
public ExpDesc Exp;
public LHSAssign() {
Prev = null;
Exp = new ExpDesc();
}
}
public class Parser
{
public static LuaProto Parse(
ILuaState lua, ILoadInfo loadinfo, string name )
{
var parser = new Parser();
parser.Lua = (LuaState)lua;
parser.Lexer = new LLex( lua, loadinfo, name );
var topFuncState = new FuncState();
parser.MainFunc( topFuncState );
return topFuncState.Proto;
}
private const int MAXVARS = 200;
private LLex Lexer;
private FuncState CurFunc;
private List<VarDesc> ActVars;
private List<LabelDesc> PendingGotos;
private List<LabelDesc> ActiveLabels;
private LuaState Lua;
private Parser()
{
ActVars = new List<VarDesc>();
PendingGotos = new List<LabelDesc>();
ActiveLabels = new List<LabelDesc>();
CurFunc = null;
}
private LuaProto AddPrototype()
{
var p = new LuaProto();
CurFunc.Proto.P.Add( p );
return p;
}
private void CodeClosure( ExpDesc v )
{
FuncState fs = CurFunc.Prev;
InitExp( v, ExpKind.VRELOCABLE,
Coder.CodeABx( fs, OpCode.OP_CLOSURE, 0,
(uint)(fs.Proto.P.Count-1) ) );
// fix it at stack top
Coder.Exp2NextReg( fs, v );
}
private void OpenFunc( FuncState fs, BlockCnt block )
{
fs.Lexer = Lexer;
fs.Prev = CurFunc;
CurFunc = fs;
fs.Pc = 0;
fs.LastTarget = 0;
fs.Jpc = Coder.NO_JUMP;
fs.FreeReg = 0;
fs.NumActVar = 0;
fs.FirstLocal = ActVars.Count;
// registers 0/1 are always valid
fs.Proto.MaxStackSize = 2;
fs.Proto.Source = Lexer.Source;
EnterBlock( fs, block, false );
}
private void CloseFunc()
{
Coder.Ret( CurFunc, 0, 0 );
LeaveBlock( CurFunc );
CurFunc = CurFunc.Prev;
}
private void MainFunc( FuncState fs )
{
ExpDesc v = new ExpDesc();
var block = new BlockCnt();
OpenFunc( fs, block );
fs.Proto.IsVarArg = true; // main func is always vararg
InitExp( v, ExpKind.VLOCAL, 0 );
NewUpvalue( fs, LuaDef.LUA_ENV, v );
Lexer.Next(); // read first token
StatList();
// check TK_EOS
CloseFunc();
}
private bool BlockFollow( bool withUntil )
{
switch( Lexer.Token.TokenType )
{
case (int)TK.ELSE:
case (int)TK.ELSEIF:
case (int)TK.END:
case (int)TK.EOS:
return true;
case (int)TK.UNTIL:
return withUntil;
default:
return false;
}
}
private bool TestNext( int tokenType )
{
if( Lexer.Token.TokenType == tokenType )
{
Lexer.Next();
return true;
}
else return false;
}
private void Check( int tokenType )
{
if( Lexer.Token.TokenType != tokenType )
ErrorExpected( tokenType );
}
private void CheckNext( int tokenType )
{
Check( tokenType );
Lexer.Next();
}
private void CheckCondition( bool cond, string msg )
{
if( !cond )
Lexer.SyntaxError( msg );
}
private void EnterLevel()
{
++Lua.NumCSharpCalls;
CheckLimit( CurFunc, Lua.NumCSharpCalls,
LuaLimits.LUAI_MAXCCALLS, "C# levels" );
}
private void LeaveLevel()
{
--Lua.NumCSharpCalls;
}
private void SemanticError( string msg )
{
// TODO
Lexer.SyntaxError( msg );
}
private void ErrorLimit( FuncState fs, int limit, string what )
{
int line = fs.Proto.LineDefined;
string where = (line == 0)
? "main function"
: string.Format("function at line {0}", line);
string msg = string.Format("too many {0} (limit is {1}) in {2}",
what, limit, where);
Lexer.SyntaxError( msg );
}
private void CheckLimit( FuncState fs, int v, int l, string what )
{
if( v > l )
ErrorLimit( fs, l, what );
}
private int RegisterLocalVar( string varname )
{
var v = new LocVar();
v.VarName = varname;
v.StartPc = 0;
v.EndPc = 0;
CurFunc.Proto.LocVars.Add(v);
return CurFunc.Proto.LocVars.Count - 1;
}
private VarDesc NewLocalVar( string name )
{
var fs = CurFunc;
int reg = RegisterLocalVar( name );
CheckLimit( fs, ActVars.Count + 1 - fs.FirstLocal,
MAXVARS, "local variables");
var v = new VarDesc();
v.Index = reg;
return v;
}
private LocVar GetLocalVar( FuncState fs, int i )
{
int idx = ActVars[fs.FirstLocal + i].Index;
Utl.Assert( idx < fs.Proto.LocVars.Count );
return fs.Proto.LocVars[idx];
}
private void AdjustLocalVars( int nvars )
{
var fs = CurFunc;
fs.NumActVar += nvars;
for( ; nvars > 0; --nvars )
{
var v = GetLocalVar( fs, fs.NumActVar - nvars );
v.StartPc = fs.Pc;
}
}
private void RemoveVars( FuncState fs, int toLevel )
{
var len = fs.NumActVar - toLevel;
while( fs.NumActVar > toLevel )
{
var v = GetLocalVar( fs, --fs.NumActVar );
v.EndPc = fs.Pc;
}
ActVars.RemoveRange( ActVars.Count-len, len );
}
private void CloseGoto( int g, LabelDesc label )
{
var gt = PendingGotos[g];
Utl.Assert( gt.Name == label.Name );
if( gt.NumActVar < label.NumActVar )
{
var v = GetLocalVar( CurFunc, gt.NumActVar );
var msg = string.Format(
"<goto {0}> at line {1} jumps into the scope of local '{2}'",
gt.Name, gt.Line, v.VarName);
SemanticError( msg );
}
Coder.PatchList( CurFunc, gt.Pc, label.Pc );
PendingGotos.RemoveAt(g);
}
// try to close a goto with existing labels; this solves backward jumps
private bool FindLabel( int g )
{
var gt = PendingGotos[g];
var block = CurFunc.Block;
for( int i=block.FirstLabel; i<ActiveLabels.Count; ++i )
{
var label = ActiveLabels[i];
// correct label?
if( label.Name == gt.Name )
{
if( gt.NumActVar > label.NumActVar &&
(block.HasUpValue || ActiveLabels.Count > block.FirstLabel) )
{
Coder.PatchClose( CurFunc, gt.Pc, label.NumActVar );
}
CloseGoto( g, label );
return true;
}
}
return false;
}
private LabelDesc NewLebelEntry( string name, int line, int pc )
{
var desc = new LabelDesc();
desc.Name = name;
desc.Pc = pc;
desc.Line = line;
desc.NumActVar = CurFunc.NumActVar;
return desc;
}
// check whether new label `label' matches any pending gotos in current
// block; solves forward jumps
private void FindGotos( LabelDesc label )
{
int i = CurFunc.Block.FirstGoto;
while( i < PendingGotos.Count )
{
if( PendingGotos[i].Name == label.Name )
CloseGoto( i, label );
else
++i;
}
}
// "export" pending gotos to outer level, to check them against
// outer labels; if the block being exited has upvalues, and
// the goto exits the scope of any variable (which can be the
// upvalue), close those variables being exited.
private void MoveGotosOut( FuncState fs, BlockCnt block )
{
int i = block.FirstGoto;
// correct pending gotos to current block and try to close it
// with visible labels
while( i < PendingGotos.Count )
{
var gt = PendingGotos[i];
if( gt.NumActVar > block.NumActVar )
{
if( block.HasUpValue )
Coder.PatchClose( fs, gt.Pc, block.NumActVar );
gt.NumActVar = block.NumActVar;
}
if( !FindLabel(i) )
++i; // move to next one
}
}
// create a label named `break' to resolve break statements
private void BreakLabel()
{
var desc = NewLebelEntry( "break", 0, CurFunc.Pc );
ActiveLabels.Add( desc );
FindGotos( ActiveLabels[ActiveLabels.Count-1] );
}
// generates an error for an undefined `goto'; choose appropriate
// message when label name is a reserved word (which can only be `break')
private void UndefGoto( LabelDesc gt )
{
string template = Lexer.IsReservedWord( gt.Name )
? "<{0}> at line {1} not inside a loop"
: "no visible label '{0}' for <goto> at line {1}";
var msg = string.Format( template, gt.Name, gt.Line );
SemanticError( msg );
}
private void EnterBlock( FuncState fs, BlockCnt block, bool isLoop )
{
block.IsLoop = isLoop;
block.NumActVar = fs.NumActVar;
block.FirstLabel = ActiveLabels.Count;
block.FirstGoto = PendingGotos.Count;
block.HasUpValue = false;
block.Previous = fs.Block;
fs.Block = block;
Utl.Assert( fs.FreeReg == fs.NumActVar );
}
private void LeaveBlock( FuncState fs )
{
var block = fs.Block;
if( block.Previous != null && block.HasUpValue )
{
int j = Coder.Jump( fs );
Coder.PatchClose( fs, j, block.NumActVar );
Coder.PatchToHere( fs, j );
}
if( block.IsLoop )
BreakLabel();
fs.Block = block.Previous;
RemoveVars( fs, block.NumActVar );
Utl.Assert( block.NumActVar == fs.NumActVar );
fs.FreeReg = fs.NumActVar; // free registers
ActiveLabels.RemoveRange( block.FirstLabel,
ActiveLabels.Count - block.FirstLabel );
// inner block?
if( block.Previous != null )
MoveGotosOut( fs, block );
// pending gotos in outer block?
else if( block.FirstGoto < PendingGotos.Count )
UndefGoto( PendingGotos[block.FirstGoto] ); // error
}
private UnOpr GetUnOpr( int op )
{
switch( op )
{
case (int)TK.NOT:
return UnOpr.NOT;
case (int)'-':
return UnOpr.MINUS;
case (int)'#':
return UnOpr.LEN;
default:
return UnOpr.NOUNOPR;
}
}
private BinOpr GetBinOpr( int op )
{
switch( op )
{
case (int)'+': return BinOpr.ADD;
case (int)'-': return BinOpr.SUB;
case (int)'*': return BinOpr.MUL;
case (int)'/': return BinOpr.DIV;
case (int)'%': return BinOpr.MOD;
case (int)'^': return BinOpr.POW;
case (int)TK.CONCAT: return BinOpr.CONCAT;
case (int)TK.NE: return BinOpr.NE;
case (int)TK.EQ: return BinOpr.EQ;
case (int)'<': return BinOpr.LT;
case (int)TK.LE: return BinOpr.LE;
case (int)'>': return BinOpr.GT;
case (int)TK.GE: return BinOpr.GE;
case (int)TK.AND: return BinOpr.AND;
case (int)TK.OR: return BinOpr.OR;
default: return BinOpr.NOBINOPR;
}
}
private int GetBinOprLeftPrior( BinOpr opr )
{
switch( opr )
{
case BinOpr.ADD: return 6;
case BinOpr.SUB: return 6;
case BinOpr.MUL: return 7;
case BinOpr.DIV: return 7;
case BinOpr.MOD: return 7;
case BinOpr.POW: return 10;
case BinOpr.CONCAT: return 5;
case BinOpr.EQ: return 3;
case BinOpr.LT: return 3;
case BinOpr.LE: return 3;
case BinOpr.NE: return 3;
case BinOpr.GT: return 3;
case BinOpr.GE: return 3;
case BinOpr.AND: return 2;
case BinOpr.OR: return 1;
case BinOpr.NOBINOPR:
throw new Exception("GetBinOprLeftPrior(NOBINOPR)");
default:
throw new Exception("Unknown BinOpr");
}
}
private int GetBinOprRightPrior( BinOpr opr )
{
switch( opr )
{
case BinOpr.ADD: return 6;
case BinOpr.SUB: return 6;
case BinOpr.MUL: return 7;
case BinOpr.DIV: return 7;
case BinOpr.MOD: return 7;
case BinOpr.POW: return 9;
case BinOpr.CONCAT: return 4;
case BinOpr.EQ: return 3;
case BinOpr.LT: return 3;
case BinOpr.LE: return 3;
case BinOpr.NE: return 3;
case BinOpr.GT: return 3;
case BinOpr.GE: return 3;
case BinOpr.AND: return 2;
case BinOpr.OR: return 1;
case BinOpr.NOBINOPR:
throw new Exception("GetBinOprRightPrior(NOBINOPR)");
default:
throw new Exception("Unknown BinOpr");
}
}
private const int UnaryPrior = 8;
// statlist -> { stat [';'] }
private void StatList()
{
while( ! BlockFollow( true ) )
{
if( Lexer.Token.TokenType == (int)TK.RETURN )
{
Statement();
return; // 'return' must be last statement
}
Statement();
}
}
// fieldsel -> ['.' | ':'] NAME
private void FieldSel( ExpDesc v )
{
var fs = CurFunc;
var key = new ExpDesc();
Coder.Exp2AnyRegUp( fs, v );
Lexer.Next(); // skip the dot or colon
CodeString( key, CheckName() );
Coder.Indexed( fs, v, key );
}
// cond -> exp
private int Cond()
{
var v = new ExpDesc();
Expr( v ); // read condition
// `falses' are all equal here
if( v.Kind == ExpKind.VNIL )
v.Kind = ExpKind.VFALSE;
Coder.GoIfTrue( CurFunc, v );
return v.ExitFalse;
}
private void GotoStat( int pc )
{
string label;
if( TestNext( (int)TK.GOTO ) )
label = CheckName();
else
{
Lexer.Next();
label = "break";
}
PendingGotos.Add( NewLebelEntry( label, Lexer.LineNumber, pc ) );
// close it if label already defined
FindLabel( PendingGotos.Count-1 );
}
// check for repeated labels on the same block
private void CheckRepeated( FuncState fs, List<LabelDesc> list, string label )
{
for( int i = fs.Block.FirstLabel; i < list.Count; ++i )
{
if( label == list[i].Name )
{
SemanticError( string.Format(
"label '{0}' already defined on line {1}",
label, list[i].Line ) );
}
}
}
// skip no-op statements
private void SkipNoOpStat()
{
while( Lexer.Token.TokenType == (int)';' ||
Lexer.Token.TokenType == (int)TK.DBCOLON )
{
Statement();
}
}
private void LabelStat( string label, int line )
{
var fs = CurFunc;
CheckRepeated( fs, ActiveLabels, label );
CheckNext( (int)TK.DBCOLON );
var desc = NewLebelEntry( label, line, fs.Pc );
ActiveLabels.Add( desc );
SkipNoOpStat();
if( BlockFollow( false ) )
{
// assume that locals are already out of scope
desc.NumActVar = fs.Block.NumActVar;
}
FindGotos( desc );
}
// whilestat -> WHILE cond DO block END
private void WhileStat( int line )
{
var fs = CurFunc;
var block = new BlockCnt();
Lexer.Next(); // skip WHILE
int whileInit = Coder.GetLabel( fs );
int condExit = Cond();
EnterBlock( fs, block, true );
CheckNext( (int)TK.DO );
Block();
Coder.JumpTo( fs, whileInit );
CheckMatch( (int)TK.END, (int)TK.WHILE, line );
LeaveBlock( fs );
Coder.PatchToHere( fs, condExit );
}
// repeatstat -> REPEAT block UNTIL cond
private void RepeatStat( int line )
{
var fs = CurFunc;
int repeatInit = Coder.GetLabel( fs );
var blockLoop = new BlockCnt();
var blockScope = new BlockCnt();
EnterBlock( fs, blockLoop, true );
EnterBlock( fs, blockScope, false );
Lexer.Next();
StatList();
CheckMatch( (int)TK.UNTIL, (int)TK.REPEAT, line );
int condExit = Cond();
if( blockScope.HasUpValue )
{
Coder.PatchClose( fs, condExit, blockScope.NumActVar );
}
LeaveBlock( fs );
Coder.PatchList( fs, condExit, repeatInit ); // close the loop
LeaveBlock( fs );
}
private int Exp1()
{
var e = new ExpDesc();
Expr( e );
Coder.Exp2NextReg( CurFunc, e );
Utl.Assert( e.Kind == ExpKind.VNONRELOC );
return e.Info;
}
// forbody -> DO block
private void ForBody( int t, int line, int nvars, bool isnum )
{
var fs = CurFunc;
var block = new BlockCnt();
AdjustLocalVars( 3 ); // control variables
CheckNext( (int)TK.DO );
int prep = isnum ? Coder.CodeAsBx( fs, OpCode.OP_FORPREP, t, Coder.NO_JUMP )
: Coder.Jump( fs );
EnterBlock( fs, block, false );
AdjustLocalVars( nvars );
Coder.ReserveRegs( fs, nvars );
Block();
LeaveBlock( fs );
Coder.PatchToHere( fs, prep );
int endfor;
if( isnum ) // numeric for?
{
endfor = Coder.CodeAsBx( fs, OpCode.OP_FORLOOP, t, Coder.NO_JUMP );
}
else // generic for
{
Coder.CodeABC( fs, OpCode.OP_TFORCALL, t, 0, nvars );
Coder.FixLine( fs, line );
endfor = Coder.CodeAsBx( fs, OpCode.OP_TFORLOOP, t+2, Coder.NO_JUMP );
}
Coder.PatchList( fs, endfor, prep+1 );
Coder.FixLine( fs, line );
}
// fornum -> NAME = exp1,expe1[,exp1] forbody
private void ForNum( string varname, int line )
{
var fs = CurFunc;
var save = fs.FreeReg;
ActVars.Add( NewLocalVar("(for index)") );
ActVars.Add( NewLocalVar("(for limit)") );
ActVars.Add( NewLocalVar("(for step)") );
ActVars.Add( NewLocalVar(varname) );
CheckNext( (int)'=' );
Exp1(); // initial value
CheckNext( (int)',' );
Exp1(); // limit
if( TestNext( (int)',' ) )
{
Exp1(); // optional step
}
else // default step = 1
{
Coder.CodeK( fs, fs.FreeReg, Coder.NumberK( fs, 1 ) );
Coder.ReserveRegs( fs, 1 );
}
ForBody( save, line, 1, true );
}
// forlist -> NAME {,NAME} IN explist forbody
private void ForList( string indexName )
{
var fs = CurFunc;
var e = new ExpDesc();
int nvars = 4; // gen, state, control, plus at least one declared var
int save = fs.FreeReg;
// create control variables
ActVars.Add( NewLocalVar("(for generator)") );
ActVars.Add( NewLocalVar("(for state)") );
ActVars.Add( NewLocalVar("(for control)") );
// create declared variables
ActVars.Add( NewLocalVar( indexName ) );
while( TestNext( (int)',' ) )
{
ActVars.Add( NewLocalVar( CheckName() ) );
nvars++;
}
CheckNext( (int)TK.IN );
int line = Lexer.LineNumber;
AdjustAssign( 3, ExpList(e), e );
Coder.CheckStack( fs, 3 ); // extra space to call generator
ForBody( save, line, nvars-3, false );
}
// forstat -> FOR (fornum | forlist) END
private void ForStat( int line )
{
var fs = CurFunc;
var block = new BlockCnt();
EnterBlock( fs, block, true );
Lexer.Next(); // skip `for'
string varname = CheckName();
switch( Lexer.Token.TokenType )
{
case (int)'=': ForNum( varname, line ); break;
case (int)',': case (int)TK.IN: ForList( varname ); break;
default: Lexer.SyntaxError("'=' or 'in' expected"); break;
}
CheckMatch( (int)TK.END, (int)TK.FOR, line );
LeaveBlock( fs );
}
// test_then_block -> [IF | ELSEIF] cond THEN block
private int TestThenBlock( int escapeList )
{
var fs = CurFunc;
var block = new BlockCnt();
int jf; // instruction to skip `then' code (if condition is false)
// skip IF or ELSEIF
Lexer.Next();
// read condition
var v = new ExpDesc();
Expr( v );
CheckNext( (int)TK.THEN );
if( Lexer.Token.TokenType == (int)TK.GOTO ||
Lexer.Token.TokenType == (int)TK.BREAK )
{
// will jump to label if condition is true
Coder.GoIfFalse( CurFunc, v );
// must enter block before `goto'
EnterBlock( fs, block, false );
// handle goto/break
GotoStat( v.ExitTrue );
// skip other no-op statements
SkipNoOpStat();
// `goto' is the entire block?
if( BlockFollow( false ) )
{
LeaveBlock( fs );
return escapeList;
}
else
{
jf = Coder.Jump( fs );
}
}
// regular case (not goto/break)
else
{
// skip over block if condition is false
Coder.GoIfTrue( CurFunc, v );
EnterBlock( fs, block, false );
jf = v.ExitFalse;
}
// `then' part
StatList();
LeaveBlock( fs );
// followed by `else' or `elseif'
if( Lexer.Token.TokenType == (int)TK.ELSE ||
Lexer.Token.TokenType == (int)TK.ELSEIF )
{
// must jump over it
escapeList = Coder.Concat( fs, escapeList, Coder.Jump(fs) );
}
Coder.PatchToHere( fs, jf );
return escapeList;
}
// ifstat -> IF cond THEN block {ELSEIF cond THEN block} [ELSE block] END
private void IfStat( int line )
{
var fs = CurFunc;
// exit list for finished parts
int escapeList = Coder.NO_JUMP;
// IF cond THEN block
escapeList = TestThenBlock( escapeList );
// ELSEIF cond THEN block
while( Lexer.Token.TokenType == (int)TK.ELSEIF )
escapeList = TestThenBlock( escapeList );
// `else' part
if( TestNext( (int)TK.ELSE ) )
Block();
CheckMatch( (int)TK.END, (int)TK.IF, line );
Coder.PatchToHere( fs, escapeList );
}
private void LocalFunc()
{
var b = new ExpDesc();
var fs = CurFunc;
var v = NewLocalVar(CheckName());
ActVars.Add(v);
AdjustLocalVars(1); /// enter its scope
Body( b, false, Lexer.LineNumber );
GetLocalVar( fs, b.Info ).StartPc = fs.Pc;
}
// stat -> LOCAL NAME {`,' NAME} [`=' explist]
private void LocalStat()
{
int nvars = 0;
int nexps;
var e = new ExpDesc();
do {
var v = NewLocalVar( CheckName() );
ActVars.Add(v);
++nvars;
} while( TestNext( (int)',' ) );
if( TestNext( (int)'=' ) )
nexps = ExpList( e );
else
{
e.Kind = ExpKind.VVOID;
nexps = 0;
}
AdjustAssign( nvars, nexps, e );
AdjustLocalVars( nvars );
}
// funcname -> NAME {fieldsel} [`:' NAME]
private bool FuncName( ExpDesc v )
{
SingleVar( v );
while( Lexer.Token.TokenType == (int)'.' )
{
FieldSel( v );
}
if( Lexer.Token.TokenType == (int)':' )
{
FieldSel( v );
return true; // is method
}
else
{
return false;
}
}
// funcstat -> FUNCTION funcname BODY
private void FuncStat( int line )
{
var v = new ExpDesc();
var b = new ExpDesc();
Lexer.Next();
bool isMethod = FuncName( v );
Body( b, isMethod, line );
Coder.StoreVar( CurFunc, v, b );
Coder.FixLine( CurFunc, line );
}
// stat -> func | assignment
private void ExprStat()
{
var v = new LHSAssign();
SuffixedExp( v.Exp );
// stat -> assignment ?
if( Lexer.Token.TokenType == (int)'=' ||
Lexer.Token.TokenType == (int)',' )
{
v.Prev = null;
Assignment( v, 1 );
}
// stat -> func
else
{
if( v.Exp.Kind != ExpKind.VCALL )
Lexer.SyntaxError("syntax error");
var pi = CurFunc.GetCode( v.Exp );
pi.Value = pi.Value.SETARG_C( 1 ); // call statment uses no results
}
}
// stat -> RETURN [explist] [';']
private void RetStat()
{
var fs = CurFunc;
int first, nret; // registers with returned values
if( BlockFollow( true ) || Lexer.Token.TokenType == (int)';' )
{
first = nret = 0; // return no values
}
else
{
var e = new ExpDesc();
nret = ExpList( e );
if( HasMultiRet( e.Kind ) )
{
Coder.SetMultiRet( fs, e );
if( e.Kind == ExpKind.VCALL && nret == 1 ) // tail call?
{
var pi = fs.GetCode(e);
pi.Value = pi.Value.SET_OPCODE( OpCode.OP_TAILCALL );
Utl.Assert( pi.Value.GETARG_A() == fs.NumActVar );
}
first = fs.NumActVar;
nret = LuaDef.LUA_MULTRET;
}
else
{
if( nret == 1 ) // only one single value
{
first = Coder.Exp2AnyReg( fs, e );
}
else
{
Coder.Exp2NextReg( fs, e ); // values must go to the `stack'
first = fs.NumActVar;
Utl.Assert( nret == fs.FreeReg - first );
}
}
}
Coder.Ret( fs, first, nret );
TestNext( (int)';' ); // skip optional semicolon
}
private void Statement()
{
// Console.WriteLine("Statement ::" + Lexer.Token);
int line = Lexer.LineNumber;
EnterLevel();
switch( Lexer.Token.TokenType )
{
case (int)';': {
Lexer.Next();
break;
}
// stat -> ifstat
case (int)TK.IF: {
IfStat( line );
break;
}
// stat -> whilestat
case (int)TK.WHILE: {
WhileStat( line );
break;
}
// stat -> DO block END
case (int)TK.DO: {
Lexer.Next();
Block();
CheckMatch( (int)TK.END, (int)TK.DO, line );
break;
}
// stat -> forstat
case (int)TK.FOR: {
ForStat( line );
break;
}
// stat -> repeatstat
case (int)TK.REPEAT: {
RepeatStat( line );
break;
}
// stat -> funcstat
case (int)TK.FUNCTION: {
FuncStat( line );
break;
}
// stat -> localstat
case (int)TK.LOCAL: {
Lexer.Next();
// local function?
if( TestNext( (int)TK.FUNCTION ) )
LocalFunc();
else
LocalStat();
break;
}
// stat -> label
case (int)TK.DBCOLON: {
Lexer.Next(); // skip double colon
LabelStat( CheckName(), line );
break;
}
// stat -> retstat
case (int)TK.RETURN: {
Lexer.Next(); // skip RETURN
RetStat();
break;
}
// stat -> breakstat
// stat -> 'goto' NAME
case (int)TK.BREAK:
case (int)TK.GOTO: {
GotoStat( Coder.Jump( CurFunc ) );
break;
}
// stat -> func | assignment
default:
ExprStat();
break;
}
// Console.WriteLine( "MaxStackSize: " + CurFunc.Proto.MaxStackSize );
// Console.WriteLine( "FreeReg: " + CurFunc.FreeReg );
// Console.WriteLine( "NumActVar: " + CurFunc.NumActVar );
Utl.Assert( CurFunc.Proto.MaxStackSize >= CurFunc.FreeReg &&
CurFunc.FreeReg >= CurFunc.NumActVar );
CurFunc.FreeReg = CurFunc.NumActVar; // free registers
LeaveLevel();
}
private string CheckName()
{
// Console.WriteLine( Lexer.Token );
var t = Lexer.Token as NameToken;
// TEST CODE
if( t == null )
{
Console.WriteLine( Lexer.LineNumber + ":" + Lexer.Token );
}
string name = t.SemInfo;
Lexer.Next();
return name;
}
private int SearchVar( FuncState fs, string name )
{
for( int i=fs.NumActVar-1; i>=0; i-- )
{
if( name == GetLocalVar( fs, i ).VarName )
return i;
}
return -1; // not found
}
private void MarkUpvalue( FuncState fs, int level )
{
var block = fs.Block;
while( block.NumActVar > level ) block = block.Previous;
block.HasUpValue = true;
}
private ExpKind SingleVarAux( FuncState fs, string name, ExpDesc e, bool flag )
{
if( fs == null )
return ExpKind.VVOID;
// look up locals at current level
int v = SearchVar( fs, name );
if( v >= 0 )
{
InitExp( e, ExpKind.VLOCAL, v );
if( !flag )
MarkUpvalue( fs, v ); // local will be used as an upval
return ExpKind.VLOCAL;
}
// not found as local at current level; try upvalues
int idx = SearchUpvalues( fs, name );
if( idx < 0 ) // not found?
{
if( SingleVarAux( fs.Prev, name, e, false ) == ExpKind.VVOID )
return ExpKind.VVOID; // not found; is a global
idx = NewUpvalue( fs, name, e );
}
InitExp( e, ExpKind.VUPVAL, idx );
return ExpKind.VUPVAL;
}
private void SingleVar( ExpDesc e )
{
string name = CheckName();
if( SingleVarAux( CurFunc, name, e, true ) == ExpKind.VVOID )
{
ExpDesc key = new ExpDesc();
SingleVarAux( CurFunc, LuaDef.LUA_ENV, e, true );
Utl.Assert( e.Kind == ExpKind.VLOCAL ||
e.Kind == ExpKind.VUPVAL );
CodeString( key, name );
Coder.Indexed( CurFunc, e, key );
}
}
private void AdjustAssign( int nvars, int nexps, ExpDesc e )
{
var fs = CurFunc;
int extra = nvars - nexps;
if( HasMultiRet( e.Kind ) )
{
// includes call itself
++extra;
if( extra < 0 )
extra = 0;
Coder.SetReturns( fs, e, extra );
if( extra > 1 )
Coder.ReserveRegs( fs, extra-1 );
}
else
{
if( e.Kind != ExpKind.VVOID )
Coder.Exp2NextReg( fs, e ); // close last expression
if( extra > 0 )
{
int reg = fs.FreeReg;
Coder.ReserveRegs( fs, extra );
Coder.CodeNil( fs, reg, extra );
}
}
}
// check whether, in an assignment to an upvalue/local variable, the
// upvalue/local variable is begin used in a previous assignment to a
// table. If so, save original upvalue/local value in a safe place and
// use this safe copy in the previous assignment.
private void CheckConflict( LHSAssign lh, ExpDesc v )
{
var fs = CurFunc;
// eventual position to save local variable
int extra = fs.FreeReg;
bool conflict = false;
// check all previous assignments
for( ; lh != null; lh = lh.Prev )
{
var e = lh.Exp;
// assign to a table?
if( e.Kind == ExpKind.VINDEXED )
{
// table is the upvalue/local being assigned now?
if( e.Ind.Vt == v.Kind && e.Ind.T == v.Info )
{
conflict = true;
e.Ind.Vt = ExpKind.VLOCAL;
e.Ind.T = extra; // previous assignment will use safe copy
}
// index is the local being assigned? (index cannot be upvalue)
if( v.Kind == ExpKind.VLOCAL && e.Ind.Idx == v.Info )
{
conflict = true;
e.Ind.Idx = extra; // previous assignment will use safe copy
}
}
}
if( conflict )
{
// copy upvalue/local value to a temporary (in position 'extra')
var op = (v.Kind == ExpKind.VLOCAL) ? OpCode.OP_MOVE : OpCode.OP_GETUPVAL;
Coder.CodeABC( fs, op, extra, v.Info, 0 );
Coder.ReserveRegs( fs, 1 );
}
}
// assignment -> ',' suffixedexp assignment
private void Assignment( LHSAssign lh, int nvars )
{
CheckCondition( ExpKindUtl.VKIsVar( lh.Exp.Kind ), "syntax error" );
ExpDesc e = new ExpDesc();
if( TestNext( (int)',' ) )
{
var nv = new LHSAssign();
nv.Prev = lh;
SuffixedExp( nv.Exp );
if( nv.Exp.Kind != ExpKind.VINDEXED )
CheckConflict( lh, nv.Exp );
CheckLimit( CurFunc, nvars + Lua.NumCSharpCalls,
LuaLimits.LUAI_MAXCCALLS, "C# levels" );
Assignment( nv, nvars+1 );
}
else
{
CheckNext( (int)'=' );
int nexps = ExpList( e );
if( nexps != nvars )
{
AdjustAssign( nvars, nexps, e );
if( nexps > nvars )
{
// remove extra values
CurFunc.FreeReg -= (nexps - nvars);
}
}
else
{
Coder.SetOneRet( CurFunc, e );
Coder.StoreVar( CurFunc, lh.Exp, e );
return;
}
}
// default assignment
InitExp( e, ExpKind.VNONRELOC, CurFunc.FreeReg-1 );
Coder.StoreVar( CurFunc, lh.Exp, e );
}
private int ExpList( ExpDesc e )
{
int n = 1; // at least one expression
Expr( e );
while( TestNext( (int)',' ) )
{
Coder.Exp2NextReg( CurFunc, e );
Expr( e );
n++;
}
return n;
}
private void Expr( ExpDesc e )
{
SubExpr( e, 0 );
}
private BinOpr SubExpr( ExpDesc e, int limit )
{
// Console.WriteLine("SubExpr limit:" + limit);
EnterLevel();
UnOpr uop = GetUnOpr( Lexer.Token.TokenType );
if( uop != UnOpr.NOUNOPR )
{
int line = Lexer.LineNumber;
Lexer.Next();
SubExpr( e, UnaryPrior );
Coder.Prefix( CurFunc, uop, e, line );
}
else SimpleExp( e );
// expand while operators have priorities higher than `limit'
BinOpr op = GetBinOpr(Lexer.Token.TokenType );
while( op != BinOpr.NOBINOPR && GetBinOprLeftPrior( op ) > limit )
{
// Console.WriteLine("op:" + op);
int line = Lexer.LineNumber;
Lexer.Next();
Coder.Infix( CurFunc, op, e );
// read sub-expression with higher priority
ExpDesc e2 = new ExpDesc();
BinOpr nextOp = SubExpr( e2, GetBinOprRightPrior( op ) );
Coder.Posfix( CurFunc, op, e, e2, line );
op = nextOp;
}
LeaveLevel();
return op;
}
private bool HasMultiRet( ExpKind k )
{
return k == ExpKind.VCALL || k == ExpKind.VVARARG;
}
private void ErrorExpected( int token )
{
Lexer.SyntaxError( string.Format( "{0} expected",
((char)token).ToString() ) );
}
private void CheckMatch( int what, int who, int where )
{
if( !TestNext( what ) )
{
if( where == Lexer.LineNumber )
ErrorExpected( what );
else
Lexer.SyntaxError( string.Format(
"{0} expected (to close {1} at line {2})",
((char)what).ToString(),
((char)who).ToString(),
where ) );
}
}
// block -> statlist
private void Block()
{
var fs = CurFunc;
var block = new BlockCnt();
EnterBlock( fs, block, false );
StatList();
LeaveBlock( fs );
}
// index -> '[' expr ']'
private void YIndex( ExpDesc v )
{
Lexer.Next();
Expr( v );
Coder.Exp2Val( CurFunc, v );
CheckNext( (int)']' );
}
// recfield -> (NAME | '[' exp1 ']') = exp1
private void RecField( ConstructorControl cc )
{
var fs = CurFunc;
int reg = fs.FreeReg;
var key = new ExpDesc();
var val = new ExpDesc();
if( Lexer.Token.TokenType == (int)TK.NAME )
{
CheckLimit( fs, cc.NumRecord, LuaLimits.MAX_INT,
"items in a constructor" );
CodeString( key, CheckName() );
}
// ls->t.token == '['
else
{
YIndex( key );
}
cc.NumRecord++;
CheckNext( (int)'=' );
int rkkey = Coder.Exp2RK( fs, key );
Expr( val );
Coder.CodeABC( fs, OpCode.OP_SETTABLE, cc.ExpTable.Info, rkkey,
Coder.Exp2RK( fs, val ) );
fs.FreeReg = reg; // free registers
}
private void CloseListField( FuncState fs, ConstructorControl cc )
{
// there is no list item
if( cc.ExpLastItem.Kind == ExpKind.VVOID )
return;
Coder.Exp2NextReg( fs, cc.ExpLastItem );
cc.ExpLastItem.Kind = ExpKind.VVOID;
if( cc.NumToStore == LuaDef.LFIELDS_PER_FLUSH )
{
// flush
Coder.SetList( fs, cc.ExpTable.Info, cc.NumArray, cc.NumToStore );
// no more item pending
cc.NumToStore = 0;
}
}
private void LastListField( FuncState fs, ConstructorControl cc )
{
if( cc.NumToStore == 0 )
return;
if( HasMultiRet( cc.ExpLastItem.Kind ) )
{
Coder.SetMultiRet( fs, cc.ExpLastItem );
Coder.SetList( fs, cc.ExpTable.Info, cc.NumArray, LuaDef.LUA_MULTRET );
// do not count last expression (unknown number of elements)
cc.NumArray--;
}
else
{
if( cc.ExpLastItem.Kind != ExpKind.VVOID )
Coder.Exp2NextReg( fs, cc.ExpLastItem );
Coder.SetList( fs, cc.ExpTable.Info, cc.NumArray, cc.NumToStore );
}
}
// listfield -> exp
private void ListField( ConstructorControl cc )
{
Expr( cc.ExpLastItem );
CheckLimit( CurFunc, cc.NumArray, LuaLimits.MAX_INT,
"items in a constructor" );
cc.NumArray++;
cc.NumToStore++;
}
// field -> listfield | recfield
private void Field( ConstructorControl cc )
{
switch( Lexer.Token.TokenType )
{
// may be 'listfield' or 'recfield'
case (int)TK.NAME: {
// expression?
if( Lexer.GetLookAhead().TokenType != (int)'=' )
ListField( cc );
else
RecField( cc );
break;
}
case (int)'[': {
RecField( cc );
break;
}
default: {
ListField( cc );
break;
}
}
}
// converts an integer to a "floating point byte", represented as
// (eeeeexxx), where the real value is (1xxx) * 2^(eeeee - 1) if
// eeeee != 0 and (xxx) otherwise.
private int Integer2FloatingPointByte(uint x)
{
int e = 0; // exponent
if( x < 8 )
return (int)x;
while( x >= 0x10 )
{
x = (x+1) >> 1;
++e;
}
return ((e+1) << 3) | ((int)x - 8);
}
// constructor -> '{' [ field { sep field } [sep] ] '}'
// sep -> ',' | ';'
private void Constructor( ExpDesc t )
{
var fs = CurFunc;
int line = Lexer.LineNumber;
int pc = Coder.CodeABC( fs, OpCode.OP_NEWTABLE, 0, 0, 0 );
var cc = new ConstructorControl();
cc.ExpTable = t;
InitExp( t, ExpKind.VRELOCABLE, pc );
InitExp( cc.ExpLastItem, ExpKind.VVOID, 0); // no value (yet)
Coder.Exp2NextReg( fs, t );
CheckNext( (int)'{' );
do {
Utl.Assert( cc.ExpLastItem.Kind == ExpKind.VVOID ||
cc.NumToStore > 0 );
if( Lexer.Token.TokenType == (int)'}' )
break;
CloseListField( fs, cc );
Field( cc );
} while( TestNext( (int)',' ) || TestNext( (int)';' ) );
CheckMatch( (int)'}', (int)'{', line );
LastListField( fs, cc );
// set initial array size and table size
// OP_NEWTABLE ARG_B ARG_C ,
// ,
// var ins = fs.Proto.Code[pc];
// ins.SETARG_B( 0 );
// ins.SETARG_C( 0 );
// , , bytecode luac
var ins = fs.Proto.Code[pc];
ins.SETARG_B( Integer2FloatingPointByte( (uint)cc.NumArray ) );
ins.SETARG_C( Integer2FloatingPointByte( (uint)cc.NumRecord) );
fs.Proto.Code[pc] = ins; // Instruction
}
private void ParList()
{
int numParams = 0;
CurFunc.Proto.IsVarArg = false;
// is `parlist' not empty?
if( Lexer.Token.TokenType != (int)')' )
{
do {
switch( Lexer.Token.TokenType )
{
// param -> NAME
case (int)TK.NAME: {
var v =NewLocalVar( CheckName() );
ActVars.Add( v );
++numParams;
break;
}
case (int)TK.DOTS: {
Lexer.Next();
CurFunc.Proto.IsVarArg = true;
break;
}
default: {
Lexer.SyntaxError("<name> or '...' expected");
break;
}
}
} while( !CurFunc.Proto.IsVarArg && TestNext( (int)',' ) );
}
AdjustLocalVars( numParams );
CurFunc.Proto.NumParams = CurFunc.NumActVar;
Coder.ReserveRegs( CurFunc, CurFunc.NumActVar );
}
private void Body( ExpDesc e, bool isMethod, int line )
{
var newFs = new FuncState();
var block = new BlockCnt();
newFs.Proto = AddPrototype();
newFs.Proto.LineDefined = line;
OpenFunc( newFs, block );
CheckNext( (int)'(' );
if( isMethod )
{
// create `self' parameter
var v = NewLocalVar("self");
ActVars.Add(v);
AdjustLocalVars(1);
}
ParList();
CheckNext( (int)')' );
StatList();
newFs.Proto.LastLineDefined = Lexer.LineNumber;
CheckMatch( (int)TK.END, (int)TK.FUNCTION, line );
CodeClosure(e);
CloseFunc();
}
private void FuncArgs( ExpDesc e, int line )
{
var args = new ExpDesc();
switch( Lexer.Token.TokenType )
{
// funcargs -> `(' [ explist ] `)'
case (int)'(': {
Lexer.Next();
if( Lexer.Token.TokenType == ')' ) // arg list is empty?
args.Kind = ExpKind.VVOID;
else {
ExpList( args );
Coder.SetMultiRet( CurFunc, args );
}
CheckMatch( (int)')', (int)'(', line );
break;
}
// funcargs -> constructor
case (int)'{': {
Constructor( args );
break;
}
// funcargs -> STRING
case (int)TK.STRING: {
var st = Lexer.Token as StringToken;
CodeString( args, st.SemInfo );
Lexer.Next();
break;
}
default: {
Lexer.SyntaxError( "function arguments expected" );
break;
}
}
Utl.Assert( e.Kind == ExpKind.VNONRELOC );
int baseReg = e.Info;
int nparams;
if( HasMultiRet( args.Kind ) )
nparams = LuaDef.LUA_MULTRET;
else {
if( args.Kind != ExpKind.VVOID )
Coder.Exp2NextReg( CurFunc, args ); // close last argument
nparams = CurFunc.FreeReg - (baseReg+1);
}
InitExp( e, ExpKind.VCALL, Coder.CodeABC( CurFunc,
OpCode.OP_CALL, baseReg, nparams+1, 2 ) );
Coder.FixLine( CurFunc, line );
// call remove function and arguments and leaves
// (unless changed) one result
CurFunc.FreeReg = baseReg + 1;
}
// ==============================================================
// Expression parsing
// ==============================================================
// primaryexp -> NAME | '(' expr ')'
private void PrimaryExp( ExpDesc e )
{
switch( Lexer.Token.TokenType )
{
case (int)'(': {
int line = Lexer.LineNumber;
Lexer.Next();
Expr( e );
CheckMatch( (int)')', (int)'(', line );
Coder.DischargeVars( CurFunc, e );
return;
}
case (int)TK.NAME: {
SingleVar( e );
return;
}
default: {
Lexer.SyntaxError("unexpected symbol");
return;
}
}
}
// suffixedexp -> primaryexp { '.' NAME | '[' exp ']' | ':' NAME funcargs | funcargs
private void SuffixedExp( ExpDesc e )
{
var fs = CurFunc;
int line = Lexer.LineNumber;
PrimaryExp( e );
for(;;)
{
switch( Lexer.Token.TokenType )
{
case (int)'.': { // fieldsel
FieldSel( e );
break;
}
case (int)'[': { // `[' exp1 `]'
var key = new ExpDesc();
Coder.Exp2AnyRegUp( fs, e );
YIndex( key );
Coder.Indexed( fs, e, key );
break;
}
case (int)':': { // `:' NAME funcargs
var key = new ExpDesc();
Lexer.Next();
CodeString( key, CheckName() );
Coder.Self( fs, e, key );
FuncArgs( e, line );
break;
}
case (int)'(':
case (int)TK.STRING:
case (int)'{': { // funcargs
Coder.Exp2NextReg( CurFunc, e );
FuncArgs( e, line );
break;
}
default: return;
}
}
}
private void SimpleExp( ExpDesc e )
{
var t = Lexer.Token;
switch( t.TokenType )
{
case (int)TK.NUMBER: {
var nt = t as NumberToken;
InitExp( e, ExpKind.VKNUM, 0 );
e.NumberValue = nt.SemInfo;
break;
}
case (int)TK.STRING: {
var st = t as StringToken;
CodeString( e, st.SemInfo );
break;
}
case (int)TK.NIL: {
InitExp( e, ExpKind.VNIL, 0 );
break;
}
case (int)TK.TRUE: {
InitExp( e, ExpKind.VTRUE, 0 );
break;
}
case (int)TK.FALSE: {
InitExp( e, ExpKind.VFALSE, 0 );
break;
}
case (int)TK.DOTS: {
CheckCondition( CurFunc.Proto.IsVarArg,
"cannot use '...' outside a vararg function" );
InitExp( e, ExpKind.VVARARG,
Coder.CodeABC( CurFunc, OpCode.OP_VARARG, 0, 1, 0 ) );
break;
}
case (int)'{': {
Constructor( e );
return;
}
case (int)TK.FUNCTION: {
Lexer.Next();
Body( e, false, Lexer.LineNumber );
return;
}
default: {
SuffixedExp( e );
return;
}
}
Lexer.Next();
}
private int SearchUpvalues( FuncState fs, string name )
{
var upvalues = fs.Proto.Upvalues;
for( int i=0; i< upvalues.Count; ++i )
{
if( upvalues[i].Name == name )
return i;
}
return -1;
}
private int NewUpvalue( FuncState fs, string name, ExpDesc e )
{
var f = fs.Proto;
int idx = f.Upvalues.Count;
var upval = new UpvalDesc();
upval.InStack = (e.Kind == ExpKind.VLOCAL);
upval.Index = e.Info;
upval.Name = name;
f.Upvalues.Add( upval );
return idx;
}
private void CodeString( ExpDesc e, string s )
{
InitExp( e, ExpKind.VK, Coder.StringK( CurFunc, s) );
}
private void InitExp( ExpDesc e, ExpKind k, int i )
{
e.Kind = k;
e.Info = i;
e.ExitTrue = Coder.NO_JUMP;
e.ExitFalse = Coder.NO_JUMP;
}
}
}