Module:NumberParser

From IdleOn MMO Wiki
Revision as of 00:52, 12 March 2024 by BHY4A (talk | contribs) (Reverted edits by BHY4A (talk) to last revision by Kiokurashi)

This module parses numbers expressed in different notations and returns a string representation of the number in standard format. This is a helper module that is intended to be used by other modules, rather than called by templates.

Usage

-- First import the module at the start of your module.
local NumberParser = require('Module:NumberParser')

-- Call the parse function with your input.
local input = '5e6'
local output = NumberParser.parse(input)

Supported Notations

Notation Examples Notes
Standard Notation 5, 37, 1000000000
Exponential Notation 3e17, 2.5e+5, 7.e15, .8e+22
  • Exponent is required
  • Coefficient must have the integer part, the decimal part, or both.
  • Both e and e+ are allowed.
Unit Notation 14K, 3M, 1.5Q
  • Coefficient must have the integer part, decimal part, or both.

Supported Units

  • K: 1,000
  • M: 1,000,000
  • B: 1,000,000,000
  • T: 1,000,000,000,000
  • Q: 1,000,000,000,000,000

Invalid Numbers

Negative numbers and decimal numbers are not supported.


require('strict')

local p = {}

--- Maps fixed unit suffixes to their exponents. Keys must be lowercase as
--- parsed strings are converted to lowercase to simplify the pattern matching
--- logic.
local UNIT_EXPONENTS = {
    k = 3,
    m = 6,
    b = 9,
    t = 12,
    q = 15,
    qt = 18
}

--- Parses a value that is exponential.
--- @param base string? The integer part of the coefficient or nil.
--- @param fraction string? The fraction part of the coefficient, or nil.
--- @param exponent string|number? The exponent.
--- @return string? result The string representation of the parsed integer.
local function parse_exponential(base, fraction, exponent)
    -- Both the exponent and coefficient are required.
    if exponent == nil or (string.len(base or '') == 0 and string.len(fraction or '') == 0) then
        return nil
    end

    -- Conversion needed because we need to perform some arithmetic with exponent.
    exponent = tonumber(exponent)
    if exponent == nil then
        return nil
    end

    -- The parsed result cannot have a decimal, so make sure the exponent large
    -- enough to account for the fraction part of the coefficient.
    local fraction_exponent = fraction ~= nil and fraction:len() or 0
    if exponent < fraction_exponent then
        return nil
    end

    local result = base

    -- Append the fraction if one was present.
    if fraction ~= nil then
        result = result .. fraction
        exponent = exponent - fraction_exponent
    end

    -- Append zeroes in place of the exponent.
    if exponent > 0 then
        result = result .. string.rep('0', exponent)
    end

    return result
end

--- Parses a string representation of a number in any supported format.
--- @param value string The string representation of the number.
--- @return string? result The string reprentation of the number in standard notation, or nil if the string is invalid.
function p.parse(value)
    -- Convert the value to lowercase and trim whitespace to simplify patterns.
    value = mw.text.trim(value:lower())
	
    -- The value is already in standard notation.
    if value:find('^%d+$') then
        return value
    end

    -- Check exponential notation (1e20, 2.5e15, ...)
    local base, fraction, exponent = value:match('^(%d*)%.?(%d*)e%+*(%d+)$')
    if exponent ~= nil then
        return parse_exponential(base, fraction, exponent)
    end

    -- Check unit notation (3K, 140M, 75Q, ...)
    local base, fraction, unit = value:match('^(%d*)%.?(%d*)(%a)$')
    if unit ~= nil then
        local exponent = UNIT_EXPONENTS[unit]
        if exponent ~= nil then
            return parse_exponential(base, fraction, exponent)
        end
    end

    -- Number didn't match any known notations, so return nil so the client can
    -- decide how to procede.
    return nil
end

function  p.parseframe(frame)
	return parse(tostring(frame.args[1] or ""))
end

return p