Skip to content

Commit

Permalink
Type and specialize brackets
Browse files Browse the repository at this point in the history
  • Loading branch information
cosine committed Apr 18, 2022
1 parent 10e5699 commit d62852d
Show file tree
Hide file tree
Showing 12 changed files with 94 additions and 46 deletions.
8 changes: 8 additions & 0 deletions integration_tests/test_data/bracket.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
let x = {};
let f = "my field";
x[f] = 12;
console.log(x[f]);
let i = 0;
let y = [10];
y[i] = 13;
console.log(y[i]);
2 changes: 2 additions & 0 deletions integration_tests/test_data/bracket.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
12
13
4 changes: 2 additions & 2 deletions libjankscripten/src/jankyscript/constructors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ pub fn dot_<I: Into<Id>>(a: Expr, b: I, s: Pos) -> Expr {
Expr::Dot(Box::new(a), b.into(), s)
}

pub fn bracket_(a: Expr, b: Expr, s: Pos) -> Expr {
Expr::Bracket(Box::new(a), Box::new(b), s)
pub fn bracket_(a: Expr, b: Expr, t: Type, s: Pos) -> Expr {
Expr::Bracket(Box::new(a), Box::new(b), t, s)
}

pub fn binary_(op: super::super::notwasm::syntax::BinaryOp, e1: Expr, e2: Expr, s: Pos) -> Expr {
Expand Down
9 changes: 7 additions & 2 deletions libjankscripten/src/jankyscript/from_js.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ fn lvalue(lval: Js::LValue) -> LValue {
match lval {
Js::Id(id) => Id(id, Type::Missing),
Js::Dot(e, x) => Dot(expr(e), x),
Js::Bracket(e1, e2) => Bracket(expr(e1), expr(e2)),
Js::Bracket(e1, e2) => Bracket(expr(e1), expr(e2), Default::default()),
}
}

Expand All @@ -27,7 +27,12 @@ fn expr(e: Js::Expr) -> Expr {
E::This => unexpected(e),
E::Id(id, s) => Expr::Id(id, Type::Missing, s),
E::Dot(e, x, s) => Expr::Dot(Box::new(expr(*e)), x, s),
E::Bracket(e1, e2, s) => Expr::Bracket(Box::new(expr(*e1)), Box::new(expr(*e2)), s),
E::Bracket(e1, e2, s) => Expr::Bracket(
Box::new(expr(*e1)),
Box::new(expr(*e2)),
Default::default(),
s,
),
E::New(_, _, _) => unexpected(e),
E::Unary(op, e, s) => Expr::JsOp(JsOp::Unary(op), vec![expr(*e)], Default::default(), s),
E::Binary(BinOp::BinaryOp(op), e1, e2, s) => Expr::JsOp(
Expand Down
4 changes: 2 additions & 2 deletions libjankscripten/src/jankyscript/fv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ fn fv_lv(lv: &mut LValue) -> IdMap {
Id(x, ty) => IdMap::unit(x.clone(), ty.clone()),
// the "id" in dot is really a field
Dot(e, _) => fv_expr(e),
Bracket(e1, e2) => fv_expr(e1).union(fv_expr(e2)),
Bracket(e1, e2, _) => fv_expr(e1).union(fv_expr(e2)),
}
}

Expand Down Expand Up @@ -97,7 +97,7 @@ fn fv_expr(expr: &mut Expr) -> IdMap {
| NewRef(e, ..)
| Deref(e, ..)
| Length(e, ..) => fv_expr(e),
Bracket(e1, e2, _) => fv_expr(e1).union(fv_expr(e2)),
Bracket(e1, e2, _, _) => fv_expr(e1).union(fv_expr(e2)),
Binary(_, e1, e2, _) => fv_expr(e1).union(fv_expr(e2)),
Assign(lv, e, _) => fv_lv(lv).union(fv_expr(e)),
Call(e, es, _) | MethodCall(e, _, es, _, _) => {
Expand Down
8 changes: 6 additions & 2 deletions libjankscripten/src/jankyscript/pretty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,9 @@ impl Pretty for LValue {
.append(pp.text("."))
.append(pp.line_())
.append(pp.as_string(id)),
LValue::Bracket(e1, e2) => e1.pretty(pp).append(e2.pretty(pp).brackets()),
LValue::Bracket(e1, e2, t) => {
prettyp!(pp, (seq (id e1) "<" (id t) ">" (brackets (id e2))))
}
}
}
}
Expand Down Expand Up @@ -183,7 +185,9 @@ impl Pretty for Expr {
pp.line_(),
pp.as_string(id),
]),
Expr::Bracket(e1, e2, _) => pp.concat(vec![e1.pretty(pp), e2.pretty(pp).brackets()]),
Expr::Bracket(e1, e2, t, _) => {
prettyp!(pp, (seq (id e1) "<" (id t) ">" (brackets (id e2))))
}
Expr::Unary(op, e, _) => op.pretty(pp).append(e.pretty(pp).parens()),
Expr::Binary(op, e1, e2, _) => pp.intersperse(
vec![
Expand Down
6 changes: 4 additions & 2 deletions libjankscripten/src/jankyscript/syntax.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ impl UnaryOp {
pub enum LValue {
Id(Id, Type),
Dot(Expr, Id),
Bracket(Expr, Expr),
Bracket(Expr, Expr, Type),
}

#[derive(Debug, PartialEq)]
Expand Down Expand Up @@ -122,7 +122,9 @@ pub enum Expr {
Object(Vec<(Key, Expr)>, Pos),
Id(Id, Type, Pos),
Dot(Box<Expr>, Id, Pos),
Bracket(Box<Expr>, Box<Expr>, Pos),
/// TODO(luna): required for links. Add a type annotation. Compile
/// appropriately to notwasm
Bracket(Box<Expr>, Box<Expr>, Type, Pos),
/// A JavaScript operator.
JsOp(JsOp, Vec<Expr>, JsOpTypeinf, Pos),
Unary(UnaryOp, Box<Expr>, Pos),
Expand Down
18 changes: 14 additions & 4 deletions libjankscripten/src/jankyscript/type_checking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -336,9 +336,14 @@ fn type_check_expr(expr: &Expr, env: Env) -> TypeCheckingResult<Type> {

Ok(rval_ty)
}
LValue::Bracket(e, computed_prop) => {
LValue::Bracket(e, computed_prop, t) => {
// e should be well typed
type_check_expr(e, env.clone())?;
ensure(
"bracket type",
t.clone(),
type_check_expr(e, env.clone())?,
s,
)?;

// computed_prop should be well typed
type_check_expr(computed_prop, env.clone())?;
Expand Down Expand Up @@ -419,8 +424,13 @@ fn type_check_expr(expr: &Expr, env: Env) -> TypeCheckingResult<Type> {
// it'll return undefined (in non-strict mode).
Ok(Type::Any)
}
Expr::Bracket(obj, dyn_prop, s) => {
let obj_type = type_check_expr(obj, env.clone())?;
Expr::Bracket(obj, dyn_prop, t, s) => {
let obj_type = ensure(
"bracket type",
t.clone(),
type_check_expr(obj, env.clone())?,
s,
)?;

ensure_indexable("brackets object", obj_type, s.clone())?;

Expand Down
15 changes: 11 additions & 4 deletions libjankscripten/src/jankyscript/typeinf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -382,11 +382,18 @@ impl<'a> Typeinf<'a> {
// Maybe we can say o[f] always has f : int? But that seems pretty
// strong. It would be nice though, because then we definitely
// wouldn't have to add an (any, any) -> any bracket operation
fn cgen_bracket(&mut self, o: &mut Expr, f: &mut Expr, p: &mut Pos) -> ast::Bool<'a> {
fn cgen_bracket(
&mut self,
o: &mut Expr,
f: &mut Expr,
t: &mut Type,
p: &mut Pos,
) -> ast::Bool<'a> {
let (phi_1, ot) = self.cgen_expr(o);
let (phi_2, ft) = self.cgen_expr(f);
// final container type
let otf = self.fresh_metavar("o");
*t = otf.clone();
let ftf = self.fresh_metavar("f");
let wcoerce = self.fresh_weight();
let wrt = self.fresh_weight();
Expand Down Expand Up @@ -704,7 +711,7 @@ impl<'a> Typeinf<'a> {
self.wobbly(p.clone(), expr, z3f!(self, true), t)
}
Expr::Dot(obj_e, x, p) => (self.cgen_dot(obj_e, x, p), Type::Any),
Expr::Bracket(o, f, p) => (self.cgen_bracket(o, f, p), Type::Any),
Expr::Bracket(o, f, t, p) => (self.cgen_bracket(o, f, t, p), Type::Any),
Expr::JsOp(op, args, JsOpTypeinf { op_metavar }, p) => {
let w = self.fresh_weight();
// Fresh metavariable for the operator that we will select, stored in the AST for
Expand Down Expand Up @@ -795,9 +802,9 @@ impl<'a> Typeinf<'a> {
let phi_3 = z3f!(self, (= (tid e_t) (typ any)));
(phi_1 & phi_2 & phi_3, Type::Any)
}
LValue::Bracket(o, f) => {
LValue::Bracket(o, f, t) => {
let (phi_1, e_t) = self.cgen_expr(&mut *e);
let phi_2 = self.cgen_bracket(o, f, p);
let phi_2 = self.cgen_bracket(o, f, t, p);
let phi_3 = z3f!(self, (= (tid e_t) (typ any)));
(phi_1 & phi_2 & phi_3, Type::Any)
}
Expand Down
11 changes: 9 additions & 2 deletions libjankscripten/src/jankyscript/walk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -316,8 +316,14 @@ where
self.walk_expr(obj, &loc);
self.walk_type(typ, &loc);
}
Bracket(ea, eb, t, _) => {
let loc = Loc::Node(Context::Expr, loc);
self.walk_expr(ea, &loc);
self.walk_expr(eb, &loc);
self.walk_type(t, &loc);
}
// 2xExpr
Bracket(ea, eb, _) | Binary(.., ea, eb, _) | Store(ea, eb, ..) => {
Binary(.., ea, eb, _) | Store(ea, eb, ..) => {
let loc = Loc::Node(Context::Expr, loc);
self.walk_expr(ea, &loc);
self.walk_expr(eb, &loc);
Expand All @@ -344,10 +350,11 @@ where
let loc = Loc::Node(Context::LValue, loc);
self.walk_expr(e, &loc);
}
Bracket(ea, eb) => {
Bracket(ea, eb, t) => {
let loc = Loc::Node(Context::LValue, loc);
self.walk_expr(ea, &loc);
self.walk_expr(eb, &loc);
self.walk_type(t, &loc);
}
}
}
Expand Down
53 changes: 28 additions & 25 deletions libjankscripten/src/notwasm/from_jankyscript.rs
Original file line number Diff line number Diff line change
Expand Up @@ -293,18 +293,20 @@ fn compile_expr<'a>(state: &'a mut S, expr: J::Expr, cxt: C<'a>) -> Rope<Stmt> {
*expr,
C::a(move |state, a| cxt.recv_a(state, unary_(op, a, p))),
),
// TODO(luna): i think JankyScript bracket supports like
// object/hashtable fetch by name, so we have to descriminate based
// on type or something(?)
J::Expr::Bracket(arr, index, p) => compile_expr(
J::Expr::Bracket(c, f, t, p) => compile_expr(
state,
*arr,
C::a(move |state, arr| {
*c,
C::a(move |state, c| {
compile_expr(
state,
*index,
C::a(move |state, index| {
cxt.recv_a(state, prim_app_("array_index", vec![arr, index], p))
*f,
C::a(move |state, f| match t {
J::Type::Array => {
cxt.recv_a(state, prim_app_("array_index", vec![c, f], p))
}
J::Type::DynObject => cxt.recv_a(state, object_get_(c, f, p)),
J::Type::String => todo!("string index???"),
_ => panic!("non-array non-object index"),
}),
)
}),
Expand Down Expand Up @@ -388,22 +390,23 @@ fn compile_expr<'a>(state: &'a mut S, expr: J::Expr, cxt: C<'a>) -> Rope<Stmt> {
}),
)
}
J::LValue::Bracket(container, field) => {
// TODO(luna): don't assume bracket is array
compile_expr(
state,
container,
C::a(move |state, cont| {
compile_expr(
state,
field,
C::a(move |state, f| {
cxt.recv_e(state, Expr::ArraySet(cont, f, a, p))
}),
)
}),
)
}
J::LValue::Bracket(container, field, typ) => compile_expr(
state,
container,
C::a(move |state, cont| {
compile_expr(
state,
field,
C::a(move |state, f| match typ {
J::Type::Array => cxt.recv_e(state, Expr::ArraySet(cont, f, a, p)),
J::Type::DynObject => {
cxt.recv_e(state, Expr::ObjectSet(cont, f, a, p))
}
_ => panic!("bad bracket lvalue type"),
}),
)
}),
),
}),
),
J::Expr::PrimCall(prim_name, args, p) => {
Expand Down
2 changes: 1 addition & 1 deletion libjankscripten/src/shared/id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ pub enum Id {
/// be replaced with another identifier. Instead of using `Option<Id>`, which is very
/// irritating, we replace the identifier with `Bogus`. So, don't construct a `Bogus` unless you
/// are sure you don't need it.
#[display(fmt = "bogus({})", _0)]
#[display(fmt = "bogus_{}", _0)]
Bogus(&'static str),
}

Expand Down

0 comments on commit d62852d

Please sign in to comment.