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); }
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); }
Value Label::Evaluate(SymbolTable* scope, EvalContext &context, bool asbool) { // The value of a label expression is an empty string // containing an anchor. The anchor was also registered // in the current scope in the PreTypecheck phase, so // other expressions can refer to this anchor. String* value = new String(); Anchor* theAnchor = scope->LookupAnchor(name); if(!theAnchor) { Error("label evaluation lookup failed for '" + name + "' - probable internal compiler error!"); return Value(); } value->AddAnchor( theAnchor ); return Value(value); }
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); }
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); }