Parsing/Shunting-yard algorithm: Difference between revisions

m (→‎{{header|Raku}}: Undo bizarre single space indent that somebody found necessary to do)
Line 4,699:
 
=={{header|Swift}}==
<syntaxhighlight lang="swift">import// FoundationUpdated to Swift 5.7
import Foundation
 
// Using arrays for both stack and queue
struct Stack<T> {
private(set) var elements = [T]()
var isEmpty: Bool { return elements.isEmpty }
 
mutating func push(newElement: T) {
elements.append(newElement)
}
}
 
mutating func pop() -> T {
return elements.removeLast()
}
}
 
func top() -> T? {
return elements.last
}
}
}
 
struct Queue<T> {
private(set) var elements = [T]()
var isEmpty: Bool { return elements.isEmpty }
 
mutating func enqueue(newElement: T) {
elements.append(newElement)
}
}
 
mutating func dequeue() -> T {
return elements.removeFirst()
}
}
}
 
Line 4,736 ⟶ 4,737:
// Define abstract interface, which can be used to restrict Set extension
protocol OperatorType: Comparable, Hashable {
var name: String { get }
var precedence: Int { get }
var associativity: Associativity { get }
}
 
struct Operator: OperatorType {
let name: String
let precedence: Int
let associativity: Associativity
// same operator names are not allowed
func hash(into hasher: inout Hasher) {
var hashValue: Int { return "\(name)".hashValue }
hasher.combine(self.name)
 
}
init(_ name: String, _ precedence: Int, _ associativity: Associativity) {
self.name = name; self.precedence = precedence; self.associativity = associativity
init(_ name: String, _ precedence: Int, _ associativity: Associativity) {
}
self.name = name; self.precedence = precedence; self.associativity = associativity
}
}
 
func ==(x: Operator, y: Operator) -> Bool {
// same operator names are not allowed
return x.name == y.name
}
 
func <(x: Operator, y: Operator) -> Bool {
// compare operators by their precedence and associavity
return (x.associativity == .Left && x.precedence == y.precedence) || x.precedence < y.precedence
}
 
extension Set where Element: OperatorType {
func contains(op: String?) -> Bool {
guard let operatorName = op else { return false }
return contains { $0.name == operatorName }
}
}
 
subscript (operatorName: String) -> Element? {
get {
return filter { $0.name == operatorName }.first
}
}
}
}
}
 
// Convenience
extension String {
var isNumber: Bool { return Double(self) != nil }
}
 
struct ShuntingYard {
enum ErrorShuntingYardError: ErrorTypeError {
case MismatchedParenthesis(String)
case UnrecognizedToken(String)
}
}
static func parse(_ input: String, operators: Set<Operator>) throws -> String {
var stack = Stack<String>()
var output = Queue<String>()
let tokens = input.components(separatedBy: " ")
let operatorNames = operators.map({ $0.name }).joined(separator: " ")
for token in tokens {
// if token is a number add it to the output queue
if token.isNumber {
output.enqueue(newElement: token)
}
// else if token is a operator:
else if operatorNames.contains(token) {
// While there is a operator on top of the stack and has lower precedence than current operator (token)
while let top = stack.top(),
operatorNames.contains(top)
&& Self.hasLowerPrecedence(token, top, operators) {
// Pop it off to the output queue
output.enqueue(newElement: stack.pop())
}
// Push current operator (token) onto the operator stack
stack.push(newElement: token)
}
// If the token is a left parenthesis, then push it onto the stack.
else if token == "(" {
stack.push(newElement: token)
}
// If the token is a right parenthesis:
else if token == ")" {
// Until the token at the top of the stack is a left parenthesis
while !stack.isEmpty && stack.top() != "(" {
// Pop operators off the stack onto the output queue.
output.enqueue(newElement: stack.pop())
}
// If the stack runs out, than there are mismatched parentheses.
if stack.isEmpty {
throw ShuntingYardError.MismatchedParenthesis(input)
}
// Pop the left parenthesis from the stack, but not onto the output queue.
let _ = stack.pop()
}
// if token is not number, operator or a parenthesis, then it is not recognized
else {
throw ShuntingYardError.UnrecognizedToken(token)
}
}
// When there are no more tokens to read:
// While there are still operator tokens in the stack:
while let top = stack.top(),
operatorNames.contains(top) {
// Pop the operator onto the output queue.
output.enqueue(newElement: stack.pop())
}
// If the operator token on the top of the stack is a parenthesis, then there are mismatched parentheses
// Note: Assume that all operators has been poped onto the output queue.
if stack.isEmpty == false {
throw ShuntingYardError.MismatchedParenthesis(input)
}
return output.elements.joined(separator: " ")
}
static private func containsOperator(stack: Stack<String>, _ operators: [String: NSDictionary]) -> Bool {
guard stack.isEmpty == false else { return false }
// Is there a matching operator in the operators set?
return operators[stack.top()!] != nil ? true : false
}
static private func hasLowerPrecedence(_ x: String, _ y: String, _ operators: Set<Operator>) -> Bool {
guard let first = operators[x], let second = operators[y] else { return false }
return first < second
}
}
 
