Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion lang/src/arr/compiler/anf-loop-compiler.arr
Original file line number Diff line number Diff line change
Expand Up @@ -2179,12 +2179,13 @@ fun compile-provides(provides):
j-field("origin", compile-origin(origin)),
j-field("typ", compile-provided-type(t))
]))
| v-fun(origin, t, name, flatness) =>
| v-fun(origin, t, name, doc, flatness) =>
j-field(v, j-obj([clist:
j-field("bind", j-str("fun")),
j-field("origin", compile-origin(origin)),
j-field("flatness", flatness.and-then(j-num).or-else(j-false)),
j-field("name", j-str(name)),
j-field("doc", j-str(doc)),
j-field("typ", compile-provided-type(t))
]))
end
Expand Down
55 changes: 54 additions & 1 deletion lang/src/arr/compiler/ast-util.arr
Original file line number Diff line number Diff line change
Expand Up @@ -1363,7 +1363,8 @@ fun canonicalize-value-export(ve :: CS.ValueExport, uri :: URI, tn):
| v-alias(o, n) => CS.v-alias(o, n)
| v-just-type(o, t) => CS.v-just-type(o, canonicalize-names(t, uri, tn))
| v-var(o, t) => CS.v-var(o, canonicalize-names(t, uri, tn))
| v-fun(o, t, name, flatness) => CS.v-fun(o, canonicalize-names(t, uri, tn), name, flatness)
| v-fun(o, t, name, doc, flatness) =>
CS.v-fun(o, canonicalize-names(t, uri, tn), name, doc, flatness)
end
end

Expand Down Expand Up @@ -1546,3 +1547,55 @@ fun get-typed-provides(resolved, typed :: TCS.Typed, uri :: URI, compile-env ::
end
end
end

fun get-fun-hover-info(expr :: A.Expr, visitor) -> {String; A.Ann}:
# empty string sounds bad but we already are parsing missing docstrings
# into the empty string, so we have to detect and elide anyways...
# similarly, we detect and elide empty annotations,
# so that is a fine default too.
doc: ```
Extracts the docstring if one exists; empty string otherwise.
Turns annotations on function-like expressions into arrow annotation;
empty annotation otherwise.
Assumes all tuple annotations have been desugared.
```

fun piece-into-arrow(params :: List<A.Bind>, ret :: A.Ann) -> A.Ann:
a-field-params = for map(p from params):
# s-tuple-binds should be gone by now
A.a-field(p.l, p.id.tosourcestring(), p.ann)
end
A.a-arrow-argnames(A.dummy-loc, a-field-params, ret, false)
end

# we have to visit the resulting arrow ann to resolve names.
# while doing so, names bound as type parameters in the expr need to be dealt with
# (otherwise they would be unbound).
# The proper thing to do would be to use `a-forall`, which doesn't exist,
# so we use `a-any` as a last resort (at the cost of worse hovering).
fun tparam-visitor(tparams :: List<A.Name>):
tparam-names = tparams.map(_.toname())
visitor.{
method a-name(self, l, id):
if A.is-s-name(id) and tparam-names.member(id.s):
# TODO: fix this!
A.a-any(l)
else:
visitor.a-name(l, id)
end
end
}
end

cases(A.Expr) expr:
| s-lam(_, _, tparams, params, ann, doc, _, _, _, _) =>
{doc; piece-into-arrow(params, ann).visit(tparam-visitor(tparams))}
| s-fun(_, _, tparams, _, params, ann, doc, _, _, _, _) =>
{doc; piece-into-arrow(params, ann).visit(tparam-visitor(tparams))}
| s-method(_, _, tparams, params, ann, doc, _, _, _, _) =>
{doc; piece-into-arrow(params, ann).visit(tparam-visitor(tparams))}
| s-method-field(_, _, tparams, params, ann, doc, _, _, _, _) =>
{doc; piece-into-arrow(params, ann).visit(tparam-visitor(tparams))}
| else => {""; A.a-blank}
end
end
11 changes: 7 additions & 4 deletions lang/src/arr/compiler/compile-structs.arr
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,9 @@ data ValueBind:
origin :: BindOrigin,
binder :: ValueBinder,
atom :: A.Name,
ann :: A.Ann)
ann :: A.Ann,
# the (possible empty) doc string for hovering over this (value) binding
doc :: String)
end

