Advertisement
1m1m0
Apr 4th, 2025
31
0
Never
This is comment for paste Project Einstein: BHv5 Branch
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. -- MathParserModule.lua
  2.  
  3. local MathParser = {}
  4.  
  5. -- Step 1: Tokenizer
  6. local function tokenize(expr)
  7.     local tokens = {}
  8.     local i = 1
  9.     while i <= #expr do
  10.         local c = expr:sub(i, i)
  11.         if c:match("%s") then
  12.             i = i + 1
  13.         elseif c:match("%d") or c == '.' then
  14.             local num = ""
  15.             -- Capture the base number (integer or float)
  16.             while i <= #expr and expr:sub(i, i):match("[%d%.]") do
  17.                 num = num .. expr:sub(i, i)
  18.                 i = i + 1
  19.             end
  20.             -- Check for scientific notation (immediately following without space)
  21.             if i <= #expr and (expr:sub(i, i) == "e" or expr:sub(i, i) == "E") then
  22.                 num = num .. expr:sub(i, i)
  23.                 i = i + 1
  24.                 -- Optional sign after e/E
  25.                 if i <= #expr and (expr:sub(i, i) == "+" or expr:sub(i, i) == "-") then
  26.                     num = num .. expr:sub(i, i)
  27.                     i = i + 1
  28.                 end
  29.                 -- Append the exponent digits
  30.                 while i <= #expr and expr:sub(i, i):match("%d") do
  31.                     num = num .. expr:sub(i, i)
  32.                     i = i + 1
  33.                 end
  34.             end
  35.             table.insert(tokens, {type = "number", value = tonumber(num)})
  36.         elseif c == "(" or c == ")" then
  37.             table.insert(tokens, {type = "paren", value = c})
  38.             i = i + 1
  39.         else
  40.             -- Try to match multi-character operators
  41.             local op = nil
  42.             -- If the operator "E" appears separated by spaces, treat it as an operator token.
  43.             if expr:sub(i, i) == "E" then
  44.                 op = "E"
  45.                 i = i + 1
  46.             else
  47.                 op = c
  48.                 i = i + 1
  49.             end
  50.             table.insert(tokens, {type = "operator", value = op})
  51.         end
  52.     end
  53.     return tokens
  54. end
  55.  
  56. -- Operator precedence and associativity
  57. local operators = {
  58.     ["E"] = {precedence = 5, associativity = "right"}, -- Scientific notation operator
  59.     ["^"] = {precedence = 4, associativity = "right"},
  60.     ["*"] = {precedence = 3, associativity = "left"},
  61.     ["/"] = {precedence = 3, associativity = "left"},
  62.     ["+"] = {precedence = 2, associativity = "left"},
  63.     ["-"] = {precedence = 2, associativity = "left"},
  64. }
  65.  
  66. -- Step 2: Parser using the shunting-yard algorithm
  67. local function shuntingYard(tokens)
  68.     local output = {}
  69.     local opStack = {}
  70.  
  71.     for _, token in ipairs(tokens) do
  72.         if token.type == "number" then
  73.             table.insert(output, token)
  74.         elseif token.type == "operator" then
  75.             local o1 = token.value
  76.             while #opStack > 0 do
  77.                 local top = opStack[#opStack]
  78.                 if top.type ~= "operator" then break end
  79.                 local o2 = top.value
  80.                 if (operators[o1].associativity == "left" and operators[o1].precedence <= operators[o2].precedence) or
  81.                     (operators[o1].associativity == "right" and operators[o1].precedence < operators[o2].precedence) then
  82.                     table.insert(output, table.remove(opStack))
  83.                 else
  84.                     break
  85.                 end
  86.             end
  87.             table.insert(opStack, token)
  88.         elseif token.type == "paren" then
  89.             if token.value == "(" then
  90.                 table.insert(opStack, token)
  91.             else  -- token.value == ")"
  92.                 while #opStack > 0 and opStack[#opStack].value ~= "(" do
  93.                     table.insert(output, table.remove(opStack))
  94.                 end
  95.                 if #opStack == 0 then
  96.                     error("Mismatched parentheses")
  97.                 end
  98.                 table.remove(opStack) -- remove "("
  99.             end
  100.         end
  101.     end
  102.  
  103.     while #opStack > 0 do
  104.         local top = table.remove(opStack)
  105.         if top.value == "(" or top.value == ")" then
  106.             error("Mismatched parentheses")
  107.         end
  108.         table.insert(output, top)
  109.     end
  110.  
  111.     return output
  112. end
  113.  
  114. -- Step 3: Evaluate Reverse Polish Notation (RPN)
  115. local function evaluateRPN(rpn)
  116.     local stack = {}
  117.     for _, token in ipairs(rpn) do
  118.         if token.type == "number" then
  119.             table.insert(stack, token.value)
  120.         elseif token.type == "operator" then
  121.             local b = table.remove(stack)
  122.             local a = table.remove(stack)
  123.             local result = nil
  124.             if token.value == "+" then
  125.                 result = a + b
  126.             elseif token.value == "-" then
  127.                 result = a - b
  128.             elseif token.value == "*" then
  129.                 result = a * b
  130.             elseif token.value == "/" then
  131.                 result = a / b
  132.             elseif token.value == "^" then
  133.                 result = a ^ b
  134.             elseif token.value == "E" then
  135.                 -- Evaluate as: a * (10^b)
  136.                 result = a * (10 ^ b)
  137.             else
  138.                 error("Unknown operator: " .. token.value)
  139.             end
  140.             table.insert(stack, result)
  141.         end
  142.     end
  143.     if #stack ~= 1 then
  144.         error("Invalid expression")
  145.     end
  146.     return stack[1]
  147. end
  148.  
  149. -- The universal parser function that catches errors and returns an error message
  150. function MathParser.evaluateExpression(expr)
  151.     local tokens = tokenize(expr)
  152.     local status, result = pcall(function()
  153.         local rpn = shuntingYard(tokens)
  154.         return evaluateRPN(rpn)
  155.     end)
  156.     if not status then
  157.         return "Error: " .. result
  158.     else
  159.         return result
  160.     end
  161. end
  162.  
  163. return MathParser
  164.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement