#lang shplait // Task: // Relax field lookup to fall back to a method send when // a field is not found and a method exists with the same name, // or vice versa // // obj.f --> if no such field, look for a method f // obj.f(17) --> if no such method, look for a field f // // Use 0 as a dummy method argument when calling a method in // place of getting a field. // Strategy #2: return an `Optionof` from `find` // Problem with that strategy: having to handle two // cases at every call can get repetitive type Exp | intE(n :: Int) | plusE(lhs :: Exp, rhs :: Exp) | multE(lhs :: Exp, rhs :: Exp) | argE() | thisE() | objectE(fields :: Listof(Symbol * Exp), methods :: Listof(Symbol * Exp)) | getE(obj_exp :: Exp, field_name :: Symbol) | sendE(obj_exp :: Exp, method_name :: Symbol, arg_exp :: Exp) type Value | intV(n :: Int) | objV(fields :: Listof(Symbol * Value), methods :: Listof(Symbol * Exp)) // ---------------------------------------- fun find(l :: Listof(Symbol * ?a), name :: Symbol) :: Optionof(?a): match l | []: none() //error(#'find, "not found: " +& name) | cons(p, rst_l): if fst(p) == name | some(snd(p)) | find(rst_l, name) module test: check: find([values(#'a, 1)], #'a) ~is some(1) check: find([values(#'a, "apple")], #'a) ~is some("apple") check: find([values(#'a, 1), values(#'b, 2)], #'b) ~is some(2) check: find([], #'a) ~is none() check: find([values(#'a, 1)], #'x) ~is none() // ---------------------------------------- def interp :: (Exp, Value, Value) -> Value: fun (a, this_val, arg_val): match a | intE(n): intV(n) | plusE(l, r): num_plus(interp(l, this_val, arg_val), interp(r, this_val, arg_val)) | multE(l, r): num_mult(interp(l, this_val, arg_val), interp(r, this_val, arg_val)) | thisE(): this_val | argE(): arg_val | objectE(fields, methods): objV(map(fun (f): def name = fst(f) def exp = snd(f) values(name, interp(exp, this_val, arg_val)), fields), methods) | getE(obj_exp, field_name): match interp(obj_exp, this_val, arg_val) | objV(fields, methods): match find(fields, field_name) | none(): def find_res = find(methods, field_name) match find_res | none(): error(#'interp, "not found") | some(exp): interp(exp, objV(fields, methods), intV(0)) | some(val): val | ~else: error(#'interp, "not an object") | sendE(obj_exp, method_name, arg_exp): def obj = interp(obj_exp, this_val, arg_val) def next_arg_val = interp(arg_exp, this_val, arg_val) match obj | objV(fields, methods): match find(methods, method_name) | none(): match find(fields, method_name) | none(): error(#'interp, "not found") | some(val): val | some(exp): interp(exp, obj, next_arg_val) | ~else: error(#'interp, "not an object") fun num_op(op :: (Int, Int) -> Int, l :: Value, r :: Value) :: Value: cond | l is_a intV && r is_a intV: intV(op(intV.n(l), intV.n(r))) | ~else: error(#'interp, "not a number") fun num_plus(l :: Value, r :: Value) :: Value: num_op(fun (a, b): a+b, l, r) fun num_mult(l :: Value, r :: Value) :: Value: num_op(fun (a, b): a*b, l, r) // ---------------------------------------- // Examples module test: def posn27: objectE( [ values(#'x, intE(2)), values(#'y, intE(7)) ], [ values(#'mdist, plusE(getE(thisE(), #'x), getE(thisE(), #'y))), values(#'addX, plusE(getE(thisE(), #'x), argE())), values(#'multY, multE(argE(), getE(thisE(), #'y))) ] ) def posn531: objectE( [ values(#'x, intE(5)), values(#'y, intE(3)), values(#'z, intE(1)) ], [ values(#'mdist, plusE(getE(thisE(), #'z), plusE(getE(thisE(), #'x), getE(thisE(), #'y)))) ] ) // ---------------------------------------- module test: check: interp(intE(10), objV([], []), intV(0)) ~is intV(10) check: interp(plusE(intE(10), intE(17)), objV([], []), intV(0)) ~is intV(27) check: interp(multE(intE(10), intE(7)), objV([], []), intV(0)) ~is intV(70) check: interp(objectE([values(#'a, intE(1)), values(#'b, intE(2))], [values(#'m, intE(0))]), objV([], []), intV(0)) ~is objV([values(#'a, intV(1)), values(#'b, intV(2))], [values(#'m, intE(0))]) check: interp(sendE(posn27, #'mdist, intE(0)), objV([], []), intV(0)) ~is intV(9) check: interp(sendE(posn27, #'addX, intE(10)), objV([], []), intV(0)) ~is intV(12) check: interp(plusE(intE(1), objectE([], [])), objV([], []), intV(0)) ~raises "not a number" check: interp(getE(intE(1), #'x), objV([], []), intV(0)) ~raises "not an object" check: interp(sendE(intE(1), #'mdist, intE(0)), objV([], []), intV(0)) ~raises "not an object" check: interp(getE(objectE([], []), #'f), objV([], []), intV(0)) ~raises "not found" check: interp(getE(objectE([], [values(#'f, intE(1))]), #'f), objV([], []), intV(0)) ~is intV(1) // This example is like // { method f(arg): arg }.f(0) // inside some other method: check: interp(getE(objectE([], [values(#'f, argE())]), #'f), objV([], []), intV(5)) ~is intV(0) check: interp(getE(objectE([], [values(#'f, thisE())]), #'f), objV([], []), intV(5)) ~is objV([], [values(#'f, thisE())]) check: interp(getE(objectE([values(#'g, intE(1))], [values(#'f, getE(thisE(), #'g))]), #'f), objV([], []), intV(5)) ~is intV(1) // { f = 1 }.f(0) check: interp(sendE(objectE([values(#'f, intE(1))], []), #'f, intE(0)), objV([], []), intV(5)) ~is intV(1) check: interp(sendE(objectE([], [values(#'f, plusE(intE(1), objectE([],[])))]), #'f, intE(0)), objV([], []), intV(5)) ~raises "not a number"