data TypeBinder:
Expand Down Expand Up @@ -405,7 +407,7 @@ data ValueExport:
| v-alias(origin :: BindOrigin, original-name :: String)
| v-just-type(origin :: BindOrigin, t :: T.Type)
| v-var(origin :: BindOrigin, t :: T.Type)
| v-fun(origin :: BindOrigin, t :: T.Type, name :: String, flatness :: Option<Number>)
| v-fun(origin :: BindOrigin, t :: T.Type, name :: String, doc :: String, flatness :: Option<Number>)
end

data DataExport:
Expand Down Expand Up @@ -437,7 +439,8 @@ fun value-export-from-raw(uri, val-export, tyvar-env :: SD.StringDict<T.Type>) -
t = val-export.tag
typ = type-from-raw(uri, val-export.typ, tyvar-env)
if t == "v-fun":
v-fun(typ, t, none)
# TODO (ZACK): WTF is up with this?
v-fun(typ, t, none, none)
else:
v-just-type(typ)
end
Expand Down Expand Up @@ -587,7 +590,7 @@ fun provides-from-raw-provides(uri, raw):
else:
none
end
vdict.set(v.name, v-fun(origin, type-from-raw(uri, v.value.typ, SD.make-string-dict()), v.value.name, flatness))
vdict.set(v.name, v-fun(origin, type-from-raw(uri, v.value.typ, SD.make-string-dict()), v.value.name, v.value.doc, flatness))
else:
origin = origin-from-raw(uri, v.value.origin, v.name)
vdict.set(v.name, v-just-type(origin, type-from-raw(uri, v.value.typ, SD.make-string-dict())))
Expand Down
4 changes: 2 additions & 2 deletions lang/src/arr/compiler/desugar.arr
Original file line number Diff line number Diff line change
Expand Up @@ -139,13 +139,13 @@ end

fun mk-id-ann(loc, base, ann) block:
a = names.make-atom(loc, base)
generated-binds.set-now(a.key(), C.value-bind(C.bo-local(loc, a), C.vb-let, a, ann))
generated-binds.set-now(a.key(), C.value-bind(C.bo-local(loc, a), C.vb-let, a, ann, ""))
{ id: a, id-b: A.s-bind(loc, false, a, ann), id-e: A.s-id(loc, a) }
end

fun mk-id-var-ann(loc, base, ann) block:
a = names.make-atom(loc, base)
generated-binds.set-now(a.key(), C.value-bind(C.bo-local(loc, a), C.vb-var, a, ann))
generated-binds.set-now(a.key(), C.value-bind(C.bo-local(loc, a), C.vb-var, a, ann, ""))
{ id: a, id-b: A.s-bind(loc, false, a, ann), id-e: A.s-id-var(loc, a) }
end

