mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-16 01:49:54 +00:00
3f1aeb3aef
git-svn-id: https://openmw.svn.sourceforge.net/svnroot/openmw/trunk@61 ea6a568a-9f4f-0410-981a-c910a81bb256
311 lines
7.7 KiB
D
311 lines
7.7 KiB
D
/*
|
|
Monster - an advanced game scripting language
|
|
Copyright (C) 2007, 2008 Nicolay Korslund
|
|
Email: <korslund@gmail.com>
|
|
WWW: http://monster.snaptoad.com/
|
|
|
|
This file (variables.d) is part of the Monster script language
|
|
package.
|
|
|
|
Monster is distributed as free software: you can redistribute it
|
|
and/or modify it under the terms of the GNU General Public License
|
|
version 3, as published by the Free Software Foundation.
|
|
|
|
This program 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
|
|
General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
version 3 along with this program. If not, see
|
|
http://www.gnu.org/licenses/ .
|
|
|
|
*/
|
|
|
|
module monster.compiler.variables;
|
|
|
|
import monster.compiler.types;
|
|
import monster.compiler.tokenizer;
|
|
import monster.compiler.expression;
|
|
import monster.compiler.scopes;
|
|
import monster.compiler.block;
|
|
|
|
import monster.minibos.string;
|
|
import monster.minibos.stdio;
|
|
import monster.vm.error;
|
|
|
|
enum VarType
|
|
{
|
|
Class,
|
|
Param,
|
|
Local,
|
|
}
|
|
|
|
struct Variable
|
|
{
|
|
Type type;
|
|
VarType vtype;
|
|
Token name;
|
|
|
|
VarScope sc; // Scope that owns this variable
|
|
|
|
int number; // Index used in bytecode to reference this variable
|
|
|
|
bool isRef; // Is this a reference variable?
|
|
bool isConst; // Used for function parameters
|
|
bool isVararg; // A vararg function parameter
|
|
}
|
|
|
|
// Variable declaration. Handles local and class variables, function
|
|
// parameters and loop variables.
|
|
class VarDeclaration : Block
|
|
{
|
|
Variable *var;
|
|
|
|
// Initializer expression, if any. Eg:
|
|
// int i = 3; => init = 3.
|
|
Expression init;
|
|
|
|
bool allowRef; // Allows reference variable.
|
|
bool allowNoType; // Allow no type to be specified (used in foreach)
|
|
bool allowConst; // Allows const.
|
|
|
|
this() {}
|
|
|
|
// Used when the type is already given, and we only need to read the
|
|
// name and what follows. This is used for multiple declarations,
|
|
// ie. int i=1, j=2; and will possibly also be used in other places
|
|
// later.
|
|
this(Type type)
|
|
{
|
|
assert(var is null);
|
|
var = new Variable;
|
|
|
|
var.type = type;
|
|
}
|
|
|
|
// Parse keywords allowed on variables
|
|
private void parseKeywords(ref TokenArray toks)
|
|
{
|
|
Floc loc;
|
|
while(1)
|
|
{
|
|
if(isNext(toks, TT.Ref, loc))
|
|
{
|
|
if(var.isRef)
|
|
fail("Multiple token 'ref' in variable declaration",
|
|
loc);
|
|
if(!allowRef)
|
|
fail("You cannot use 'ref' variables here", loc);
|
|
var.isRef = true;
|
|
continue;
|
|
}
|
|
if(isNext(toks, TT.Const, loc))
|
|
{
|
|
if(var.isConst)
|
|
fail("Multiple token 'const' in variable declaration",
|
|
loc);
|
|
var.isConst = true;
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Parse a series of array specifiers, ie.
|
|
// []
|
|
// [][]...
|
|
// [expr1][expr2]....
|
|
// If takeExpr = false then the last form is not allowed
|
|
static ExprArray getArray(ref TokenArray toks, bool takeExpr = false)
|
|
{
|
|
// Arrays?
|
|
ExprArray arrayArgs;
|
|
while(isNext(toks,TT.LeftSquare))
|
|
{
|
|
Expression expr = null;
|
|
|
|
// Is there an expression inside the brackets?
|
|
if(!isNext(toks, TT.RightSquare))
|
|
{
|
|
Floc loc = getLoc(toks);
|
|
|
|
expr = Expression.identify(toks);
|
|
|
|
if(!takeExpr)
|
|
fail("Array expression [" ~ expr.toString ~
|
|
"] not allowed here", loc);
|
|
|
|
if(!isNext(toks, TT.RightSquare))
|
|
fail("Expected matching ]", toks);
|
|
}
|
|
|
|
// Insert the expression (or a null if the brackets were
|
|
// empty)
|
|
arrayArgs ~= expr;
|
|
}
|
|
return arrayArgs;
|
|
}
|
|
|
|
// Get the total number of array dimensions. Eg. int[] j[][]; has a
|
|
// total of three dimensions. This is now handled entirely by the
|
|
// Type class so this function is here for backwards compatability.
|
|
int arrays()
|
|
{
|
|
return var.type.arrays;
|
|
}
|
|
|
|
// This is slightly messy. But what I'm trying to do IS slightly
|
|
// messy.
|
|
static bool hasType(TokenArray toks)
|
|
{
|
|
// Remove the type, if any
|
|
if(!Type.canParseRem(toks)) return false;
|
|
|
|
// Skip any keywords
|
|
while(1)
|
|
{
|
|
if(isNext(toks, TT.Ref)) continue;
|
|
break;
|
|
}
|
|
|
|
// There must be a variable identifier at the end
|
|
return isNext(toks, TT.Identifier);
|
|
}
|
|
|
|
override void parse(ref TokenArray toks)
|
|
{
|
|
if(var is null)
|
|
{
|
|
var = new Variable;
|
|
|
|
// Keywords may come before or after the type
|
|
parseKeywords(toks);
|
|
|
|
// Parse the type, if any.
|
|
if(!allowNoType || hasType(toks))
|
|
{
|
|
var.type = Type.identify(toks);
|
|
|
|
parseKeywords(toks);
|
|
}
|
|
}
|
|
// The type was already set externally.
|
|
else
|
|
{
|
|
assert(var.type !is null);
|
|
|
|
// allowNoType is not used in these cases
|
|
assert(allowNoType == false);
|
|
}
|
|
|
|
if(!isNext(toks, TT.Identifier, var.name))
|
|
fail("Variable name must be identifier", toks);
|
|
|
|
loc = var.name.loc;
|
|
|
|
// Look for arrays after the variable name.
|
|
ExprArray arrayArgs = getArray(toks);
|
|
|
|
/* We must append these arrays to the type, in the reverse
|
|
order. Eg.
|
|
|
|
int[1][2] i[4][3];
|
|
|
|
is the same as
|
|
|
|
int[1][2][3][4] i;
|
|
|
|
(also the same as int i[4][3][2][1];)
|
|
|
|
Since we don't take expressions here yet the order is
|
|
irrelevant, but let's set it up right.
|
|
*/
|
|
foreach_reverse(e; arrayArgs)
|
|
{
|
|
assert(e is null);
|
|
var.type = new ArrayType(var.type);
|
|
}
|
|
|
|
// Does the variable have an initializer?
|
|
if(isNext(toks, TT.Equals))
|
|
init = Expression.identify(toks);
|
|
}
|
|
|
|
char[] toString()
|
|
{
|
|
char[] res = var.type.toString() ~ " " ~ var.name.str;
|
|
if(init !is null)
|
|
res ~= " = " ~ init.toString;
|
|
return res;
|
|
}
|
|
|
|
// Special version used for explicitly numbering function
|
|
// parameters. Only called from FuncDeclaration.resolve()
|
|
void resolve(Scope sc, int num)
|
|
{
|
|
assert(num<0, "VarDec.resolve was given a positive num: " ~ .toString(num));
|
|
var.number = num;
|
|
resolve(sc);
|
|
}
|
|
|
|
// Calls resolve() for all sub-expressions
|
|
override void resolve(Scope sc)
|
|
{
|
|
var.type.resolve(sc);
|
|
|
|
if(!allowConst && var.isConst)
|
|
fail("'const' is not allowed here", loc);
|
|
|
|
// Store the scope in the var struct for later referral.
|
|
var.sc = cast(VarScope)sc;
|
|
assert(var.sc !is null, "variables can only be declared in VarScopes");
|
|
|
|
if(var.number == 0)
|
|
{
|
|
// If 'number' has not been set at this point (ie. we are
|
|
// not a function parameter), we must get it from the scope.
|
|
if(sc.isClass())
|
|
// Class variable. Get a position in the data segment.
|
|
var.number = sc.addNewDataVar(var.type.getSize());
|
|
else
|
|
// We're a local variable. Ask the scope what number we
|
|
// should have.
|
|
var.number = sc.addNewLocalVar(var.type.getSize());
|
|
}
|
|
else assert(sc.isFunc());
|
|
|
|
if(init !is null)
|
|
{
|
|
init.resolve(sc);
|
|
|
|
// Convert type, if necessary.
|
|
try var.type.typeCast(init);
|
|
catch(TypeException)
|
|
fail(format("Cannot initialize %s of type %s with %s of type %s",
|
|
var.name.str, var.type,
|
|
init, init.type), loc);
|
|
assert(init.type == var.type);
|
|
}
|
|
|
|
// Insert ourselves into the scope.
|
|
sc.insertVar(var);
|
|
}
|
|
|
|
// Executed for local variables upon declaration. Push the variable
|
|
// on the stack.
|
|
void compile()
|
|
{
|
|
// Validate the type
|
|
var.type.validate();
|
|
|
|
setLine();
|
|
|
|
if(init !is null)
|
|
// Push the initializer
|
|
init.eval();
|
|
else
|
|
// Default initializer
|
|
var.type.pushInit();
|
|
}
|
|
}
|