/**
   * Class constructor.
   *
   * @param returns the number of values to return
   */
  public StrCpyInstruction(int returns) {
    if (PageExInfo.in())
      throw new NslContextException(
          EnumSet.of(NslContext.Section, NslContext.Function, NslContext.Global), name);
    if (returns != 1) throw new NslReturnValueException(name, 1);

    ArrayList<Expression> paramsList = Expression.matchList();
    int paramsCount = paramsList.size();
    if (paramsCount < 1 || paramsCount > 3) throw new NslArgumentException(name, 1, 3);

    this.string = paramsList.get(0);

    if (paramsCount > 1) {
      this.maxLen = paramsList.get(1);

      if (paramsCount > 2) {
        this.startOffset = paramsList.get(2);
      } else {
        this.startOffset = null;
      }
    } else {
      this.maxLen = null;
      this.startOffset = null;
    }
  }
  /** Class constructor. */
  public SwitchStatement() {
    if (!SectionInfo.in() && !FunctionInfo.in())
      throw new NslContextException(EnumSet.of(NslContext.Section, NslContext.Function), "switch");

    int lineNo = ScriptParser.tokenizer.lineno();
    ScriptParser.tokenizer.matchOrDie('(');
    this.switchExpression = Expression.matchComplex();
    ScriptParser.tokenizer.matchOrDie(')');
    ScriptParser.tokenizer.matchOrDie('{');

    // Set non-null values so that the block statement can contain break statements.
    CodeInfo.getCurrent().setBreakLabel(RelativeJump.Zero);

    this.statementList = new ArrayList<Statement>();
    this.casesList = new ArrayList<SwitchCaseStatement>();
    this.defaultCase = null;

    // Get the statements including case statements.
    while (true) {
      if (ScriptParser.tokenizer.match("case")) {
        Statement statement = new SwitchCaseStatement();
        if (this.defaultCase != null)
          throw new NslException(
              "The \"default\" case in a \"switch\" statement must be the last case", true);
        this.casesList.add((SwitchCaseStatement) statement);
        this.statementList.add(statement);
      } else if (ScriptParser.tokenizer.match("default")) {
        this.defaultCase = new SwitchDefaultCaseStatement();
        this.statementList.add(this.defaultCase);
      } else {
        Statement statement = Statement.match();
        if (statement == null) break;
        this.statementList.add(statement);
      }
    }

    // No cases?
    if (this.casesList.isEmpty())
      throw new NslException(
          "A \"switch\" statement must have at least one \"case\" statement", true);

    // Validate switch cases for jump instructions.
    if (this.switchExpression instanceof JumpExpression)
      ((JumpExpression) this.switchExpression).checkSwitchCases(this.casesList, lineNo);

    // Check the last statement is a break statement.
    boolean noBreak = true;
    if (!this.statementList.isEmpty()) {
      Statement last = this.statementList.get(this.statementList.size() - 1);
      if (last instanceof BlockStatement) last = ((BlockStatement) last).getLast();
      if (last instanceof BreakStatement) noBreak = false;
    }
    if (noBreak)
      throw new NslException("A \"switch\" statement must end with a \"break\" statement", true);

    CodeInfo.getCurrent().setBreakLabel(null);

    ScriptParser.tokenizer.matchOrDie('}');
  }
  /**
   * Class constructor.
   *
   * @param returns the number of values to return
   */
  public GetErrorLevelInstruction(int returns) {
    if (!SectionInfo.in() && !FunctionInfo.in())
      throw new NslContextException(EnumSet.of(NslContext.Section, NslContext.Function), name);
    if (returns != 1) throw new NslReturnValueException(name, 1);

    ArrayList<Expression> paramsList = Expression.matchList();
    if (!paramsList.isEmpty()) throw new NslArgumentException(name, 0);
  }
  /**
   * Class constructor.
   *
   * @param returns the number of values to return
   */
  public FlushINIInstruction(int returns) {
    if (!SectionInfo.in() && !FunctionInfo.in())
      throw new NslContextException(EnumSet.of(NslContext.Section, NslContext.Function), name);
    if (returns > 0) throw new NslReturnValueException(name);

    ArrayList<Expression> paramsList = Expression.matchList();
    if (paramsList.size() != 1) throw new NslArgumentException(name, 1);

    this.iniFile = paramsList.get(0);
  }
  /**
   * Class constructor.
   *
   * @param returns the number of values to return
   */
  public LockWindowInstruction(int returns) {
    if (!SectionInfo.in() && !FunctionInfo.in())
      throw new NslContextException(EnumSet.of(NslContext.Section, NslContext.Function), name);
    if (returns > 0) throw new NslReturnValueException(name);

    ArrayList<Expression> paramsList = Expression.matchList();
    if (paramsList.size() != 1) throw new NslArgumentException(name, 1);

    this.value = paramsList.get(0);
    if (!ExpressionType.isString(this.value))
      throw new NslArgumentException(name, 1, ExpressionType.String);
  }
 /**
  * Assembles the source code.
  *
  * @param var the variable to assign the value to
  */
 @Override
 public void assemble(Register var) throws IOException {
   Expression varOrString = AssembleExpression.getRegisterOrExpression(this.string);
   if (this.maxLen != null) {
     Expression varOrMaxLen = AssembleExpression.getRegisterOrExpression(this.maxLen);
     if (this.startOffset != null) {
       Expression varOrStartOffset = AssembleExpression.getRegisterOrExpression(this.startOffset);
       ScriptParser.writeLine(
           name + " " + var + " " + varOrString + " " + varOrMaxLen + " " + varOrStartOffset);
       varOrStartOffset.setInUse(false);
     } else {
       ScriptParser.writeLine(name + " " + var + " " + varOrString + " " + varOrMaxLen);
     }
     varOrMaxLen.setInUse(false);
   } else {
     ScriptParser.writeLine(name + " " + var + " " + varOrString);
   }
   varOrString.setInUse(false);
 }
 /** Assembles the source code. */
 @Override
 public void assemble() throws IOException {
   Expression varOrIniFile = AssembleExpression.getRegisterOrExpression(this.iniFile);
   ScriptParser.writeLine(name + " " + varOrIniFile);
   varOrIniFile.setInUse(false);
 }
  /**
   * Class constructor.
   *
   * @param returns the number of values to return
   */
  public CreateFontInstruction(int returns) {
    if (PageExInfo.in())
      throw new NslContextException(
          EnumSet.of(NslContext.Section, NslContext.Function, NslContext.Global), name);
    if (returns != 1) throw new NslReturnValueException(name);

    ArrayList<Expression> paramsList = Expression.matchList();
    int paramsCount = paramsList.size();
    if (paramsCount < 1 || paramsCount > 6) throw new NslArgumentException(name, 1, 6);

    this.fontFace = paramsList.get(0);
    if (!ExpressionType.isString(this.fontFace))
      throw new NslArgumentException(name, 1, ExpressionType.String);

    if (paramsCount > 1) {
      this.height = paramsList.get(1);
      if (!ExpressionType.isInteger(this.height))
        throw new NslArgumentException(name, 2, ExpressionType.Integer);

      if (paramsCount > 2) {
        this.weight = paramsList.get(2);
        if (!ExpressionType.isInteger(this.weight))
          throw new NslArgumentException(name, 3, ExpressionType.Integer);

        if (paramsCount > 3) {
          this.italic = paramsList.get(3);
          if (!ExpressionType.isBoolean(this.italic))
            throw new NslArgumentException(name, 4, ExpressionType.Boolean);

          if (paramsCount > 4) {
            this.underline = paramsList.get(4);
            if (!ExpressionType.isBoolean(this.underline))
              throw new NslArgumentException(name, 5, ExpressionType.Boolean);

            if (paramsCount > 5) {
              this.strike = paramsList.get(5);
              if (!ExpressionType.isBoolean(this.strike))
                throw new NslArgumentException(name, 6, ExpressionType.Boolean);
            } else {
              this.strike = null;
            }
          } else {
            this.underline = null;
            this.strike = null;
          }
        } else {
          this.italic = null;
          this.underline = null;
          this.strike = null;
        }
      } else {
        this.weight = null;
        this.italic = null;
        this.underline = null;
        this.strike = null;
      }
    } else {
      this.height = null;
      this.weight = null;
      this.italic = null;
      this.underline = null;
      this.strike = null;
    }
  }
  /**
   * Assembles the source code.
   *
   * @throws IOException
   */
  @Override
  public void assemble() throws IOException {
    // Do not assemble anything if there are no cases!
    if (this.casesList.isEmpty()) return;

    // Give each case a label.
    for (SwitchCaseStatement statement : this.casesList)
      statement.setLabel(LabelList.getCurrent().getNext());
    if (this.defaultCase != null) this.defaultCase.setLabel(LabelList.getCurrent().getNext());

    Label gotoEnd = LabelList.getCurrent().getNext();
    Label gotoStart = LabelList.getCurrent().getNext();

    // Go to the jump table which is assembled after the switch case labels and
    // statements.
    ScriptParser.writeLine("Goto " + gotoStart);

    // Using "break;" inside a switch jumps to the end.
    Label parentBreak = CodeInfo.getCurrent().setBreakLabel(gotoEnd);

    // Assemble all the statements inside the switch { }. This includes the
    // case labels.
    for (Statement statement : this.statementList) statement.assemble();

    // Restore the parent break label.
    CodeInfo.getCurrent().setBreakLabel(parentBreak);

    // Label at the top of the jump table.
    gotoStart.write();

    // Jump instructions can jump directly to the case labels.
    if (this.switchExpression instanceof JumpExpression) {
      ((JumpExpression) this.switchExpression).assemble(this.casesList);
    }
    // Other expressions we just assemble them if required and compare their
    // result.
    else {
      Expression varOrSwitchExpression =
          AssembleExpression.getRegisterOrExpression(this.switchExpression);
      for (SwitchCaseStatement caseStatement : this.casesList) {
        // Type is an integer; use IntCmp.
        if (caseStatement.getMatch().getType().equals(ExpressionType.Integer)) {
          ScriptParser.writeLine(
              String.format(
                  "IntCmp %s %s %s",
                  varOrSwitchExpression, caseStatement.getMatch(), caseStatement.getLabel()));
        }
        // Type is a string; use StrCmp or StrCmpS (if special `` quotes were
        // used).
        else {
          ScriptParser.writeLine(
              String.format(
                  "StrCmp%s %s %s %s",
                  caseStatement.getMatch().getType().equals(ExpressionType.StringSpecial)
                      ? "S"
                      : "",
                  varOrSwitchExpression,
                  caseStatement.getMatch(),
                  caseStatement.getLabel()));
        }
      }
      varOrSwitchExpression.setInUse(false);
    }

    // Default case jump.
    if (this.defaultCase != null) ScriptParser.writeLine("Goto " + this.defaultCase.getLabel());

    gotoEnd.write();
  }