Expand Down
14 changes: 10 additions & 4 deletions lang/src/arr/compiler/flatness.arr
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,7 @@ fun get-flatness-for-module-fun(id, field, mb, env) -> Flatness:
| none => none
| some(value-export) =>
cases(C.ValueExport) value-export:
| v-fun(_, _, _, flatness) =>
| v-fun(_, _, _, _, flatness) =>
flatness
| else => none
end
Expand Down Expand Up @@ -452,7 +452,7 @@ fun make-prog-flatness-env(anfed :: AA.AProg, post-env :: C.ComputedEnvironment,
| none => nothing
| some(ve) =>
cases(C.ValueExport) ve:
| v-fun(_, _, _, flatness) => sd.set-now(vb.atom.key(), flatness)
| v-fun(_, _, _, _, flatness) => sd.set-now(vb.atom.key(), flatness)
| else => nothing
end
end
Expand All @@ -462,7 +462,7 @@ fun make-prog-flatness-env(anfed :: AA.AProg, post-env :: C.ComputedEnvironment,
raise("The name: " + vb.atom.toname() + " could not be found on the module " + vb.origin.uri-of-definition)
| some(value-export) =>
cases(C.ValueExport) value-export:
| v-fun(_, _, _, flatness) =>
| v-fun(_, _, _, _, flatness) =>
sd.set-now(k, flatness)
| else =>
nothing
Expand Down Expand Up @@ -568,10 +568,16 @@ fun get-flat-provides(provides, env, post-env, { flatness-env; _ }, ast) block:
| v-alias(origin, name) => env.value-by-uri-value(origin.uri-of-definition, origin.original-name.toname())
| else => ve
end
existing-doc = cases(C.ValueExport) existing-val:
| v-fun(_, _, _, doc, _) => doc
| else => ""
end
cases(Option) maybe-flatness:
| none => ve
| some(flatness-result) =>
C.v-fun(ve.origin, existing-val.t, k, flatness-result)
# only fall back to the existing doc if we have to
doc = if bind.doc == "": existing-doc else: bind.doc end
C.v-fun(ve.origin, existing-val.t, k, doc, flatness-result)
end
end
s.set(k, new-val)
Expand Down
66 changes: 37 additions & 29 deletions lang/src/arr/compiler/resolve-scope.arr
Original file line number Diff line number Diff line change
Expand Up @@ -767,17 +767,12 @@ fun resolve-names(p :: A.Program, thismodule-uri :: String, initial-env :: C.Com
cases(Option) val-info block:
| none => raise("The value is a global that doesn't exist in any module: " + name)
| some(shadow val-info) =>
cases(C.ValueExport) val-info block:
| v-var(_, t) =>
b = C.value-bind(C.bo-global(some(origin), uri-of-definition, origin.original-name), C.vb-var, names.s-global(A.dummy-loc, name), A.a-blank)
bindings.set-now(names.s-global(A.dummy-loc, name).key(), b)
acc.set-now(name, b)
| else =>
# TODO(joe): Good place to add _location_ to valueexport to report errs better
b = C.value-bind(C.bo-global(some(origin), uri-of-definition, origin.original-name), C.vb-let, names.s-global(A.dummy-loc, name), A.a-blank)
bindings.set-now(names.s-global(A.dummy-loc, name).key(), b)
acc.set-now(name, b)
end
vbinder = if C.is-v-var(val-info): C.vb-var else: C.vb-let end
doc = if C.is-v-fun(val-info): val-info.doc else: "" end
b = C.value-bind(C.bo-global(some(origin), uri-of-definition, origin.original-name),
vbinder, names.s-global(A.dummy-loc, name), A.a-blank, doc)
bindings.set-now(names.s-global(A.dummy-loc, name).key(), b)
acc.set-now(name, b)
end
end
acc.freeze()
Expand Down Expand Up @@ -813,8 +808,12 @@ fun resolve-names(p :: A.Program, thismodule-uri :: String, initial-env :: C.Com
# TODO(joe): I think that b.b.ann.visit below could be wrong if
# a letrec'd ID is used in a refinement within the same letrec,
# so state may be necessary here
ann = b.b.ann.visit(visitor)
{doc; computed-fun-ann} = U.get-fun-hover-info(b.value, visitor)
# only override if there is no annotation written
shadow ann = if A.is-a-blank(ann): computed-fun-ann else: ann end
atom-env = make-atom-for(b.b.id, b.b.shadows, env, bindings,
C.value-bind(C.bo-local(b.l, b.b.id), C.vb-letrec, _, b.b.ann.visit(visitor)))
C.value-bind(C.bo-local(b.l, b.b.id), C.vb-letrec, _, ann, doc))
{ atom-env.env; link(atom-env.atom, atoms) }
end
new-visitor = visitor.{env: env}
Expand Down Expand Up @@ -864,7 +863,7 @@ fun resolve-names(p :: A.Program, thismodule-uri :: String, initial-env :: C.Com
fun handle-column-binds(column-binds :: A.ColumnBinds, visitor):
env-and-binds = for fold(acc from { env: visitor.env, cbs: [list: ] }, cb from column-binds.binds):
atom-env = make-atom-for(cb.id, cb.shadows, acc.env, bindings,
C.value-bind(C.bo-local(cb.l, cb.id), C.vb-let, _, cb.ann.visit(visitor)))
C.value-bind(C.bo-local(cb.l, cb.id), C.vb-let, _, cb.ann.visit(visitor), ""))
new-cb = A.s-bind(cb.l, cb.shadows, atom-env.atom, cb.ann.visit(visitor.{env: acc.env}))
{ env: atom-env.env, cbs: link(new-cb, acc.cbs) }
end
Expand All @@ -878,19 +877,22 @@ fun resolve-names(p :: A.Program, thismodule-uri :: String, initial-env :: C.Com
"$included-" + to-string(include-counter)
end

# ZACK: revisit like above for module related stuff...
fun add-value-name(l, imp-loc, env, vname, as-name, mod-info):
maybe-value-export = mod-info.values.get(vname.toname())
cases(Option) maybe-value-export block:
| none =>
name-errors := link(C.name-not-provided(l, imp-loc, vname, "value"), name-errors)
env
| some(value-export) =>
vbinder = cases(C.ValueExport) value-export block:
| v-var(_, t) => C.vb-var
| else => C.vb-let
end
vbinder = if C.is-v-var(value-export): C.vb-var else: C.vb-let end
doc = if C.is-v-fun(value-export): value-export.doc else: "" end
atom-env = make-import-atom-for(as-name, value-export.origin.uri-of-definition, env, bindings,
C.value-bind(C.bo-module(as-name.l, value-export.origin.definition-bind-site, value-export.origin.uri-of-definition, value-export.origin.original-name), vbinder, _, A.a-any(vname.l)))
C.value-bind(C.bo-module(as-name.l,
value-export.origin.definition-bind-site,
value-export.origin.uri-of-definition,
value-export.origin.original-name),
vbinder, _, A.a-any(vname.l), doc))
atom-env.env
end
end
Expand Down Expand Up @@ -1474,8 +1476,9 @@ fun resolve-names(p :: A.Program, thismodule-uri :: String, initial-env :: C.Com
# TODO(joe): What should the TypeBindTyp be here?
atom-env-t = make-atom-for(name, false, te, type-bindings,
C.type-bind(C.bo-local(l2, name), C.tb-type-let, _, C.tb-none))
# ZACK TODO: wtf is this??
atom-env = make-atom-for(tname, false, e, bindings,
C.value-bind(C.bo-local(l2, tname), C.vb-let, _, A.a-blank))
C.value-bind(C.bo-local(l2, tname), C.vb-let, _, A.a-blank, "ZACK what this"))
new-bind = A.s-newtype-bind(l2, atom-env-t.atom, atom-env.atom)
{ atom-env.env; atom-env-t.env; link(new-bind, bs) }
end
Expand All @@ -1488,11 +1491,14 @@ fun resolve-names(p :: A.Program, thismodule-uri :: String, initial-env :: C.Com
{e; bs; atoms} = acc
cases(A.LetBind) b block:
| s-let-bind(l2, bind, expr) =>
visited-ann = bind.ann.visit(self.{env: e})
ann = bind.ann.visit(self.{env: e})
{doc; computed-fun-ann} = U.get-fun-hover-info(b.value, self.{env: e})
# only override if there is no annotation written
shadow ann = if A.is-a-blank(ann): computed-fun-ann else: ann end
atom-env = make-atom-for(bind.id, bind.shadows, e, bindings,
C.value-bind(C.bo-local(l2, bind.id), C.vb-let, _, visited-ann))
C.value-bind(C.bo-local(l2, bind.id), C.vb-let, _, ann, doc))
visit-expr = expr.visit(self.{env: e})
new-bind = A.s-let-bind(l2, A.s-bind(l2, bind.shadows, atom-env.atom, visited-ann), visit-expr)
new-bind = A.s-let-bind(l2, A.s-bind(l2, bind.shadows, atom-env.atom, ann), visit-expr)
{
atom-env.env;
link(new-bind, bs);
Expand All @@ -1501,7 +1507,8 @@ fun resolve-names(p :: A.Program, thismodule-uri :: String, initial-env :: C.Com
| s-var-bind(l2, bind, expr) =>
visited-ann = bind.ann.visit(self.{env: e})
atom-env = make-atom-for(bind.id, bind.shadows, e, bindings,
C.value-bind(C.bo-local(l2, bind.id), C.vb-var, _, visited-ann))
# ZACK TODO: we can't do anything here right...
C.value-bind(C.bo-local(l2, bind.id), C.vb-var, _, visited-ann, ""))
visit-expr = expr.visit(self.{env: e})
new-bind = A.s-var-bind(l2, A.s-bind(l2, bind.shadows, atom-env.atom, visited-ann), visit-expr)
{
Expand All @@ -1526,7 +1533,7 @@ fun resolve-names(p :: A.Program, thismodule-uri :: String, initial-env :: C.Com
cases(A.ForBind) fb block:
| s-for-bind(l2, bind, val) =>
atom-env = make-atom-for(bind.id, bind.shadows, env, bindings,
C.value-bind(C.bo-local(l2, bind.id), C.vb-let, _, bind.ann.visit(self)))
C.value-bind(C.bo-local(l2, bind.id), C.vb-let, _, bind.ann.visit(self), ""))
new-bind = A.s-bind(bind.l, bind.shadows, atom-env.atom, bind.ann.visit(self.{env: env}))
visit-val = val.visit(self)
new-fb = A.s-for-bind(l2, new-bind, visit-val)
Expand All @@ -1539,7 +1546,7 @@ fun resolve-names(p :: A.Program, thismodule-uri :: String, initial-env :: C.Com
{env; atoms} = for fold(acc from { self.env; empty }, a from args.map(_.bind)):
{env; atoms} = acc
atom-env = make-atom-for(a.id, a.shadows, env, bindings,
C.value-bind(C.bo-local(a.l, a.id), C.vb-let, _, a.ann.visit(self)))
C.value-bind(C.bo-local(a.l, a.id), C.vb-let, _, a.ann.visit(self), ""))
{ atom-env.env; link(atom-env.atom, atoms) }
end
new-args = for map2(a from args, at from atoms.reverse()):
Expand Down Expand Up @@ -1580,7 +1587,7 @@ fun resolve-names(p :: A.Program, thismodule-uri :: String, initial-env :: C.Com
{env; atoms} = for fold(acc from { with-params.env; empty }, a from args):
{env; atoms} = acc
atom-env = make-atom-for(a.id, a.shadows, env, bindings,
C.value-bind(C.bo-local(a.l, a.id), C.vb-let, _, a.ann.visit(with-params)))
C.value-bind(C.bo-local(a.l, a.id), C.vb-let, _, a.ann.visit(with-params), ""))
{ atom-env.env; link(atom-env.atom, atoms) }
end
new-args = for map2(a from args, at from atoms.reverse()):
Expand Down Expand Up @@ -1608,7 +1615,7 @@ fun resolve-names(p :: A.Program, thismodule-uri :: String, initial-env :: C.Com
{env; atoms} = for fold(acc from { with-params.env; empty }, a from args):
{env; atoms} = acc
atom-env = make-atom-for(a.id, a.shadows, env, bindings,
C.value-bind(C.bo-local(a.l, a.id), C.vb-let, _, a.ann.visit(with-params)))
C.value-bind(C.bo-local(a.l, a.id), C.vb-let, _, a.ann.visit(with-params), ""))
{ atom-env.env; link(atom-env.atom, atoms) }
end
new-args = for map2(a from args, at from atoms.reverse()):
Expand All @@ -1631,7 +1638,7 @@ fun resolve-names(p :: A.Program, thismodule-uri :: String, initial-env :: C.Com
{env; atoms} = for fold(acc from { with-params.env; empty }, a from args):
{env; atoms} = acc
atom-env = make-atom-for(a.id, a.shadows, env, bindings,
C.value-bind(C.bo-local(a.l, a.id), C.vb-let, _, a.ann.visit(with-params)))
C.value-bind(C.bo-local(a.l, a.id), C.vb-let, _, a.ann.visit(with-params), ""))
{ atom-env.env; link(atom-env.atom, atoms) }
end
new-args = for map2(a from args, at from atoms.reverse()):
Expand Down Expand Up @@ -1721,7 +1728,8 @@ fun resolve-names(p :: A.Program, thismodule-uri :: String, initial-env :: C.Com
new-bind = cases(A.Bind) bind:
| s-bind(l2, shadows, name, ann) =>
atom-env = make-atom-for(name, true, self.env, bindings,
C.value-bind(C.bo-local(l2, name), C.vb-let, _, ann.visit(self)))
# ZACK TODO: maybe improve this?
C.value-bind(C.bo-local(l2, name), C.vb-let, _, ann.visit(self), "VARIANT"))
A.s-bind(l2, shadows, atom-env.atom, ann.visit(self))
end
A.s-variant-member(l, typ, new-bind)
Expand Down
2 changes: 2 additions & 0 deletions lang/src/js/base/type-util.js
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ define("pyret-base/js/type-util", [], function() {
origin: origin,
bind: "fun",
name: value.name || "",
doc: value.doc || "",
flatness: flatness,
typ: toPyretType(runtime, expandType(value.typ, shorthands))
});
Expand Down Expand Up @@ -287,6 +288,7 @@ define("pyret-base/js/type-util", [], function() {
origin: typ.origin,
flatness: typ.flatness,
name: typ.name,
doc: typ.doc || "",
typ: expandType(typ.typ, shorthands)
};
}
Expand Down
Loading