# Copyright 2005-2008 Kevin Reid, under the terms of the MIT X license # found at http://www.opensource.org/licenses/mit-license.html ................ ? pragma.syntax("0.9") Using the primitive guards, not the complex E-implemented ones ? def tfloat64 := ; null ? def tint := ; null LiteralExpr ? 43 # value: 43 ? "foo" # value: "foo" ? "fo\"o" # value: "fo\"o" ? '%' # value: '%' ? 2.5 # value: 2.5 CallExpr ? 1 / 2 # value: 0.5 order-of-evaluation test ? (print("1"); print).run((print("2"); " left"), (print("3"); " right")) # stdout: 123 left right DefineExpr, NounExpr, FinalPattern ? def pi := 3.14159 # value: 3.14159 ? pi * 2 # value: 6.28318 VarPattern, AssignExpr ? def var x := 1 # value: 1 ? x # value: 1 ? x := 2 # value: 2 ? x # value: 2 Bug: def variables were once assignable: ? { def x := 1; x += 1; x } # problem: x is not an assignable variable var slot with guard ? def var guarded_var :tfloat64 := 1.0 # value: 1.0 ? guarded_var # value: 1.0 ? guarded_var := 2.0 # value: 2.0 ? guarded_var # value: 2.0 ? guarded_var := 3 # value: 3 ? guarded_var # value: 3.0 ? guarded_var := "four" # problem: the String "four" doesn't coerce to a float64 ? def var failing_guarded_var :tfloat64 := "five" # problem: the String "five" doesn't coerce to a float64 Bug: assignment to a var was using coerce/1 instead of coerce/2 ? { def var x :(def guard { to coerce(sp,ej) :any {return sp} > to coerce(_) :any {throw("oops")} }) \ > := 1 > x := 2 > x } # value: 2 Non-contagion: ? def y := x # value: 2 ? x := 0 # value: 0 ? y # value: 2 More AssignExpr: ? y := 3 # problem: y is not an assignable variable confirm static rejection ? if (false) { y := 3 } # problem: y is not an assignable variable outer shadowed binding doesn't cause rejection (var) ? { def sbt := 1; [{ var sbt := 2; { sbt := 3 }; sbt}, sbt] } # value: [3, 1] outer shadowed binding doesn't cause rejection (slot) ? { def sbt := 1; [{ def &sbt := def _ {to put(_){} to get(){}}; { sbt := 3 }; sbt}, sbt] } # value: [null, 1] bug test: recognize the end of a scope box as ending shadowing ? { var sbt := 1; [{ def sbt := 2 }, sbt := 3; sbt] } # value: [2, 3] ? x := (x + 3) # value: 3 ? x # value: 3 ? x := (x + 5) # value: 8 ? x # value: 8 ? x := (x - 6) # value: 2 ? x # value: 2 ? x := (x := 3) - 1 # value: 2 ? x # value: 2 ? x := x # value: 2 ? x # value: 2 SeqExpr: ? null; x := 11; null; x + 1 # value: 12 ? x # value: 11 HideExpr and scoping: ? { def z := 100; z } # value: 100 ? z # problem: undefined variable: z EscapeExpr ? escape x { 40 } # value: 40 ? escape x { def escapeRes := 41; x(escapeRes) } # value: 41 scope check ? x # value: 11 ? escapeRes # problem: undefined variable: escapeRes disabling of ejector, and a non-simple pattern ? { def ej; escape bind ej {}; ej("oops"); print("end") } # problem: ? escape via(def _(_,e):any{throw.eject(e,"biff")}) _ { print("failing ejector pattern test") } # problem: biff ejector printing ? { def ej; escape bind ej {}; ej } # value: ? escape ej {print(ej)} # stdout: catch pattern's failure behavior ' ? escape x { x(1) } catch y :nullOk { [y] } # value: 1 catch pattern executes after unwinding ? escape x { try { x(1) } finally { print("bye!") } } catch via (def _(s,_) :any {print("hi!"); return s}) y { [y] } # stdout: bye!hi! # value: [1] EscapeExpr with catch ? escape x { 50 } catch v { v + 51 } # value: 50 ? escape x { x(60) } catch v { v + 61 } # value: 121 XXX catch pattern failure XXX multiple catch, and failure thereof EscapeExpr bug: ejector should be valid in the pattern ? escape via (fn ej,_ {ej("ok");ej}) ej { ej("oops") } # value: "ok" ? escape via (fn ej,_ {ej("ok");ej}) ej { ej("oops") } catch v { v + "." } # value: "ok." EscapeExpr optimizer testing There is an optimization routine which deletes block and ejector creation for EscapeExprs whose ejectors are not used. These are tests for visible failures. meta.getState can see it ? def _() :any { return escape x { meta.getState()["&x"] } } () # value: <& > failing patterns ? def _() :any { escape [x] {} } () # problem: the "org.erights.e.elib.base$ejector" doesn't coerce to a ConstList ? def _() :any { escape x :int {} } () # problem: the "org.erights.e.elib.base$ejector" doesn't coerce to an int List expressions for further testing ? [1, 2, 3] # value: [1, 2, 3] ? def tlist := [x, y, null] # value: [11, 2, null] ? tlist[0] # value: 11 ? tlist[1] # value: 2 ? [tlist[2]] # value: [null] FinalPattern guards ? escape e {[def x exit e := 92, x]} # value: [92, 92] ? escape e {[def x :tint exit e := 93, x]} # value: [93, 93] ? escape e {[def x :tfloat64 exit e := 94, x]} # value: [94, 94.0] ? escape e {[def x :tint exit e := 95.0, x]} # value: problem: the float64 95.0 doesn't coerce to an int MetaContextExpr ? meta.context() :DeepFrozen # value: ? meta.context().getFQNPrefix() # value: "__main$" ? meta.context().getSource() # problem: There is no enclosing object expression at __main$ ? [meta.context().getOptSource()] # value: [null] ? fn { meta.context().getSource() } () # value: e`def _ { # # method run() { # meta.context().getSource() # } # }` ? fn { meta.context().getOptSource() } () # value: e`def _ { # # method run() { # meta.context().getOptSource() # } # }` A MetaContextExpr must not produce a result which allows one to distinguish multiple evaluations of the same code; it must be always the same *or* always fresh. ? def expr := e`fn { meta.context() }` > def [f1, f2] := [expr.eval(safeScope), expr.eval(safeScope)] > (f1() == f1()) == (f1() == f2()) # value: true Furthermore, we choose that it be always the same, i.e. selfless. ? { (def c := meta.context()) == E.call(E, "call", c.__optUncall()) } # value: true MetaStateExpr ? (def _() :any { def x := ∫ return meta.getState() })() # value: ["&x" => <& >, "&__return" => <& <__return ejector>>, "&null" => <& null>, "&int" => , "&any" => <& any>] XXX toplevel meta.getState() ObjectExpr ? {def trivialObject {}} # value: ? def thing { > method foo() :any { x += 1 } > method bar() :any { y } > } # value: ? thing # value: ? thing.bar() # value: 2 ? thing.foo() # value: 12 ? thing.foo() # value: 13 ? { def x implements x {} } # problem: Apparent usage of 'implements' in the script is rejected (this test once tested that it was actual usage and allowed) ? { def x := 1; { def thing implements (def x {to audit(_) :any {return true}}) { to run() :any {return x} }; [x, thing()] }} # problem: Scope of as and implements ? def foo as (def x {to audit(_) {return true}}) implements x {} # value: Matcher ? def matches { > match msg { msg } > } # value: ? matches.hi("there") # value: ["hi", ["there"]] ? def nonmatches { > match ==null { "oops" } > } # value: ? nonmatches.hi("there") # problem: no such method: __main$nonmatches#hi/1 ? try { nonmatches.hi("there") } catch p {"ok"} # value: "ok" ? def maybes { > to regular() :any { return "yep" } > match [=="yes", args] { args[1] } > } # value: ? maybes.yes(1, 2) # value: 2 ? maybes.no(3, 4) # problem: no such method: __main$maybes#no/2 ? maybes.regular() # value: "yep" Bug: matcher catching non-overridden miranda methods. ? def matcherMirandaCheck { > match msg { throw(E.toQuote(msg)) } > } # value: In the bug, this is instead caught by the matcher. ? [matcherMirandaCheck.__optSealedDispatch(42)] # value: [null] Bug: incorrect scoping of the pattern of a matcher. (This can't happen any more in the new compiler design - xxx worth deleting? ') ? def makeMatcherClosure { > match msg { > fn { msg } > } > } # value: ? def match_c1 := makeMatcherClosure.first() # value: <...makeMatcherClosure$_> ? def match_c2 := makeMatcherClosure.second() # value: <...makeMatcherClosure$_> ? match_c1() # value: ["first", []] ? match_c2() # value: ["second", []] Plumbing ? def one match msg { E.callWithPair(1, msg) } # value: 1 ? one + 1 # value: 2 ? 2 + one # value: 3 ? one == 1 # value: false ? one.__conformTo(any) == 1 # value: true Plumbing expressions can have auditors ? def auditor { method audit(_,_) :any {true} } > def x implements auditor match msg { E.callWithPair("x", msg) } # value: "x" ? __auditedBy(auditor, x) # value: true ? __auditedBy(DeepFrozenStamp, x) # value: false Plumbing expressions can't have super ' ? def x extends y match msg {} # problem: (line 1)@17: expecting "{", found 'match' XXX will be 'syntax error', wording to change, gain file position info ObjectExpr printing When the ObjectExpr's name pattern is an IgnorePattern, the object's FQN prefix is used to provide a name. ? def foo() { return def _ {} }; foo() # value: <...foo$_> Alleged type ? /** object doc */ > def objectOfInterestingType { > to getNested() :notNull { > return def nestedObject {} > } > } # value: ? def ooitType := objectOfInterestingType.__getAllegedType() # value: ObjectOfInterestingType ? ooitType.getFQName() # value: "__main$objectOfInterestingType" ? objectOfInterestingType.getNested().__getAllegedType().getFQName() # value: "__main$objectOfInterestingType$nestedObject" ? def objectWithMatcherType { > to additionalVerb(_) :any {} > match [=="__getAllegedType", []] { ooitType } > } > (objectWithMatcherType.__getAllegedType().getMessageTypes() &! null.__getAllegedType().getMessageTypes()).getKeys() # value: ["additionalVerb/1", "getNested/0"] ? def objectWithNonTypeMatcher { > to soleVerb(_) :any {} > match [=="foo", []] { ooitType } > } > (objectWithNonTypeMatcher.__getAllegedType().getMessageTypes() &! null.__getAllegedType().getMessageTypes()).getKeys() # value: ["soleVerb/1"] ? def like(v) :any { return def conformer { to __conformTo(_) :any { return v }}} # value: ? def objectWithMatcherLikeType { > to additionalVerb(_) :any {} > match [=="__getAllegedType", []] { like(ooitType) } > } > (objectWithMatcherLikeType.__getAllegedType().getMessageTypes() &! null.__getAllegedType().getMessageTypes()).getKeys() # problem: no such method: __main$like$conformer#getMessageTypes/0 XXX is this appropriate behavior? ? def objectWithMatcherDuckType { > to additionalVerb(_) :any {} > match [=="__getAllegedType", []] { def _ extends ooitType {} } > } > (objectWithMatcherDuckType.__getAllegedType().getMessageTypes() &! null.__getAllegedType().getMessageTypes()).getKeys() # value: ["additionalVerb/1", "getNested/0"] XXX is this appropriate behavior? ObjectExpr auditing moved to audit.updoc IfExpr ? if (true) { "t" } else { "f" } # value: "t" ? if (false) { "t" } else { "f" } # value: "f" ? if ([false, true][0]) { "t" } else { "f" } # value: "f" ? if ([false, true][1]) { "t" } else { "f" } # value: "t" ? if (true) { "t" } # value: "t" ? [if (false) { "t" }] # value: [null] ? { def a := 1; if (def a := 2; true) { a } else { a } } # value: 2 ? { def a := 1; if (def a := 2; false) { a } else { a } } # value: 1 ? { if (def a := 2; true) { a } else { a } } # problem: undefined variable: a ? { if (def a := 2; false) { a } else { a } } # problem: undefined variable: a ? { if (def a := 2; true) { a }; a } # problem: undefined variable: a ? if (1) {"oops"} # problem: the int 1 doesn't coerce to a boolean ? if (def _ { to __conformTo(_) :any {return true} }) {"y"} else {"n"} # value: "y" these two further tests were written to debug the internal E-IS-TRUE used by IfExpr, not IfExpr itself ? if (def _ { to __conformTo(_) :any {return false} }) {"y"} else {"n"} # value: "n" ? if (def conformist { to __conformTo(_) :any {return conformist} }) {"y"} else {"n"} # problem: the "__main$conformist" doesn't coerce to a boolean FinallyExpr ? [try {} finally {}] # value: [null] ? try { 1 } finally { 2 } # value: 1 ? {var s := "a"; s += "b"; try { s += "c"; } finally { s += "d"; }; s += "e"; s } # value: "abcde" FinallyExpr/EscapeExpr interaction ? escape e { > try { > e("Will this be seen?") > } finally { > throw("No.") > } > throw("can't happen") > } # problem: No. ? escape e { > try { > e("Original") > } finally { > e("Replacement") > } > throw("can't happen") > } # value: "Replacement" CatchExpr XXX write sufficient tests former bug: catch pattern and body executed before internal finallys ? try { > try { print("body "); throw("biff") } finally { print("finally ") } > } catch p { print("catch ") } # stdout: body finally catch non-catching ? try { throw("hi") } catch =="irrelevant" { "oops" } # problem: hi backtrace is caught ? def p; try { throw("hi") } catch bind p {}; lisp["E.ELIB", "LOCAL-THROW-SEALED-BOX-BACKTRACE"].getFunction()(p :()) != null # value: true (NOTE: the result of L-T-S-B-B contains non-E-object functions, so manipulating it is dangerous) Trinary DefineExpr Equivalent to binary define ? def x :tint exit null := 9 # value: 9 ? x # value: 9 ? def x :String exit null := 9 # problem: the int 9 doesn't coerce to a String With non-null ejector ? def x :tint exit def _(e) :void { print(e); throw("biff") } := 9 # value: 9 ? def x :String exit def _(e) :void { print(e); throw("biff") } := 9 # stdout: problem: the int 9 doesn't coerce to a String # problem: biff Evaluation order ? def via(fn s,_ {println("patt");throw("exit")}) x \ > exit (println("ejector"); null) \ > := (println("specimen"); 8) # stdout: ejector # specimen # patt # # problem: exit Returning "ejector" XXX in Java-E this creates a note in the trace that the ejector returned and proceeds as if the ejector was null ? def x :String exit def _(_) :void {} := 9 # problem: optEjector <__main$_> returned: null Scoping ? {def y := 0; {def ==0 exit (def y := null) := y}} # problem: null is not 0 SlotExpr, SlotPattern, AssignExpr final slot ? &pi # value: <& 3.14159> ? (&pi).get() # value: 3.14159 ? (&pi).isFinal() # value: true ? (&pi).__optUncall() # value: [, "run", [3.14159]] Since this is not an AssignExpr but a SlotExpr and call, the error is that which results from FinalSlot#put, and is not statically caught. ? [(&pi).put(3)] # problem: not an assignable slot: <& 3.14159> ? if (false) { [(&pi).put(3)] } outer normal slot ? &any # value: <& any> ? (&any) :all[DeepFrozen, PassByCopy] # value: <& any> ? (&any).get() # value: any ? (&any).isFinal() # value: true ? (&any).__optUncall() # value: [, "run", [any]] ? [(&any).put(3)] # problem: not an assignable slot: <& any> outer lazy slot ? &require # value: ? (&require) :DeepFrozen # value: ? (&require).get() # value: ? (&require).isFinal() # value: true ? [(&require).__optUncall()] # value: [null] ? [(&require).put(3)] # problem: no such method: lazyApplySlot#put/1 var slots, slot patterns, assignment ? var slottest_a := 3.14 # value: 3.14 ? &slottest_a # value: ? (&slottest_a).get() # value: 3.14 ? (&slottest_a).isFinal() # value: false x ? (&slottest_a).__optUncall() x # value: [, "run", [3.14]] ? [(&slottest_a).put(3)] # value: [null] ? def slottest_slot := &slottest_a # value: ? def &slottest_b := slottest_slot # value: ? slottest_a # value: 3 ? slottest_b # value: 3 ? slottest_b := 3.14159 # value: 3.14159 ? slottest_a # value: 3.14159 uncall ? slottest_slot.__optUncall() # value: [, "run", [3.14159]] guarded SlotPattern, failure thereof ? def &spg :DeepFrozen := &any # value: <& any> ? spg # value: any ? def &spg :DeepFrozen := &slottest_a # problem: is not DeepFrozen XXX SlotPattern with a transforming guard custom slot ? def &perverse := { > var v := 0 > def perverseSlot { > to get() :any { return v += 1 } > to put(new :tint) :void { v += new * 100 } > to isFinal() :any { return false } > } > } # value: ? perverse # value: 1 ? perverse # value: 2 ? perverse := 1 # value: 1 ? perverse # value: 103 ? perverse := "a" # problem: the String "a" doesn't coerce to an int ? perverse # value: 104 BindingExpr, BindingPattern ? def makeCoercedSlot := > def CoercedSlot := makeCoercedSlot.asType() > def FinalSlot := __makeFinalSlot.asType() > null ? def foo :int := 1 # value: 1 ? def binding := &&foo # value: <& <& 1> :FinalSlot[int]> ? binding == binding :CoercedSlot # value: true ? def &&bar := binding > bar # value: 1 ? def &&baz := makeCoercedSlot(FinalSlot[int], __makeFinalSlot(2), null) > baz # value: 2 ? (&&baz).getGuard() # value: FinalSlot[int] ListPattern ? [1, 2, 3] =~ [lp_a, lp_b, lp_c :tfloat64] # value: true ? [lp_c, lp_b, lp_a] # value: [3.0, 2, 1] ? ["a", "b"] =~ [lp_a] # value: false ? &lp_a # value: ? def [_, _] := ["a"] # problem: a 1 size list doesn't match a 2 size list pattern ? "abc" =~ [lp_a, _, _] # value: true ? lp_a # value: 'a' ? 4 =~ [_] # value: false ? var listoid_i := 1 > def listoid { > to __conformTo(_) :any { return [listoid_i] * (listoid_i += 1) } > } # value: ? [listoid =~ [lp_a, _], lp_a] # value: [true, 1] ? listoid =~ [_, _, _] # value: true IgnorePattern ? 1 =~ _ # value: true ? Ref.promise()[0] =~ _ # value: true ? Ref.broken("biff") =~ _ # value: true ? throw("bang") =~ _ # problem: bang check for non-evaluation ? var x := 0; def _ := (x += 1); x # value: 1 ViaPattern ? pragma.enable("verb-curry") success ? escape a { [def via (def _(b,_):any{return b+1}) c exit a := 1, c] } # value: [1, 2] failure in via ? escape a { [def via (def _(_,b):any{throw.eject(b,"fail")}) c exit a := 1, c] } # value: problem: fail failure in subpattern ? escape a { [def via (def _(b,_):any{return b+1}) c :char exit a := 1, c] } # value: problem: the int 2 doesn't coerce to a char pattern noun ? escape via (any.coerce) aa { print(aa) } # stdout: XXX should have no name instead, since via may transform the specimen arbitrarily so the noun is unrelated? pattern param-desc ? def f(via (any.coerce) a) :any {}.__getAllegedType().getMessageTypes()["run/1"] # value: to run(a) :any --- Ejector identities --- A previous version of the compiler implemented an optimization to avoid allocating ejectors when not needed; it did this by allocating ejectors exactly when they needed to be reified by a ViaPattern or guarded pattern. This resulted in distinct ejector objects being created, which has been declared incorrect (kpreid & MarkM agree as of 2008-04-13). ? def e1 > def e2 > try { throw("bean") } catch via (fn s,bind e1{s}) \ > via (fn s,bind e2{s}) \ > _ { e1 == e2 } # value: true --- Miscellaneous scoping --- ? def outerDefined := 44 # value: 44 ? { def x := 1; [{ def x := 2; x }, x] } # value: [2, 1] ? { def x := 1; [( def x := 2; x ), x] } # problem: "x" already in scope ? { notOuterDefined + 1; def notOuterDefined := 2 } # problem: undefined variable: notOuterDefined ? {[notOuterDefined + 1, def notOuterDefined := 2]} # problem: undefined variable: notOuterDefined xxx This is what current Java-E does, but should it? Should we instead reject usage preceding shadowing define? ? { outerDefined + 1; def outerDefined := 2 } # value: 2 ? {[outerDefined + 1, def outerDefined := 2]} # value: [45, 2] XXX this should be tested with explicit node construction ? { def x :x := 1 } # problem: kernel FinalPattern may not use its own noun (e`x`) in its guard (e`x`) Bug: rebinding boundary was not being established by pure object expressions ? { def a := 1; def f { method run(a) :any { a } }; f(2) } # value: 2 ? { def a := 1; def f match a { a }; f(2) } # value: ["run", [2]] Source position in errors ? e__quasiParser.run("a".asFrom("file:///foo")).eval(safeScope) # problem: undefined variable: a @ ? e__quasiParser.run("null := 1".asFrom("file:///foo")).asKernelE().eval(safeScope) # problem: null is not an assignable variable @ XXX see MetaContextExpr implementation x ? e__quasiParser.run("meta.context().getSource()".asFrom("file:///foo")).eval(safeScope) x # problem: There is no enclosing object expression at __main$ @ XXX test perverse scoping cases: recursive defines pre-transform (raw-node-input case) def x := y && x static rejection (also ||) for x in x {} static rejection check that all cases of scope boxes allow redefinition, and that all kinds of defining patterns check for disallowed redefinitions XXX var slots should not double-coerce XXX remove all uses of MatchBindExpr since it's an import now