package com.lemenzo.calculator.engine import java.math.BigInteger class ProgrammerEngine { enum class Base(val radix: Int) { BIN(2), OCT(8), DEC(10), HEX(16) } enum class WordSize(val bits: Int) { B8(8), B16(16), B32(32), B64(64) } var base = Base.DEC var wordSize = WordSize.B64 fun evaluate(expression: String): BigInteger { require(expression.length <= MAX_EXPRESSION_LENGTH) { "Expression too long" } val p = Parser(expression) val result = p.parseOr() p.skipWs() require(p.end()) { "Unexpected input" } return normalize(result) } fun format(value: BigInteger, outputBase: Base = base): String { val unsigned = if (value.signum() < 0) value.add(BigInteger.ONE.shiftLeft(wordSize.bits)) else value return when (outputBase) { Base.BIN -> unsigned.toString(2).uppercase() Base.OCT -> unsigned.toString(8).uppercase() Base.DEC -> value.toString() Base.HEX -> unsigned.toString(16).uppercase() } } fun allBases(value: BigInteger): Map = Base.entries.associateWith { format(value, it) } private fun normalize(v: BigInteger): BigInteger { val bits = wordSize.bits val modulus = BigInteger.ONE.shiftLeft(bits) var n = v.mod(modulus) if (n.testBit(bits - 1)) n = n.subtract(modulus) return n } private inner class Parser(private val s: String) { var i = 0 fun end() = i >= s.length fun skipWs() { while (!end() && s[i].isWhitespace()) i++ } fun parseOr(): BigInteger { var v = parseXor() while (word("OR")) v = normalize(v.or(parseXor())) return v } fun parseXor(): BigInteger { var v = parseAnd() while (word("XOR")) v = normalize(v.xor(parseAnd())) return v } fun parseAnd(): BigInteger { var v = parseShift() while (word("AND")) v = normalize(v.and(parseShift())) return v } fun parseShift(): BigInteger { var v = parseAdd() while (true) { v = when { token("<<") -> normalize(v.shiftLeft(shiftCount(parseAdd()))) token(">>") -> normalize(v.shiftRight(shiftCount(parseAdd()))) else -> return v } } } fun shiftCount(value: BigInteger): Int { val n = value.intValueExact() require(n in 0 until wordSize.bits) { "Shift count range" } return n } fun parseAdd(): BigInteger { var v = parseMul() while (true) { v = when { token("+") -> normalize(v.add(parseMul())) token("-") -> normalize(v.subtract(parseMul())) else -> return v } } } fun parseMul(): BigInteger { var v = parseUnary() while (true) { v = when { token("*") || token("×") -> normalize(v.multiply(parseUnary())) token("/") || token("÷") -> { val r = parseUnary(); require(r != BigInteger.ZERO) { "Division by zero" }; normalize(v.divide(r)) } token("%") -> { val r = parseUnary(); require(r != BigInteger.ZERO) { "Division by zero" }; normalize(v.remainder(r)) } else -> return v } } } fun parseUnary(): BigInteger { return when { word("NOT") -> normalize(parseUnary().not()) token("-") -> normalize(parseUnary().negate()) token("+") -> parseUnary() else -> parsePrimary() } } fun parsePrimary(): BigInteger { skipWs() if (token("(")) { val v = parseOr(); require(token(")")) { "Missing )" }; return v } val start = i while (!end() && s[i].isLetterOrDigit()) i++ require(i > start) { "Number expected" } return normalize(s.substring(start, i).toBigInteger(base.radix)) } fun token(t: String): Boolean { skipWs(); if (s.regionMatches(i, t, 0, t.length, ignoreCase = false)) { i += t.length; return true }; return false } fun word(t: String): Boolean { skipWs(); if (!s.regionMatches(i, t, 0, t.length, ignoreCase = true)) return false val end = i + t.length if (end < s.length && s[end].isLetterOrDigit()) return false i = end; return true } } private companion object { const val MAX_EXPRESSION_LENGTH = 512 } }