Source: parser/operandparser.js

/** A parser for MIPS operands
 * @constructor
 * @param {Parser.TokenStream}  tokenStream The stream of tokens to parse
 * @param {array} symbols An array of pre-defined symbols
 */
Parser.OperandParser = function(tokenStream, symbols) {
    this.tokenStream = tokenStream;
    this.exprParser = new Parser.ExprParser(tokenStream, symbols);
    this.mipsEmulator = new MipsEmulator();

    /** Parse a register operand
     * @returns {string}  The name of the register
     * @throws {Parser.ParseError} The input must start with a register
     */
    this.parseRegister = function() {
        let token = this.tokenStream.consume(Parser.TokenType.Register);
        if (this.mipsEmulator.isValidRegister(token.value)) {
            return token.value;
        } else {
            throw new Parser.ParseError('Not a valid register', token);
        }
    }

    /** Parse a writable register operand
     * 
     * Like {@link Parser.OperandParser.parseRegister}, but the register must be writable.
     * 
     * @returns {string}  The name of the register
     * @throws {Parser.ParseError} The input must start with a writable register
     */
    this.parseWritableRegister = function() {
        let token = this.tokenStream.consume(Parser.TokenType.Register);
        if (this.mipsEmulator.isValidWritableRegister(token.value)) {
            return token.value;
        } else {
            throw new Parser.ParseError('Not a writable register', token);
        }
    }

    /** Parse a label
     * 
     * A label is a simple identifier.
     * 
     * @returns {string}  The name of the label
     * @throws {Parser.ParseError} The input must start with a label
     */
    this.parseLabel = function() {
        let token = this.tokenStream.consume(Parser.TokenType.Identifier);
        return token.value;
    }

    /** Parse a signed constant expression
     * Parses a constant expression and checks whether its value is representable as a
     * two's-complement integer with the given number of bits.
     * 
     * @param {number} bits  The number of bits available (including sign bit) for the number
     * @returns {number}  The value of the constant
     * @throws {Parser.ParseError} The value must be representable as a two's-complement integer with the given number of bits
     */
    this.parseSignedConstant = function(bits) {
        let value = this.exprParser.parseExpression();
        let minValue = MIPS.minSignedValue(bits);
        let maxValue = MIPS.maxSignedValue(bits);
        if (minValue <= value && value <= maxValue) {
            return value;
        } else {
            throw new Parser.ParseError(value+' cannot be expressed as '+bits+'-bit signed value');
        }
    }

    /** Parse an unsigned constant expression
     * Parses a constant expression and checks whether its value is representable as an
     * unsigned integer with the given number of bits.
     * 
     * @param {number} bits  The number of bits available for the number
     * @returns {number}  The value of the constant
     * @throws {Parser.ParseError} The value must be representable as an unsigned integer with the given number of bits
     */
    this.parseUnsignedConstant = function(bits) {
        let value = this.exprParser.parseExpression();
        let minValue = MIPS.minUnsignedValue(bits);
        let maxValue = MIPS.maxUnsignedValue(bits);
        if (minValue <= value && value <= maxValue) {
            return value;
        } else {
            throw new Parser.ParseError(value+' cannot be expressed as '+bits+'-bit unsigned value');
        }
    }

    /** Parse a constant expression
     * Parses a constant expression and checks whether its value is representable as either an
     * unsigned or a two's complement integer with the given number of bits.
     * 
     * @param {number} bits  The number of bits available for the number
     * @returns {number}  The value of the constant
     * @throws {Parser.ParseError} The value must be representable as an unsigned or two's complement integer with the given number of bits
     */
    this.parseConstant = function(bits) {
        let value = this.exprParser.parseExpression();
        let minValue = MIPS.minSignedValue(bits);
        let maxValue = MIPS.maxUnsignedValue(bits);
        if (minValue <= value && value <= maxValue) {
            return value;
        } else {
            throw new Parser.ParseError(value+' cannot be expressed as '+bits+'-bit value');
        }
    }

    /** Parse an address for loading or storing.
     * A load/store-address consists of an (optional) signed offset and a register in parentheses (`imm($rs)`).
     * 
     * @param {number} bits  The number of bits allowed for the offset
     * @return {Object} A dictionary containing the offset in key `imm` and the register name in key  `$rs`.
     */
    this.parseLoadStoreAddress = function(bits) {
        let that = this;
        let imm = this.tokenStream.tryParsing(
            function() { return that.parseSignedConstant(bits); },
            0
        );
        let reg = this.tokenStream.tryParsing(
            function() {
                that.tokenStream.consume(Parser.TokenType.LParen);
                let reg = that.parseRegister();
                that.tokenStream.consume(Parser.TokenType.RParen);
                return reg;
            },
            '$zero'
        );
        return {
            'imm': imm,
            '$rs': reg
        };
}
}

/** Create an {@link Parser.OperandParser} from a string
 * @param {string}  input The string to parse
 * @returns {Parser.OperandParser} The operand parser for parsing the string
 */
Parser.operandParserFromString = function(text) {
    let tokenStream = Parser.tokenStreamFromString(text);
    return new Parser.OperandParser(tokenStream);
}