immibis

immicalc

Jan 13th, 2012
1,633
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. -- This program is public domain. Do whatever you want with it.
  2.  
  3. local expr
  4.  
  5. local spacechars = " \t\n\r"
  6.  
  7. local function skipspaces()
  8.  while spacechars:find(expr:sub(1, 1),1,true) ~= nil and expr:len() > 0 do
  9.   expr = expr:sub(2)
  10.  end
  11. end
  12.  
  13. local function acceptch(ch)
  14.  skipspaces()
  15.  if expr:sub(1,1) == ch then
  16.   expr = expr:sub(2)
  17.   return true
  18.  else
  19.   return false
  20.  end
  21. end
  22.  
  23. local function acceptnum()
  24.  skipspaces()
  25.  -- I'm sure there's a better way
  26.  for len = expr:len(), 1, -1 do
  27.   local success, n = pcall(tonumber, expr:sub(1, len))
  28.   if success and n ~= nil then
  29.    expr = expr:sub(len + 1)
  30.    return n
  31.   end
  32.  end
  33.  return nil
  34. end
  35.  
  36. local alpha = "_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
  37. local alnum = alpha .. "0123456789"
  38.  
  39. local function acceptident()
  40.  local n = 0
  41.  skipspaces()
  42.  if alpha:find(expr:sub(1, 1),1,true) == nil then return nil end
  43.  while alnum:find(expr:sub(n+1, n+1),1,true) ~= nil and expr:len() > n do
  44.   n = n + 1
  45.  end
  46.  local ident = expr:sub(1, n)
  47.  expr = expr:sub(n+1)
  48.  return ident
  49. end
  50.  
  51. local function parse_error(s)
  52.  error(s and ("parse error: " .. s) or "parse error")
  53. end
  54.  
  55. local expression_root
  56.  
  57. local math_functions = {
  58. abs = math.abs,
  59. acos = math.acos,
  60. asin = math.asin,
  61. atan = math.atan,
  62. atan2 = math.atan2,
  63. ceil = math.ceil,
  64. floor = math.floor,
  65. cos = math.cos,
  66. sin = math.sin,
  67. tan = math.tan,
  68. cosh = math.cosh,
  69. sinh = math.sinh,
  70. tanh = math.tanh,
  71. -- math.deg, math.rad
  72. exp = math.exp,
  73. ln = math.log,
  74. log = math.log10,
  75. pow = math.pow,
  76. min = math.min,
  77. max = math.max,
  78. sqrt = math.sqrt,
  79. random = math.random
  80. }
  81.  
  82. local math_constants = {
  83. pi = math.pi,
  84. e = math.exp(1),
  85. inf = math.huge,
  86. nan = math.huge - math.huge
  87. }
  88.  
  89. local function call_function(id, arglist)
  90.  if math_functions[id] == nil then
  91.   error("no such function: " .. id)
  92.  end
  93.  return math_functions[id](unpack(arglist))
  94. end
  95.  
  96. local function get_constant(id)
  97.  return math_constants[id] or error("no such constant: "..id)
  98. end
  99.  
  100. local function num_or_brackets()
  101.  if acceptch("(") then
  102.   local n = expression_root()
  103.   if not acceptch(")") then parse_error("non-matching parentheses") end
  104.   return n
  105.  elseif acceptch("-") then
  106.   return -expression_root()
  107.  else
  108.   local id = acceptident()
  109.   if id ~= nil then
  110.    if not acceptch("(") then
  111.     return get_constant(id)
  112.    elseif acceptch(")") then
  113.     return call_function(id, {})
  114.    else
  115.     local arglist = {}
  116.     local done = false
  117.     while not done do
  118.      table.insert(arglist, expression_root())
  119.      if acceptch(")") then
  120.       done = true
  121.      elseif not acceptch(",") then
  122.       parse_error("expected , or )")
  123.      end
  124.     end
  125.     return call_function(id, arglist)
  126.    end
  127.   else
  128.    local n = acceptnum()
  129.    if n == nil then parse_error("expected number or bracketed expression") end
  130.    return n
  131.   end
  132.  end
  133. end
  134.  
  135. local function division()
  136.  local n1 = num_or_brackets()
  137.  while acceptch("/") do
  138.   n1 = n1 / num_or_brackets()
  139.  end
  140.  return n1
  141. end
  142.  
  143. local function multiplication()
  144.  local n1 = division()
  145.  while acceptch("*") do
  146.   n1 = n1 * division()
  147.  end
  148.  return n1
  149. end
  150. local function subtraction()
  151.  local n1 = multiplication()
  152.  while acceptch("-") do
  153.   n1 = n1 - multiplication()
  154.  end
  155.  return n1
  156. end
  157. local function addition()
  158.  local n1 = subtraction()
  159.  while acceptch("+") do
  160.   n1 = n1 + subtraction()
  161.  end
  162.  return n1
  163. end
  164. expression_root = addition
  165.  
  166. local function parse(e)
  167.  expr = e
  168.  local success, result = pcall(expression_root)
  169.  if success and expr ~= "" then
  170.   print("Garbage after expression?")
  171.  end
  172.  if success then
  173.   print("Answer: " .. tostring(result))
  174.  else
  175.   print(result)
  176.   print("Near: " .. (expr == "" and "end of expression" or expr))
  177.  end
  178. end
  179.  
  180. local function mainloop()
  181.  local quitting = false
  182.  print("")
  183.  print("Welcome to immicalc.")
  184.  print("This software is public domain.")
  185.  print("")
  186.  print("Enter expressions, one per line.")
  187.  print("Enter 'quit' to quit.")
  188.  print("")
  189.  while not quitting do
  190.   local line = io.read()
  191.   if line == "quit" then
  192.    quitting = true
  193.   else
  194.    parse(line)
  195.    print("")
  196.   end
  197.  end
  198. end
  199.  
  200. mainloop()
RAW Paste Data