/* Include in tests
static func parse(input: String, operators: Set<Operator>) throws -> String {
func testParse() throws {
var stack = Stack<String>()
let input = "3 + 4 * 2 / ( 1 - 5 ) ^ 2 ^ 3"
var output = Queue<String>()
let operators: Set<Operator> = [
let tokens = input.componentsSeparatedByString(" ")
Operator("^", 4, .Right),
 
Operator("*", 3, .Left),
for token in tokens {
Operator("/", 3, .Left),
// Wikipedia: if token is a number add it to the output queue
Operator("+", 2, .Left),
if token.isNumber {
Operator("-", 2, .Left)
output.enqueue(token)
]
}
let output = try! ShuntingYard.parse(input, operators: operators)
// Wikipedia: else if token is a operator:
XCTAssertEqual(output, "3 4 2 * 1 5 - 2 3 ^ ^ / +")
else if operators.contains(token) {
// Wikipedia: while there is a operator on top of the stack and has lower precedence than current operator (token)
while operators.contains(stack.top()) && hasLowerPrecedence(token, stack.top()!, operators) {
// Wikipedia: pop it off to the output queue
output.enqueue(stack.pop())
}
// Wikipedia: push current operator (token) onto the operator stack
stack.push(token)
}
// Wikipedia: If the token is a left parenthesis, then push it onto the stack.
else if token == "(" {
stack.push(token)
}
// Wikipedia: If the token is a right parenthesis:
else if token == ")" {
// Wikipedia: Until the token at the top of the stack is a left parenthesis
while !stack.isEmpty && stack.top() != "(" {
// Wikipedia: pop operators off the stack onto the output queue.
output.enqueue(stack.pop())
}
 
// If the stack runs out, than there are mismatched parentheses.
if stack.isEmpty {
throw Error.MismatchedParenthesis(input)
}
 
// Wikipedia: Pop the left parenthesis from the stack, but not onto the output queue.
stack.pop()
}
// if token is not number, operator or a parenthesis, then is not recognized
else {
throw Error.UnrecognizedToken(token)
}
}
 
// Wikipedia: When there are no more tokens to read:
 
// Wikipedia: While there are still operator tokens in the stack:
while operators.contains(stack.top()) {
// Wikipedia: Pop the operator onto the output queue.
output.enqueue(stack.pop())
}
 
// Wikipedia: If the operator token on the top of the stack is a parenthesis, then there are mismatched parentheses
// Note: Assume that all operators has been poped onto the output queue.
if stack.isEmpty == false {
throw Error.MismatchedParenthesis(input)
}
 
return output.elements.joinWithSeparator(" ")
}
 
static private func containsOperator(stack: Stack<String>, _ operators: [String: NSDictionary]) -> Bool {
guard stack.isEmpty == false else { return false }
// Is there a matching operator in the operators set?
return operators[stack.top()!] != nil ? true : false
}
 
static private func hasLowerPrecedence(x: String, _ y: String, _ operators: Set<Operator>) -> Bool {
guard let first = operators[x], let second = operators[y] else { return false }
return first < second
}
}
*/
 
let input = "3 + 4 * 2 / ( 1 - 5 ) ^ 2 ^ 3"
let operators: Set<Operator> = [
Operator("^", 4, .Right),
Operator("*", 3, .Left),
Operator("/", 3, .Left),
Operator("+", 2, .Left),
Operator("-", 2, .Left)
]
let output = try! ShuntingYard.parse(input, operators: operators)
 
print("input: \(input)")
print("output: \(output)")
</syntaxhighlight>
{{out}}
2

edits