(* Daniel Blanchard - CISC672 - Dr. Pollock 9/5/2006 This program acts as a simple integer-based infix notation calculator. Requires atoi.cl from examples directory. *) (* This class provides a stack abstraction for Strings. *) class StringStack inherits A2I { topLength : Int; stack : String <- ""; lengthString : String <- "|0"; topIntLength : Int <- 2; top() : String { stack.substr(0, topLength) }; pop() : Object { if isEmpty() then abort() else { stack <- stack.substr(topLength, stack.length()-topLength); lengthString <- lengthString.substr(0,lengthString.length()-topIntLength); (let i : Int <- lengthString.length() in { (let current : String <- lengthString.substr(i-1,1) in while not current = "|" loop { i <- i - 1; current <- lengthString.substr(i-1,1); } pool ); topLength <- a2i(lengthString.substr(i,lengthString.length()-i)); topIntLength <- i2a(topLength).length()+1; } ); } fi }; push(s : String) : Object { { stack <- s.concat(stack); lengthString <- lengthString.concat("|".concat(i2a(s.length()))); topLength <- s.length(); topIntLength <- i2a(topLength).length()+1; } }; isEmpty() : Bool { if stack = "" then true else false fi }; }; (* This class handles all of the conversion from infix to postfix notation. *) class PostfixConverter inherits A2I { operatorStack : StringStack; postfixString : String; currentOperand : String; -- Determines whether or not a given character is a digit. isNumber(char : String) : Bool { if char = "0" then true else if char = "1" then true else if char = "2" then true else if char = "3" then true else if char = "4" then true else if char = "5" then true else if char = "6" then true else if char = "7" then true else if char = "8" then true else if char = "9" then true else false fi fi fi fi fi fi fi fi fi fi }; -- Used to determine operator precedence precedence(operator : String) : Int { if operator = "*" then 1 else if operator = "/" then 1 else if operator = "+" then 0 else if operator = "-" then 0 else 0 fi fi fi fi }; -- As there seem to be no boolean connectives in Cool, I made this function to serve as a condition for a while loop. moreOperators(aStack : StringStack, operator : String) : Bool { if not aStack.isEmpty() then if not aStack.top() = "(" then if precedence(operator) <= precedence(aStack.top()) then true else false fi else false fi else false fi }; -- Converts infix expressions to postfix infixToPostfix(infixString : String) : String { { operatorStack <- new StringStack; postfixString <- ""; currentOperand <- ""; (let j : Int <- infixString.length() in (let i : Int <- 0 in while i < j loop (let currentChar : String <- infixString.substr(i,1) in { if (isNumber(currentChar)) then currentOperand <- currentOperand.concat(currentChar) else { if not currentOperand = "" then { postfixString <- postfixString.concat(currentOperand.concat(" ")); currentOperand <- ""; } else 0 fi; if currentChar = "(" then operatorStack.push(currentChar) else if currentChar = ")" then { while not operatorStack.top() = "(" loop { postfixString <- postfixString.concat(operatorStack.top().concat(" ")); operatorStack.pop(); } pool; operatorStack.pop(); } else { while moreOperators(operatorStack, currentChar) loop { postfixString <- postfixString.concat(operatorStack.top().concat(" ")); operatorStack.pop(); } pool; operatorStack.push(currentChar); } fi fi; } fi; i <- i + 1; } ) pool ) ); while not operatorStack.isEmpty() loop { postfixString <- postfixString.concat(operatorStack.top().concat(" ")); operatorStack.pop(); } pool; postfixString; } }; }; (* Main class that prompts user for input, calls on converter class, and then calculates result. *) class Main inherits IO { converter : PostfixConverter <- new PostfixConverter; calculationStack : StringStack; currentOperand : String; -- Prompts the user and reads the input. prompt() : String { { out_string("\nEnter an expression to calculate (type 'stop' to quit): "); in_string(); } }; -- Performs all of the calculations on the postfix string calculate(postfix : String) : Int { { calculationStack <- new StringStack; (let start : Int <- 0 in (let current : Int <- 0 in while current < postfix.length() loop (let currentChar : String <- postfix.substr(current,1) in { if currentChar = " " then { calculationStack.push(postfix.substr(start,current-start)); start <- current + 1; } else 0 fi; if 0 < start then if not converter.isNumber(calculationStack.top().substr(0,1)) then (let operator : String <- calculationStack.top() in { calculationStack.pop(); (let operand2 : Int <- converter.a2i(calculationStack.top()) in { calculationStack.pop(); (let operand1 : Int <- converter.a2i(calculationStack.top()) in { calculationStack.pop(); if operator = "*" then calculationStack.push(converter.i2a(operand1 * operand2)) else if operator = "/" then calculationStack.push(converter.i2a(operand1 / operand2)) else if operator = "+" then calculationStack.push(converter.i2a(operand1 + operand2)) else if operator = "-" then calculationStack.push(converter.i2a(operand1 - operand2)) else { out_string("ILLEGAL OPERATOR!"); abort(); } fi fi fi fi; } ); } ); } ) else 0 fi else 0 fi; current <- current + 1; } ) pool ) ); converter.a2i(calculationStack.top()); } }; main() : Object { while true loop (let s : String <- prompt() in if s = "stop" then abort() else { out_int(calculate(converter.infixToPostfix("(".concat(s).concat(")")))); } fi ) pool }; };