Skip to content

Instantly share code, notes, and snippets.

@carols10cents
Last active March 15, 2016 00:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save carols10cents/6b6c801c6be8ac8057f0 to your computer and use it in GitHub Desktop.
Save carols10cents/6b6c801c6be8ac8057f0 to your computer and use it in GitHub Desktop.
Stolen ADT exercises
enum Atom {
Number(i32),
}
impl Atom {
fn eval(&self) -> i32 {
match *self {
Atom::Number(n) => n,
}
}
}
enum Sum {
Add(Box<Sum>, Atom),
Minus(Box<Sum>, Atom),
Atom(Atom),
}
impl Sum {
fn eval(&self) -> i32 {
match *self {
Sum::Add(ref l, ref r) => l.eval() + r.eval(),
Sum::Minus(ref l, ref r) => l.eval() - r.eval(),
Sum::Atom(ref s) => s.eval(),
}
}
}
#[test]
fn test_add() {
let ast = Sum::Add(Box::new(Sum::Atom(Atom::Number(20))), Atom::Number(5));
assert_eq!(25, ast.eval());
}
#[test]
fn test_minus() {
let ast = Sum::Minus(Box::new(Sum::Atom(Atom::Number(0))), Atom::Number(5));
assert_eq!(-5, ast.eval());
}
#[test]
fn test_nesting() {
let ast = Sum::Minus(Box::new(Sum::Atom(Atom::Number(0))), Atom::Number(5));
let ast = Sum::Add(Box::new(ast), Atom::Number(4));
assert_eq!(-1, ast.eval());
}
// Algebraic Data Types, Compilers and Swift
// Programming Exercise One Solution:
// Sum ADT
protocol Evaluable {
func eval() -> Int
}
enum Atom: Evaluable {
case Number(Int)
func eval() -> Int {
switch self {
case .Number(let number):
return number
}
}
}
enum Sum: Evaluable {
indirect case Add(Sum, Atom)
indirect case Minus(Sum, Atom)
case AtomShortcut(Atom)
func eval() -> Int {
switch self {
// TODO: Handle Add case
// TODO: Handle Minus case
case .AtomShortcut(let atom):
return atom.eval()
default:
return 42
}
}
}
// Tests
// DO NOT EDIT BELOW THIS LINE
// However, feel free to look at the tests to help guide you
func testRunnerAST(name:String, input:Evaluable, expected:Int) {
let result = input.eval()
testRunnerResultsPrinter(name, result:result, expected:expected)
}
func testRunnerResultsPrinter(name:String, result:Int, expected:Int) {
if result == expected {
print("✓ \(name) test passed")
} else {
print("✗ \(name) test failed")
print("Expected: \(expected)")
print("Got: \(result)")
}
// End with an empty line
print("")
}
let ast = Sum.Add(.AtomShortcut(.Number(20)), .Number(5))
testRunnerAST("Add", input:ast, expected:25)
let ast2 = Sum.Minus(.AtomShortcut(.Number(0)), .Number(5))
testRunnerAST("Minus", input:ast2, expected:-5)
let ast3 = Sum.Add(ast2, .Number(4))
testRunnerAST("Nesting", input:ast3, expected:-1)
trait Eval {
fn eval(&self) -> i32;
}
enum Product {
Multiplication(Box<Product>, Sum),
Division(Box<Product>, Sum),
Atom(Atom),
}
impl Eval for Product {
fn eval(&self) -> i32 {
match *self {
Product::Multiplication(ref l, ref r) => l.eval() * r.eval(),
Product::Division(ref l, ref r) => {
match r.eval() {
0 => -1,
r => l.eval() / r,
}
},
Product::Atom(ref v) => v.eval(),
}
}
}
enum Sum {
Add(Box<Sum>, Atom),
Minus(Box<Sum>, Atom),
Atom(Atom),
}
impl Eval for Sum {
fn eval(&self) -> i32 {
match *self {
Sum::Add(ref l, ref r) => l.eval() + r.eval(),
Sum::Minus(ref l, ref r) => l.eval() - r.eval(),
Sum::Atom(ref v) => v.eval(),
}
}
}
enum Atom {
Number(i32),
}
impl Eval for Atom {
fn eval(&self) -> i32 {
match *self {
Atom::Number(n) => n,
}
}
}
#[test]
fn test_add() {
let ast = Sum::Add(Box::new(Sum::Atom(Atom::Number(20))), Atom::Number(5));
assert_eq!(25, ast.eval());
}
#[test]
fn test_minus() {
let ast = Sum::Minus(Box::new(Sum::Atom(Atom::Number(0))), Atom::Number(5));
assert_eq!(-5, ast.eval());
}
#[test]
fn test_nesting() {
let ast = Sum::Minus(Box::new(Sum::Atom(Atom::Number(0))), Atom::Number(5));
let ast = Sum::Add(Box::new(ast), Atom::Number(4));
assert_eq!(-1, ast.eval());
}
#[test]
fn test_mult() {
let ast = Product::Multiplication(Box::new(Product::Atom(Atom::Number(20))), Sum::Atom(Atom::Number(5)));
assert_eq!(100, ast.eval());
}
#[test]
fn test_div() {
let ast = Product::Division(Box::new(Product::Atom(Atom::Number(0))), Sum::Atom(Atom::Number(5)));
assert_eq!(0, ast.eval());
}
#[test]
fn test_div_zero() {
let ast = Product::Division(Box::new(Product::Atom(Atom::Number(7))), Sum::Atom(Atom::Number(0)));
assert_eq!(-1, ast.eval());
}
#[test]
fn test_prod_nesting() {
let ast = Product::Division(Box::new(Product::Atom(Atom::Number(7))), Sum::Atom(Atom::Number(0)));
let ast = Product::Multiplication(Box::new(ast), Sum::Atom(Atom::Number(6)));
assert_eq!(-6, ast.eval());
}
// Algebraic Data Types, Compilers and Swift
// Programming Exercise Two:
// Products and Pattern Matching
protocol Evaluable {
func eval() -> Int
}
enum Atom: Evaluable {
case Number(Int)
func eval() -> Int {
switch self {
case .Number(let number):
return number
}
}
}
enum Product: Evaluable {
indirect case Multiplication(Product, Atom)
indirect case Division(Product, Atom)
case AtomShortcut(Atom)
func eval() -> Int {
switch self {
// TODO: Handle Multiplication case
// TODO: Handle Division case
// TODO: Handle Division where rhs == 0 special case
// TODO: Handle AtomShortcut case
default:
return 42
}
}
}
enum Sum: Evaluable {
indirect case Add(Sum, Product)
indirect case Minus(Sum, Product)
case ProductShortcut(Product)
case AtomShortcut(Atom)
func eval() -> Int {
switch self {
case .Add(let lhs, let rhs):
return lhs.eval() + rhs.eval()
case .Minus(let lhs, let rhs):
return lhs.eval() - rhs.eval()
case .ProductShortcut(let product):
return product.eval()
case .AtomShortcut(let atom):
return atom.eval()
}
}
}
// Tests
// DO NOT EDIT BELOW THIS LINE
// However, feel free to look at the tests to help guide you
func testRunnerAST(name:String, input:Evaluable, expected:Int) {
let result = input.eval()
testRunnerResultsPrinter(name, result:result, expected:expected)
}
func testRunnerResultsPrinter(name:String, result:Int, expected:Int) {
if result == expected {
print("✓ \(name) test passed")
} else {
print("✗ \(name) test failed")
print("Expected: \(expected)")
print("Got: \(result)")
}
// End with an empty line
print("")
}
let ast = Product.Multiplication(.AtomShortcut(.Number(20)), .Number(5))
testRunnerAST("Multiplication", input:ast, expected:100)
let ast2 = Product.Division(.AtomShortcut(.Number(0)), .Number(5))
testRunnerAST("Division", input:ast2, expected:0)
let ast3 = Product.Division(.AtomShortcut(.Number(7)), .Number(0))
testRunnerAST("Division By Zero", input:ast3, expected:-1)
let ast4 = Product.Multiplication(ast3, .Number(6))
testRunnerAST("Product Nesting", input:ast4, expected:-6)
// Algebraic Data Types, Compilers and Swift
// Programming Exercise Three Solution:
// Errors and Enviroments
typealias Env = [String: Int]
enum CompilerError: ErrorType {
case DivisionByZero
case UndefinedVariable(String)
}
protocol Evaluable {
func eval(env:Env) throws -> Int
}
enum Atom: Evaluable {
case Number(Int)
case Variable(String)
func eval(env:Env) throws -> Int {
switch self {
case .Number(let number):
return number
// TODO: Handle Variable case
// TODO: Throw UndefinedVariable if variable is not found
// TODO: Throw DivisionByZero when division by zero occurs
default:
return 42
}
}
}
enum Product: Evaluable {
indirect case Multiplication(Product, Atom)
indirect case Division(Product, Atom)
case AtomShortcut(Atom)
func eval(env:Env) throws -> Int {
switch self {
case .Multiplication(let lhs, let rhs):
return try lhs.eval(env) * rhs.eval(env)
case .Division(let lhs, let rhs):
switch rhs {
case .Number(let number) where number == 0:
return -1
default:
return try lhs.eval(env) / rhs.eval(env)
}
case .AtomShortcut(let atom):
return try atom.eval(env)
}
}
}
enum Sum: Evaluable {
indirect case Add(Sum, Product)
indirect case Minus(Sum, Product)
case ProductShortcut(Product)
case AtomShortcut(Atom)
func eval(env:Env) throws -> Int {
switch self {
case .Add(let lhs, let rhs):
return try lhs.eval(env) + rhs.eval(env)
case .Minus(let lhs, let rhs):
return try lhs.eval(env) - rhs.eval(env)
case .ProductShortcut(let product):
return try product.eval(env)
case .AtomShortcut(let atom):
return try atom.eval(env)
}
}
}
// Tests
// DO NOT EDIT BELOW THIS LINE
// However, feel free to look at the tests to help guide you
func testRunnerAST(name:String, input:Evaluable, env:Env, expected:String) {
let result:String
do {
result = "\(try input.eval(env))"
} catch CompilerError.DivisionByZero {
result = "Compiler Error - Division By Zero"
} catch CompilerError.UndefinedVariable(let identifier) {
result = "Compiler Error - Undefined Variable: \(identifier)"
} catch {
result = "Compiler Error - Unknown Error"
}
testRunnerResultsPrinter(name, result:result, expected:expected)
}
func testRunnerResultsPrinter(name:String, result:String, expected:String) {
if result == expected {
print("✓ \(name) test passed")
} else {
print("✗ \(name) test failed")
print("Expected: \(expected)")
print("Got: \(result)")
}
// End with an empty line
print("")
}
let ast1 = Product.Division(.AtomShortcut(.Number(7)), .Number(0))
testRunnerAST("Division By Zero", input:ast1, env:Env(), expected:"Compiler Error - Division By Zero")
let ast2 = Product.Division(.AtomShortcut(.Variable("z")), .Number(1))
testRunnerAST("Undefined Variable z", input:ast2, env:Env(), expected:"Compiler Error - Undefined Variable: z")
let ast3 = Product.Division(.AtomShortcut(.Variable("z")), .Number(1))
testRunnerAST("Variable z", input:ast3, env:["z":5], expected:"5")
// Algebraic Data Types, Compilers and Swift
// Programming Exercise Four Solution:
// Statement
typealias Env = [String: Int]
enum CompilerError: ErrorType {
case DivisionByZero
case UndefinedVariable(String)
}
protocol Evaluable {
func eval(env:Env) throws -> Int
}
enum Atom: Evaluable {
case Number(Int)
case Variable(String)
func findIdentifier(env:Env, identifier:String) throws -> Int {
if let val = env[identifier] {
return val
} else {
throw CompilerError.UndefinedVariable(identifier)
}
}
func eval(env:Env) throws -> Int {
switch self {
case .Number(let number):
return number
case .Variable(let variable):
return try findIdentifier(env, identifier:variable)
}
}
}
enum Product: Evaluable {
indirect case Multiplication(Product, Atom)
indirect case Division(Product, Atom)
case AtomShortcut(Atom)
func eval(env:Env) throws -> Int {
switch self {
case .Multiplication(let lhs, let rhs):
return try lhs.eval(env) * rhs.eval(env)
case .Division(let lhs, let rhs):
switch rhs {
case .Number(let number) where number == 0:
throw CompilerError.DivisionByZero
default:
return try lhs.eval(env) / rhs.eval(env)
}
case .AtomShortcut(let atom):
return try atom.eval(env)
}
}
}
enum Sum: Evaluable {
indirect case Add(Sum, Product)
indirect case Minus(Sum, Product)
case ProductShortcut(Product)
case AtomShortcut(Atom)
func eval(env:Env) throws -> Int {
switch self {
case .Add(let lhs, let rhs):
return try lhs.eval(env) + rhs.eval(env)
case .Minus(let lhs, let rhs):
return try lhs.eval(env) - rhs.eval(env)
case .ProductShortcut(let product):
return try product.eval(env)
case .AtomShortcut(let atom):
return try atom.eval(env)
}
}
}
enum Statement {
indirect case Chain(Statement, Statement)
indirect case Assign(String, Statement)
case SumShortcut(Sum)
case AtomShortcut(Atom)
func eval(env:Env) throws -> Env {
switch self {
case .Chain(let statOne, let statTwo):
// TODO: Fix Chain case
return ["": 0]
case .Assign(let identifier, let stat):
// TODO: Fix Assign case
return ["": 0]
case .SumShortcut(let sum):
return ["output": try sum.eval(env)]
case .AtomShortcut(let atom):
return ["output": try atom.eval(env)]
}
}
}
// Tests
// DO NOT EDIT BELOW THIS LINE
// However, feel free to look at the tests to help guide you
func testRunnerAST(name:String, input:Statement, env:Env, expected:String) {
let result:String
do {
result = "\(try input.eval(env))"
} catch CompilerError.DivisionByZero {
result = "Compiler Error - Division By Zero"
} catch CompilerError.UndefinedVariable(let identifier) {
result = "Compiler Error - Undefined Variable: \(identifier)"
} catch {
result = "Compiler Error - Unknown Error"
}
testRunnerResultsPrinter(name, result:result, expected:expected)
}
func testRunnerResultsPrinter(name:String, result:String, expected:String) {
if result == expected {
print("✓ \(name) test passed")
} else {
print("✗ \(name) test failed")
print("Expected: \(expected)")
print("Got: \(result)")
}
// End with an empty line
print("")
}
let ast1 =
Statement.Assign(
"y",
.AtomShortcut(.Number(1))
)
testRunnerAST("Assign", input:ast1, env:Env(), expected:"[\"y\": 1]")
let ast2 =
Statement.Chain(
.Assign(
"y",
.AtomShortcut(.Number(1))
),
.SumShortcut(
.Add(
.AtomShortcut(.Variable("y")),
.AtomShortcut(.Number(1))
)
)
)
testRunnerAST("Chain", input:ast2, env:Env(), expected:"[\"output\": 2]")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment