Last active
March 15, 2016 00:58
-
-
Save carols10cents/6b6c801c6be8ac8057f0 to your computer and use it in GitHub Desktop.
Stolen ADT exercises
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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()); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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) | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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()); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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) | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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") | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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