예제 #1
0
def parseUnit(units, conversions, tokens, overwrite=False):
    """
	Convert the next series of tokens into a unit
	@param units: a map of unit symbols to unit objects to be modified
	@param conversions: a map of unit symbols to scale factors to be modified
	@param tokens: a list of tokens
	"""
    baseUnitMap = {}

    # Handle base unit
    sym = UC_Utils.parseSymbol(tokens)
    if not overwrite and (sym in units):
        raise UC_Common.FileFormatError(
            f"Duplicate definition of unit '{sym}'")

    # Handle derived unit
    nextToken = UC_Utils.getNextToken(tokens)
    if nextToken == UC_Common.MAP_DELIMITER:
        scaleFactor = UC_Utils.parseFloat(tokens)
        UC_Utils.getNextToken(tokens, UC_Common.SEP_DELIMITER)
        baseUnitMap = parseBaseUnitMap(tokens)
        conversions[sym] = scaleFactor

    # Handle other tokens
    elif nextToken != UC_Common.END_DELIMITER:
        raise UC_Common.FileFormatError(
            f"Expected delimiter; received '{nextToken}'")

    # Create unit
    units[sym] = UC_Unit.Unit(sym, baseUnitMap)
예제 #2
0
def parseBaseUnitMap(tokens):
    """
	Convert the next series of tokens into a map of units to exponents
	@param tokens: a list of tokens
	@return pairs of units and their corresponding exponents
	"""
    baseUnitMap = {}

    baseSym = UC_Utils.parseSymbol(tokens)
    if baseSym not in baseUnitMap: baseUnitMap[baseSym] = 0
    baseUnitMap[baseSym] += UC_Utils.parseInt(tokens)

    while UC_Utils.peekNextToken(tokens) == UC_Common.SEP_DELIMITER:
        UC_Utils.getNextToken(tokens)
        baseSym = UC_Utils.parseSymbol(tokens)
        if baseSym not in baseUnitMap: baseUnitMap[baseSym] = 0
        baseUnitMap[baseSym] += UC_Utils.parseInt(tokens)

    UC_Utils.getNextToken(tokens, UC_Common.END_DELIMITER)
    return baseUnitMap
예제 #3
0
def parsePrefixMapping(prefixes, tokens, base, overwrite=False):
    """
	Convert the next series of tokens into a prefix-exponent pair
	@param prefixes: the prefix-exponent map to modify
	@param tokens: a list of tokens
	@param base: the base for the exponent
	"""
    prefix = UC_Utils.getNextToken(tokens)
    if not overwrite and (prefix in prefixes):
        raise UC_Common.FileFormatError(
            f"Duplicate definition of prefix '{prefix}'")
    prefixes[prefix] = (base, Decimal(UC_Utils.parseInt(tokens)))
예제 #4
0
def parsePrefix(prefixes, tokens, overwrite=False):
    """
	Convert the next series of tokens into prefix-exponent pairs
	@param prefixes: the prefix-exponent map to modify
	@param tokens: a list of tokens
	"""
    base = UC_Utils.parseFloat(tokens)
    UC_Utils.getNextToken(tokens, UC_Common.MAP_DELIMITER)

    parsePrefixMapping(prefixes, tokens, base, overwrite)
    while UC_Utils.peekNextToken(tokens) == UC_Common.SEP_DELIMITER:
        UC_Utils.getNextToken(tokens)
        parsePrefixMapping(prefixes, tokens, base, overwrite)
    UC_Utils.getNextToken(tokens, UC_Common.END_DELIMITER)
예제 #5
0
def test_parser(verbose=False):
    test_result = 0

    # Test token queue
    tokens = ["A1", "B2", "C3"]
    if UC_Utils.getNextToken(tokens) != "A1":
        test_result += test_fail("Failed to get expected symbol", verbose)
    if UC_Utils.getNextToken(tokens) != "B2":
        test_result += test_fail("Failed to get expected symbol", verbose)
    if UC_Utils.getNextToken(tokens) != "C3":
        test_result += test_fail("Failed to get expected symbol", verbose)
    try:
        token = UC_Utils.getNextToken(tokens)
        test_result += test_fail(f"Received unexpected token {token}", verbose)
    except:
        pass

    # Test the parsing of basic datatypes
    res = UC_Utils.parseInt(["2"])
    if res != Decimal("2"):
        test_result += test_fail("Incorrectly parsed int", verbose)
    res = UC_Utils.parseFloat(["2.7"])
    if res != Decimal("2.7"):
        test_result += test_fail("Incorrectly parsed float", verbose)
    res = UC_Utils.parseSymbol(["sym"])
    if res != "sym":
        test_result += test_fail("Incorrectly parsed symbol", verbose)

    # Test the parsing of unit dependencies
    baseUnitMap = UC_FileParser.parseBaseUnitMap(
        ["A", "1", ",", "B", "2", ";"])
    if baseUnitMap["A"] != 1:
        test_result += test_fail("Incorrectly parsed base unit map", verbose)
    if baseUnitMap["B"] != 2:
        test_result += test_fail("Incorrectly parsed base unit map", verbose)
    if len(baseUnitMap) != 2:
        test_result += test_fail("Incorrectly parsed base unit map", verbose)
    try:
        baseUnitMap = UC_FileParser.parseBaseUnitMap(
            ["A", "1", ",", "B", "2", "C"])
        test_result += test_fail(
            "Should fail to parse an incorrectly formatted dependency string",
            verbose)
    except:
        pass

    # Test the parsing of a base unit
    units = {}
    conversions = {}
    UC_FileParser.parseUnit(units, conversions, ["A", ";"])
    if (("A" not in units) or ("A" in conversions) or (len(units) != 1)):
        test_result += test_fail("Incorrectly parsed base unit", verbose)
    try:
        UC_FileParser.parseUnit(["A", "B"])
        test_result += test_fail(
            "Should fail to parse an incorrectly formatted base unit string",
            verbose)
    except:
        pass

    # Test the parsing of a derived unit
    UC_FileParser.parseUnit(
        units, conversions,
        ["H", ":", "12.4", ",", "A", "1", ",", "B", "2", ";"])
    if (("H" not in units) or (units["H"].baseUnits["A"] != 1)
            or (units["H"].baseUnits["B"] != 2)
            or (len(units["H"].baseUnits) != 2)
            or (conversions["H"] != Decimal("12.4")) or (len(units) != 2)):
        test_result += test_fail("Incorrectly parsed derived unit", verbose)
    try:
        UC_FileParser.parseUnit(
            units, conversions,
            ["H", ":", "12.4", ",", "A", "1", ",", "B", "2", "C"])
        test_result += test_fail(
            "Should fail to parse an incorrectly formatted derived unit string",
            verbose)
    except:
        pass
    try:
        UC_FileParser.parseUnit(units, conversions, ["H", ":", "12.4", ";"])
        test_result += test_fail(
            "Should fail to parse an incorrectly formatted derived unit string",
            verbose)
    except:
        pass

    # Test the parsing of a prefix map
    prefixes = {}
    UC_FileParser.parsePrefix(prefixes,
                              ["10", ":", "k", "3", ",", "M", "6", ";"])
    if ((len(prefixes) != 2) or (prefixes["k"] != (10, 3)) or (prefixes["M"] !=
                                                               (10, 6))):
        test_result += test_fail("Incorrectly parsed prefix map", verbose)
    try:
        UC_FileParser.parsePrefix(prefixes,
                                  ["10", ":", "k", "3", ",", "M", "6", "C"])
        test_result += test_fail(
            "Should fail to parse an incorrectly formatted prefix map string",
            verbose)
    except:
        pass
    try:
        UC_FileParser.parsePrefix(prefixes, ["10", ":", ";"])
        test_result += test_fail(
            "Should fail to parse an incorrectly formatted prefix map string",
            verbose)
    except:
        pass

    return test_result
