Files

86 lines
2.2 KiB
Swift

//
// Interpreter.swift
// ParsingTests
//
// Created by Nick Lockwood on 04/09/2018.
// Copyright © 2018 Nick Lockwood. All rights reserved.
//
import Foundation
// MARK: interface
public enum RuntimeError: Error, Equatable {
case undefinedVariable(String)
}
public func evaluate(_ program: [Statement]) throws -> String {
let environment = Environment()
for statement in program {
try statement.evaluate(in: environment)
}
return environment.output
}
// MARK: implementation
enum Value: CustomStringConvertible, Equatable {
case number(Double)
case string(String)
var description: String {
switch self {
case .number(let double):
return String(format: "%g", double)
case .string(let string):
return string
}
}
}
final class Environment {
var variables: [String: Value] = [:]
var output = ""
}
extension Statement {
func evaluate(in environment: Environment) throws {
switch self {
case .declaration(name: let name, value: let expression):
let value = try expression.evaluate(in: environment)
environment.variables[name] = value
case .print(let expression):
let value = try expression.evaluate(in: environment)
environment.output.append("\(value)\n")
}
}
}
extension Expression {
func evaluate(in environment: Environment) throws -> Value {
switch self {
case .number(let double):
return .number(double)
case .string(let string):
return .string(string)
case .variable(let name):
guard let value = environment.variables[name] else {
throw RuntimeError.undefinedVariable(name)
}
return value
case .addition(lhs: let expression1, rhs: let expression2):
let value1 = try expression1.evaluate(in: environment)
let value2 = try expression2.evaluate(in: environment)
switch (value1, value2) {
case (.number(let lhs), .number(let rhs)):
return .number(lhs + rhs)
case (.string, _),
(_, .string):
return .string("\(value1)\(value2)")
}
}
}
}