summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias Schiffer <mschiffer@universe-factory.net>2024-05-01 09:50:26 +0200
committerMatthias Schiffer <mschiffer@universe-factory.net>2024-05-01 15:42:07 +0200
commit8d15e1ab1c3c3bca3c794c5f1499e0108d60d42f (patch)
treef15b750f95a04dcc78c578f2038cc8201d5367d5
parent8308121242d3982152d1136dd485599d988fdb5d (diff)
downloadrebel-8d15e1ab1c3c3bca3c794c5f1499e0108d60d42f.tar
rebel-8d15e1ab1c3c3bca3c794c5f1499e0108d60d42f.zip
rebel-lang: scope: allow assigning fields and array elements
-rw-r--r--crates/rebel-lang/src/scope.rs166
1 files changed, 127 insertions, 39 deletions
diff --git a/crates/rebel-lang/src/scope.rs b/crates/rebel-lang/src/scope.rs
index bb3f2e9..1c6d52f 100644
--- a/crates/rebel-lang/src/scope.rs
+++ b/crates/rebel-lang/src/scope.rs
@@ -20,6 +20,32 @@ pub struct LookupError;
pub type Result<T> = std::result::Result<T, LookupError>;
+#[derive(Debug)]
+enum ValueSlot<'a> {
+ Value(&'a mut Value),
+ Option(&'a mut Option<Value>),
+}
+
+impl<'a> ValueSlot<'a> {
+ fn assign(&mut self, value: Value) {
+ match self {
+ ValueSlot::Value(slot) => {
+ **slot = value;
+ }
+ ValueSlot::Option(slot) => {
+ **slot = Some(value);
+ }
+ }
+ }
+
+ fn into_inner(self) -> Option<&'a mut Value> {
+ match self {
+ ValueSlot::Value(slot) => Some(slot),
+ ValueSlot::Option(slot) => slot.as_mut(),
+ }
+ }
+}
+
#[derive(Debug, Clone)]
pub struct Context {
pub vars: Module<Var>,
@@ -54,17 +80,6 @@ impl Context {
}
}
- fn local_ident<'a>(path: &'a ast::Path) -> Option<ast::Ident<'a>> {
- if path.root != ast::PathRoot::Relative {
- return None;
- }
-
- let [ident] = path.components[..] else {
- return None;
- };
- Some(ident)
- }
-
pub fn lookup_var(&self, path: &ast::Path) -> Result<&Var> {
if path.root != ast::PathRoot::Relative {
return Err(LookupError);
@@ -89,6 +104,78 @@ impl Context {
self.types.lookup(&path.components).ok_or(LookupError)
}
+ fn lookup_path_var_mut(&mut self, path: &ast::Path) -> Result<&mut Var> {
+ if path.root != ast::PathRoot::Relative {
+ return Err(LookupError);
+ }
+
+ self.vars.lookup_mut(&path.components).ok_or(LookupError)
+ }
+
+ fn lookup_destr_pat_var_type_mut(&mut self, pat: &pat::DestrPat) -> Result<&mut Type> {
+ Ok(match pat {
+ pat::DestrPat::Index { pat, index: _ } => {
+ match self.lookup_destr_pat_var_type_mut(pat)? {
+ Type::Array(inner) => inner.as_mut(),
+ _ => return Err(LookupError),
+ }
+ }
+ pat::DestrPat::Field { pat, field } => {
+ match self.lookup_destr_pat_var_type_mut(pat)? {
+ Type::Struct(entries) => entries.get_mut(field.name).ok_or(LookupError)?,
+ _ => return Err(LookupError),
+ }
+ }
+ pat::DestrPat::Paren(subpat) => self.lookup_destr_pat_var_type_mut(subpat)?,
+ pat::DestrPat::Path(path) => &mut self.lookup_path_var_mut(path)?.inferred_type,
+ })
+ }
+
+ fn lookup_destr_pat_var_type_value_mut(
+ &mut self,
+ pat: &pat::DestrPat,
+ ) -> Result<(&mut Type, ValueSlot)> {
+ Ok(match pat {
+ pat::DestrPat::Index { pat, index } => {
+ let index =
+ usize::try_from(match Value::eval(self, index).or(Err(LookupError))? {
+ Value::Int(index) => index,
+ _ => {
+ return Err(LookupError);
+ }
+ })
+ .or(Err(LookupError))?;
+ let (typ, value) = self.lookup_destr_pat_var_type_value_mut(pat)?;
+ let (inner_type, inner_value) = match (typ, value.into_inner().ok_or(LookupError)?)
+ {
+ (Type::Array(inner_type), Value::Array(inner_value)) => (
+ inner_type.as_mut(),
+ inner_value.get_mut(index).ok_or(LookupError)?,
+ ),
+ _ => return Err(LookupError),
+ };
+ (inner_type, ValueSlot::Value(inner_value))
+ }
+ pat::DestrPat::Field { pat, field } => {
+ let (typ, value) = self.lookup_destr_pat_var_type_value_mut(pat)?;
+ let (inner_type, inner_value) = match (typ, value.into_inner().ok_or(LookupError)?)
+ {
+ (Type::Struct(type_entries), Value::Struct(value_entries)) => (
+ type_entries.get_mut(field.name).ok_or(LookupError)?,
+ value_entries.get_mut(field.name).ok_or(LookupError)?,
+ ),
+ _ => return Err(LookupError),
+ };
+ (inner_type, ValueSlot::Value(inner_value))
+ }
+ pat::DestrPat::Paren(subpat) => self.lookup_destr_pat_var_type_value_mut(subpat)?,
+ pat::DestrPat::Path(path) => {
+ let var = self.lookup_path_var_mut(path)?;
+ (&mut var.inferred_type, ValueSlot::Option(&mut var.value))
+ }
+ })
+ }
+
pub fn record_type(&mut self, stmt: &ast::BlockStmt) -> typing::Result<Type> {
match stmt {
ast::BlockStmt::Let { dest, expr } => {
@@ -122,25 +209,16 @@ impl Context {
}
}
ast::BlockStmt::Assign { dest, expr } => {
- let pat::DestrPat::Path(dest_path) = dest.as_ref() else {
- // TODO: Other pattern types
- return Err(TypeError);
- };
- let dest_ident = Self::local_ident(dest_path).ok_or(TypeError)?;
-
let expr_type = Type::ast_expr_type(self, expr)?;
- // TODO: Lexical scoping
- let Some(ModuleEntry::Def(var)) = self.vars.0.get_mut(dest_ident.name) else {
- return Err(TypeError);
- };
- let inferred_type = var.inferred_type.clone().unify(expr_type, Coerce::Common)?;
+ let pat_type = self
+ .lookup_destr_pat_var_type_mut(dest)
+ .or(Err(TypeError))?;
- var.explicit_type
- .clone()
- .unify(inferred_type.clone(), Coerce::Assign)?;
+ let inferred_type = pat_type.clone().unify(expr_type, Coerce::Common)?;
+ *pat_type = inferred_type.clone();
- var.inferred_type = inferred_type.clone();
+ // TODO: Check explicit type
return Ok(inferred_type);
}
@@ -192,26 +270,21 @@ impl Context {
}
}
ast::BlockStmt::Assign { dest, expr } => {
- let pat::DestrPat::Path(dest_path) = dest.as_ref() else {
- // TODO: Other pattern types
- panic!("Type error during eval");
- };
- let dest_ident = Self::local_ident(dest_path).expect("Type error during eval");
-
let value = Value::eval(self, expr)?;
- let expr_type = value.typ().or(Err(EvalError))?;
+ let expr_type = value.typ().expect("Type error during eval");
- let Some(ModuleEntry::Def(var)) = self.vars.0.get_mut(dest_ident.name) else {
- unreachable!("Type error during eval");
- };
+ let (pat_type, mut pat_value) = self
+ .lookup_destr_pat_var_type_value_mut(dest)
+ .or(Err(EvalError))?;
- var.inferred_type = var
- .inferred_type
+ *pat_type = pat_type
.clone()
.unify(expr_type, Coerce::Common)
.expect("Type error during eval");
+ pat_value.assign(value.clone());
+
// TODO: Debug check: Test against explicit type
- var.value = Some(value.clone());
+
return Ok(value);
}
ast::BlockStmt::Expr { expr } => {
@@ -245,6 +318,21 @@ impl<T> Module<T> {
}
}
}
+
+ pub fn lookup_mut(&mut self, path: &[ast::Ident<'_>]) -> Option<&mut T> {
+ let (ident, rest) = path.split_first()?;
+
+ match self.0.get_mut(ident.name)? {
+ ModuleEntry::Module(module) => module.lookup_mut(rest),
+ ModuleEntry::Def(def) => {
+ if rest.is_empty() {
+ Some(def)
+ } else {
+ None
+ }
+ }
+ }
+ }
}
impl<T> Default for Module<T> {