예제 #6
0
def aggregateUnits(tokens):
    """
	Combine tokens which constitute compound units
	@param tokens: a list of tokens
	"""
    aggregatedTokens = []
    unitTokens = []
    parsingExp = 0

    def appendUnitTokens(aggregatedTokens, unitTokens, token=None):
        # Append unit tokens to list of aggregated tokens
        if unitTokens:
            if unitTokens[-1] == UC_Common.OPERATOR_MUL or unitTokens[
                    -1] == UC_Common.OPERATOR_DIV:
                operator = unitTokens.pop()
                aggregatedTokens.append(unitTokens)
                aggregatedTokens.append(operator)
            else:
                aggregatedTokens.append(unitTokens)
        if token is not None:
            # Inject multiplication if needed
            if ((aggregatedTokens)
                    and (not UC_Utils.isSpecialChar(aggregatedTokens[-1]))
                    and (token == UC_Common.BRACKET_OPEN)):
                aggregatedTokens.append(UC_Common.OPERATOR_MUL)
            aggregatedTokens.append(token)
        return []

    def handleParseExpDecrement(tokens, unitTokens, parsingExp):
        # Check if multiplication needs to be injected between adjacent units
        if parsingExp != 1: return parsingExp
        if tokens:
            if tokens[0] == UC_Common.OPERATOR_MUL or tokens[
                    0] == UC_Common.OPERATOR_DIV:
                unitTokens.append(tokens.pop(0))
            elif UC_Utils.isValidSymbol(tokens[0]):
                unitTokens.append(UC_Common.OPERATOR_MUL)
        return 0

    def handleAppendUnitSymbol(tokens, unitTokens, parsingExp):
        if tokens:
            token = tokens[0]
            if token == UC_Common.OPERATOR_EXP:
                unitTokens.append(tokens.pop(0))
                return 1
            elif token == UC_Common.OPERATOR_MUL or token == UC_Common.OPERATOR_DIV:
                unitTokens.append(tokens.pop(0))
            elif UC_Utils.isValidSymbol(token):
                unitTokens.append(UC_Common.OPERATOR_MUL)
        return parsingExp

    while tokens:
        token = UC_Utils.getNextToken(tokens)
        if token == UC_Common.BRACKET_OPEN:
            if parsingExp:
                unitTokens.append(token)
                parsingExp += 1
            else:
                unitTokens = appendUnitTokens(aggregatedTokens, unitTokens,
                                              token)
        elif token == UC_Common.BRACKET_SHUT:
            if parsingExp:
                unitTokens.append(token)
                parsingExp = handleParseExpDecrement(tokens, unitTokens,
                                                     parsingExp - 1)
            else:
                unitTokens = appendUnitTokens(aggregatedTokens, unitTokens,
                                              token)
        elif UC_Utils.isFloat(token):
            if parsingExp:
                if not UC_Utils.isInt(token):
                    raise UC_Common.UnitError(
                        f"Expected int; received '{token}'")
                unitTokens.append(token)
                parsingExp = handleParseExpDecrement(tokens, unitTokens,
                                                     parsingExp)
            else:
                unitTokens = appendUnitTokens(aggregatedTokens, unitTokens,
                                              token)
        elif UC_Utils.isValidSymbol(token):
            if parsingExp:
                raise UC_Common.UnitError(f"Expected int; received '{token}'")
            unitTokens.append(token)
            parsingExp = handleAppendUnitSymbol(tokens, unitTokens, parsingExp)
        elif UC_Utils.isOperator(token):
            if parsingExp:
                raise UC_Common.UnitError(f"Expected int; received '{token}'")
            else:
                unitTokens = appendUnitTokens(aggregatedTokens, unitTokens,
                                              token)
        else:
            raise UC_Common.UnitError(f"Unknown token; received '{token}'")

    appendUnitTokens(aggregatedTokens, unitTokens)
    return aggregatedTokens