M src/ast.rs +5 -0
@@ 217,6 217,11 @@ pub enum Expr {
ArrayCtor {
body: Vec<Expr>,
},
+ /// Array element reference
+ ArrayRef {
+ expr: Box<Expr>,
+ idx: Box<Expr>,
+ },
/// Tuple element reference
TupleRef {
expr: Box<Expr>,
M src/backend/rust.rs +8 -0
@@ 478,6 478,14 @@ fn compile_expr(expr: &hir::ExprNode, tc
accm += ")\n";
accm
}
+ E::ArrayRef { expr, idx } => {
+ // TODO: We don't actually have usize types yet, so...
+ format!(
+ "{}[{} as usize]",
+ compile_expr(expr, tck),
+ compile_expr(idx, tck)
+ )
+ }
E::StructRef { expr, elt } => {
// panic!("Should never happen, structs should always be tuples by now!");
format!("{}.{}", compile_expr(expr, tck), elt)
M src/format.rs +6 -0
@@ 233,6 233,12 @@ fn unparse_expr(e: &Expr, indent: usize,
}
write!(out, "}}")
}
+ E::ArrayRef { expr, idx } => {
+ unparse_expr(&*expr, indent, out)?;
+ write!(out, "[")?;
+ unparse_expr(&*idx, 0, out)?;
+ write!(out, "]")
+ }
E::TupleRef { expr, elt } => {
unparse_expr(&*expr, indent, out)?;
write!(out, ".{}", elt)
M src/hir.rs +6 -2
@@ 240,7 240,7 @@ pub enum Expr {
elt: Sym,
},
ArrayRef {
- e: ExprNode,
+ expr: ExprNode,
idx: ExprNode,
},
Assign {
@@ 438,7 438,7 @@ impl Expr {
expr.write(0, f)?;
write!(f, " {})", elt)?;
}
- ArrayRef { e, idx } => {
+ ArrayRef { expr: e, idx } => {
write!(f, "(arrayref ")?;
e.write(0, f)?;
write!(f, " ")?;
@@ 670,6 670,10 @@ fn lower_expr(expr: &ast::Expr) -> ExprN
.collect();
Expr::StructCtor { body: lowered_body }
}
+ E::ArrayRef { expr, idx } => Expr::ArrayRef {
+ expr: lower_expr(expr),
+ idx: lower_expr(idx),
+ },
E::TupleRef { expr, elt } => Expr::TupleRef {
expr: lower_expr(expr),
elt: *elt,
M src/parser.rs +11 -0
@@ 1021,6 1021,15 @@ impl<'input> Parser<'input> {
typeparams: vec![],
}
}
+ T::LBracket => {
+ self.expect(T::LBracket);
+ let param = self.parse_expr(0)?;
+ self.expect(T::RBracket);
+ ast::Expr::ArrayRef {
+ expr: Box::new(lhs),
+ idx: Box::new(param),
+ }
+ }
T::Dollar => {
self.expect(T::Dollar);
ast::Expr::TypeUnwrap {
@@ 1369,6 1378,8 @@ fn postfix_binding_power(op: &TokenKind)
T::LBrace => Some((119, ())),
// ":" universal function call syntax
T::Colon => Some((115, ())),
+ // "[" array index
+ T::LBracket => Some((114, ())),
// "^" for pointer derefs. TODO: Check precedence?
// This gets a little weird since it's postfix, not used
// to thinking about it. So it's like...
M src/passes.rs +2 -2
@@ 143,8 143,8 @@ fn expr_map(expr: ExprNode, f: &mut dyn
expr: expr_map(expr, f),
elt,
},
- E::ArrayRef { e, idx } => E::ArrayRef {
- e: expr_map(e, f),
+ E::ArrayRef { expr, idx } => E::ArrayRef {
+ expr: expr_map(expr, f),
idx,
},
E::Assign { lhs, rhs } => E::Assign {
M src/typeck.rs +43 -8
@@ 38,7 38,7 @@ pub enum TypeInfo {
Struct(BTreeMap<Sym, TypeId>),
/// Definitely a sum type
Sum(BTreeMap<Sym, TypeId>),
- Array(TypeId, usize),
+ Array(TypeId, Option<usize>),
/// This is some generic type that has a name like @A
/// AKA a type parameter.
TypeParam(Sym),
@@ 100,7 100,14 @@ impl TypeInfo {
accm += "end\n";
accm.into()
}
- Array(id, len) => Cow::Owned(format!("{}[{}]", tck.vars[id].get_name(tck), len)),
+ Array(id, len) => {
+ // Output something intelligible if we don't know the
+ // length of the array.
+ let len_str = len
+ .map(|l| format!("{}", l))
+ .unwrap_or_else(|| format!("unknown"));
+ Cow::Owned(format!("{}[{}]", tck.vars[id].get_name(tck), len_str))
+ }
TypeParam(sym) => Cow::Owned(format!("@{}", &*sym.val())),
}
}
@@ 385,7 392,7 @@ impl Tck {
Type::Generic(s) => TypeInfo::TypeParam(*s),
Type::Array(ty, len) => {
let new_body = self.insert_known(&ty);
- TypeInfo::Array(new_body, *len)
+ TypeInfo::Array(new_body, Some(*len))
}
// TODO: Generics?
Type::Struct(body, _names) => {
@@ 598,8 605,8 @@ impl Tck {
}
Ok(())
}
+ // Same as struct types
(Sum(body1), Sum(body2)) => {
- // Same as struct types
for (nm, t1) in body1.iter() {
let t2 = body2[nm];
self.unify(symtbl, *t1, t2)?;
@@ 610,6 617,21 @@ impl Tck {
}
Ok(())
}
+ // Either both array lengths are the same or both are
+ // unknown. The latter might need to be an error, we will see.
+ (Array(body1, len1), Array(body2, len2)) if len1 == len2 => {
+ self.unify(symtbl, body1, body2)
+ }
+ (Array(body1, None), Array(body2, Some(_len2))) => {
+ self.vars.insert(a, TypeInfo::Ref(b));
+ self.unify(symtbl, body1, body2)
+ //todo!("propegate array lengths")
+ }
+ (Array(body1, Some(_len1)), Array(body2, None)) => {
+ self.vars.insert(b, TypeInfo::Ref(a));
+ self.unify(symtbl, body1, body2)
+ // todo!("propegate array lengths")
+ }
(Array(body1, len1), Array(body2, len2)) if len1 == len2 => {
self.unify(symtbl, body1, body2)
}
@@ 672,7 694,7 @@ impl Tck {
}
Array(ty, len) => {
let real_body = self.reconstruct(*ty)?;
- Ok(Type::Array(Box::new(real_body), *len))
+ Ok(Type::Array(Box::new(real_body), len.unwrap()))
}
Sum(body) => {
let real_body: Result<BTreeMap<_, _>, TypeError> = body
@@ 745,7 767,7 @@ impl Tck {
}
Type::Array(ty, len) => {
let inst_ty = helper(tck, named_types, &*ty);
- TypeInfo::Array(inst_ty, *len)
+ TypeInfo::Array(inst_ty, Some(*len))
}
Type::Sum(body, _names) => {
let inst_body = body
@@ 1517,11 1539,24 @@ fn typecheck_expr(
let expr_type = typecheck_expr(tck, symtbl, func_rettype, expr)?;
tck.unify(symtbl, body_type, expr_type)?;
}
- let arr_type = tck.insert(TypeInfo::Array(body_type, len));
+ let arr_type = tck.insert(TypeInfo::Array(body_type, Some(len)));
tck.set_expr_type(expr, arr_type);
Ok(arr_type)
}
- ArrayRef { e: _, idx: _ } => todo!(),
+ ArrayRef {
+ expr: arr_expr,
+ idx,
+ } => {
+ let expr_type = typecheck_expr(tck, symtbl, func_rettype, arr_expr)?;
+ let idx_type = typecheck_expr(tck, symtbl, func_rettype, idx)?;
+ let elt_type = tck.insert(TypeInfo::Unknown);
+ let expected_expr_type = tck.insert(TypeInfo::Array(elt_type, None));
+ let expected_idx_type = tck.insert(TypeInfo::Prim(PrimType::UnknownInt));
+ tck.unify(symtbl, expr_type, expected_expr_type)?;
+ tck.unify(symtbl, idx_type, expected_idx_type)?;
+ tck.set_expr_type(expr, elt_type);
+ Ok(elt_type)
+ }
Typecast { e: _, to: _ } => todo!(),
};
if let Err(e) = rettype {
A => tests/programs/test_array4.gt +32 -0
@@ 0,0 1,32 @@
+-- Compile:
+-- status: success
+-- Run:
+-- status: success
+-- stdout:
+-- 1
+-- 5
+-- 15
+
+fn actually_use_array(arr I32[5]) I32 =
+ let mut i I32 = 0
+ let mut summ = 0
+ loop
+ if i == 5 then break end
+ summ = summ + arr[i]
+ i = i + 1
+
+ end
+ summ
+end
+
+fn main() {} =
+ let x I32[3] = [1, 2, 3]
+ let y I32 = x[0]
+ __println(y)
+ let a I32[3][3] = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
+ let b = a[1][1]
+ __println(b)
+
+ __println(actually_use_array([1, 2, 3, 4, 5]))
+
+end
A => tests/programs/test_eq_module.gt +32 -0
@@ 0,0 1,32 @@
+-- Compile:
+-- status: error
+
+type Eq(@T) = struct
+ eq: fn(@T, @T) Bool,
+end
+
+const IntEq Eq(I32) = Eq {
+ .eq = fn(lhs I32, rhs I32) Bool = lhs == rhs end,
+}
+
+const BoolEq Eq(Bool) = Eq {
+ .eq = fn(lhs Bool, rhs Bool) Bool = lhs == rhs end,
+}
+
+fn all(eq_impl Eq(@T), val @T, array @T[3] | @T) Bool =
+ let mut i I32 = 0
+ loop
+ if i == 3 then break end
+ let val2 = array[i]
+ if not eq_impl$.eq(val, val2) then return false end
+ i = i + 1
+ end
+ true
+end
+
+fn main() {} =
+ let t1 = all(IntEq, 3, [1, 2, 3]) -- returns false
+ let t2 = all(BoolEq, true, [true, true, true]) -- returns true
+ __println_bool(t1)
+ __println_bool(t2)
+end
M tests/programs/test_result1.gt +4 -0
@@ 1,3 1,6 @@
+-- Compile:
+-- status: error
+
type Result(@T, @E) = sum
Ok @T,
Err @E,
@@ 8,4 11,5 @@ fn main() {} =
let y = Result.Err true
let res = x == y
__println_bool(res)
+ {}
end