Esempio n. 1
0
Value OrExpr::Evaluate(SymbolTable *scope, EvalContext& context, bool asbool)
{
	// Scope override; must do this for every expression that might contain an identifier
	if(this->scope != NULL)
		scope = this->scope;

	// Lowering of A or B:
	//  [A]
	//  [iftrue goto end]
	//  [B]
	//  label end:
	String* value = new String();

	string labelbase = context.GetUniqueLabelName();
	Anchor* endanchor = new Anchor(labelbase + ".end");

	// a
	value->Append( a->Evaluate(scope, context, true).ToCodeString() );
	// iftrue goto end:
	value->Code("1B 03 FF FF FF FF");
	value->AddReference(value->GetPos()-4, endanchor);
	// b
	value->Append( b->Evaluate(scope, context, true).ToCodeString() );
	// end:
	value->AddAnchor(endanchor);

	return Value(value);
}
Esempio n. 2
0
Value AndExpr::Evaluate(SymbolTable *scope, EvalContext& context, bool asbool)
{
	// Scope override; must do this for every expression that might contain an identifier
	if(this->scope != NULL)
		scope = this->scope;

	// Lowering A and B:
	//  [A]
	//  [iffalse goto end]
	//  [B]
	//  label end:

	String* value = new String();

	// Create internal label
	string labelbase = context.GetUniqueLabelName();
	Anchor* endanchor = new Anchor(labelbase + ".end");

	// Evaluate the first operand
	value->Append( a->Evaluate(scope, context, true).ToCodeString() );

	// Add a jump to the end if the first operand is false
	value->Code("1B 02 FF FF FF FF");
	value->AddReference(value->GetPos()-4, endanchor);

	// TODO:
	//  Hm. I just realized that some boolean expressions (and and or) rely on reference
	//  resolution to operate correctly. Thus, it doesn't make sense to use them in ROM
	//  write statements at the moment, because ROM write statements occur after normal
	//  resolution, but without doing any resolution themselves. Perhaps ROM write statements
	//  should have a special resolution step to take care of stuff like this. (Perhaps
	//  the ROM data itself should be represented as a ByteChunk, with refs?)
	//  Anyway, don't worry about this for now, since using boolean expressions in a ROM
	//  write statement is not a very likely usage scenario.
	// UPDATE: 11/11/2008
	//  This issue has been fixed by moving the evaluation of ROM write subexpressions
	//  back to the evaluation pass of the compiler, and simply caching the results and
	//  resolving references in a later pass. However, it still might be worthwhile to
	//  consider alternative solutions; whole-ROM resolution seems interesting for example.

	// Evaluate the second operand
	value->Append( b->Evaluate(scope, context, true).ToCodeString() );

	// Set the position of the end label
	value->AddAnchor(endanchor);

	return Value(value);
}
Esempio n. 3
0
Value MenuExpr::Evaluate(SymbolTable* scope, EvalContext& context, bool asbool)
{
	if(this->scope != NULL)
		scope = this->scope;

	// Lowering a menu statement:
	// [19 02][option][02] - for each option
	// [1C 0C $cols][11][12]
	// [09 $num (statementjmps)]
	// [goto end]
	// [statement][goto end] - for each statement
	// label end:

	String* value = new String();

	// Create internal labels
	vector<Anchor*> anchors;
	string labelbase = context.GetUniqueLabelName();

	for(unsigned int i = 0; i < options.size(); ++i) {
		std::stringstream ss;
		ss << ".opt" << i;
		Anchor* a = new Anchor(labelbase + ss.str());
		anchors.push_back(a);
	}
	Anchor* endanchor = new Anchor(labelbase + ".end");

	// First, append the options between [19 02] and [02] codes
	for(unsigned int i = 0; i < options.size(); ++i) {
		value->Code("19 02");
		value->Append( options[i]->Evaluate(scope, context).ToCodeString() );
		value->Code("02");
	}

	// Next, append the option display commands
	// If we're only using two options, and no number of columns was specified,
	// use "1C 07", otherwise, use "1C 0C".
	if(options.size() == 2 && defcolumns)
		value->Code("1C 07");
	else
		value->Code("1C 0C");

	value->Byte(columns);// write exactly one byte for the column count
	value->Code("11 12");

	// Next, the multi-jump code
	value->Code("09");
	value->Byte(results.size());// write exactly one byte for the option count
	for(unsigned int i = 0; i < results.size(); ++i) {
		value->Code("FF FF FF FF");
		value->AddReference(value->GetPos() - 4, anchors[i]);
	}

	// Add a jump to the "default" option after the multi-jump, or end if no default
	value->Code("0A FF FF FF FF");
	if(defaultopt != -1)
		value->AddReference(value->GetPos() - 4, anchors[defaultopt]);
	else
		value->AddReference(value->GetPos() - 4, endanchor);


	// Finally, write out all the options, with a "goto end" after each
	// At each point we set the position of the relevant label.
	for(unsigned int i = 0; i < results.size(); ++i)
	{
		value->AddAnchor(anchors[i]);
		value->Append( results[i]->Evaluate(scope, context).ToCodeString() );

		// Add a "goto end" after every statement, in case it falls through
		value->Code("0A FF FF FF FF");
		value->AddReference(value->GetPos() - 4, endanchor);
	}

	// Last step: set position of the "end" label
	value->AddAnchor(endanchor);

	return Value(value);
}
Esempio n. 4
0
Value IdentExpr::Evaluate(SymbolTable* scope, EvalContext& context, bool asbool)
{
	/// Scope override; must do this for every expression that might contain an identifier
	if(this->scope != NULL)
		scope = this->scope;

	//context.file = this->file;
	//context.line = this->linenumber;

	// To evaluate an identifier expression:
	// First, we have to look up the symbol and check the type of its value.
	// If it's a constant, we just evaluate the constant (provided there are no
	// parameters given; if there are parameters, we should report an error.)
	// If it's a command, we need to:
	//  - bind each argument expression to the corresponding symbol in the commands
	//     parameter list.
	//  - invoke the command.

	Module* module = context.module;
	
	SymbolTable* lookupScope = scope;

	// If the ident expr's "file" field is not empty, we'll look it up in a different module
	if(!file.empty()) {
		Module* mod = module->GetSiblingContext(file);
		if(!mod) {
			Error("reference to nonexistent module '" + file + "'");
			return Value::Null;
		}
		lookupScope = mod->GetRootTable();
	}

	Value found = lookupScope->Lookup(name);

	if(found != Value::Undefined) {
		// In most cases, we just return the value.
		if(found.GetType() != Type::Macro)
		{
			// However, evaluated vars are not importable.
			if(lookupScope != scope) {
				Error("cannot access local variable declaration '" + name + "' in module '" + file + "'");
				return Value::Null;
			}
			return found;
		}

		Node* node = found.GetNode();
		Value result;

		if(node->GetType() == conststmt)
		{
			if(hasparens) {
				Error("'" + GetFullName() + "' refers to a constant; cannot use parentheses");
				return Value();
			}
			result = dynamic_cast<ConstDef*>(node)->EvaluateExpr(scope, context, asbool);
		}
		else if(node->GetType() == commandstmt)
		{
			for(unsigned int i = 0; i < args.size(); ++i)
				args[i]->scope = scope;

			CommandDef* cmd = dynamic_cast<CommandDef*>(found.GetNode());

			if(cmd->GetArgCount() != args.size())
				Error("incorrect number of parameters to command '" + GetFullName() + "'");
			else
				result = cmd->Invoke(context, args);
		}
		else if(node->GetType() == ambiguousid)
		{
			AmbiguousID* ambig = dynamic_cast<AmbiguousID*>(found.GetNode());
			Error(ambig->ToString(""));
			result = Value::Null;
		}
		else if(node->IsExpression())
		{
			result = dynamic_cast<Expression*>(node)->Evaluate(scope, context, asbool);
		}
		else
		{
			Error("invalid type");
		}
		return result;
	}

	// Didn't find it in the symbol table, check the jumps table
	Anchor* foundanchor = lookupScope->LookupAnchor(name);

	if(foundanchor) {
		if(hasparens) {
			Error("'" + GetFullName() + "' refers to a label; cannot use parentheses");
			return Value();
		}

		String* val = new String();
		val->Long(foundanchor->GetTarget());

		// The targeted label might not have its address computed yet, so we register a
		// reference to the target label (unless refs are forbidden by the context)
		if(!context.norefs)
			val->AddReference(val->GetPos()-4, foundanchor);

		return Value(val);
	}

	Error("use of undefined identifier '" + GetFullName() + "'");
	return Value();
}
Esempio n. 5
0
Value IfExpr::Evaluate(SymbolTable *env, EvalContext& context, bool asbool)
{
	if(this->scope != NULL)
		env = this->scope;

	/*
	 * Lowering an if statement:
	 *
	 *  [condition]
	 *  [iffalse goto falselbl]
	 *  [thenstmt]
	 *  [goto endlbl]
	 * falselbl:
	 *  [elsestmt]
	 * endlbl:
	 */

	String* value = new String();
	
	// Create internal labels
	string labelbase = context.GetUniqueLabelName();
	Anchor* endanchor = new Anchor(labelbase + ".end");
	Anchor* falseanchor = new Anchor(labelbase + ".false");

	// First, we evaluate the condition
	Value cond_val = condition->Evaluate(env, context, true);

	// TODO: this might be an opportunity to do some typechecking on the returned value,
	// instead of just converting it to a string. Maybe some warnings would be appropriate?
	// (i.e., conditioning on a number value, which is almost always meaningless)

	// append cond_val to the output:
	value->Append(cond_val.ToCodeString());

	// Then, we output an "iffalse goto false" instruction, and register a jump reference
	value->Code("1B 02 FF FF FF FF");
	value->AddReference(value->GetSize() - 4, falseanchor);

	// Evaluate the "then" statement
	Value then_val = thenexpr->Evaluate(env, context);
	value->Append(then_val.ToCodeString());


	// Add a "goto end"
	// TODO: strictly speaking, we can dispense with this last goto when
	// there is no 'else' clause. We'll leave it here for now until we
	// get the first round of regression tests in place, and then we'll
	// update it along with the other evaluation refactoring.
	value->Code("0A FF FF FF FF");
	value->AddReference(value->GetPos() - 4, endanchor);

	// Set the position of the false anchor within the string
	value->AddAnchor(falseanchor);

	// Evaluate the "else" statement
	if(elseexpr) {
		Value else_val = elseexpr->Evaluate(env, context);
		value->Append(else_val.ToCodeString());
	}

	// Set the position of the "end" label
	value->AddAnchor(endanchor);

	return Value(value);
}