diff --git a/src/core.rs b/src/core.rs index d5d7181..085ddaf 100644 --- a/src/core.rs +++ b/src/core.rs @@ -468,14 +468,16 @@ pub struct Struct { pub id: usize, pub size: usize, pub name: Rc, + pub fields: Vec>, } impl Struct { - pub fn new(size: usize, name: Rc) -> Self { + pub fn new(name: Rc, fields: Vec>) -> Self { Struct { id: STRUCT_COUNTER.fetch_add(1, std::sync::atomic::Ordering::SeqCst), - size, + size: fields.len(), name, + fields, } } } @@ -638,9 +640,7 @@ pub fn call_type(ty: &ObjType, arg: Vec) -> NRes { Precedence::zero(), )), ObjType::Struct(s) => { - if arg.len() == 0 { - Ok(Obj::Instance(s.clone(), vec![Obj::Null; s.size])) - } else if arg.len() == s.size { + if arg.len() == s.size { Ok(Obj::Instance(s.clone(), arg)) } else { Err(NErr::argument_error(format!( @@ -1803,6 +1803,7 @@ pub enum Expr { // shouldn't stay in the tree: CommaSeq(Vec>), Splat(Box), + Member(Box, String), } #[derive(Debug)] @@ -2534,6 +2535,10 @@ pub fn freeze(env: &mut FreezeEnv, expr: &LocExpr) -> NRes { Expr::Return(e) => Ok(Expr::Return(opt_box_freeze(env, e)?)), Expr::Throw(e) => Ok(Expr::Throw(box_freeze(env, e)?)), Expr::Continue => Ok(Expr::Continue), + Expr::Member(x, field) => Ok(Expr::Member( + box_freeze_underscore_ok(env, x)?, + field.clone(), + )), }?, }) } @@ -3417,6 +3422,28 @@ impl Parser { }; } } + Some(Token::Dot) => { + self.advance(); + match self.peek_loc_token() { + Some(LocToken { + token: Token::Ident(s), + end, + .. + }) => { + let end = *end; + let field = s.clone(); + self.advance(); + cur = LocExpr { + expr: Expr::Member(Box::new(cur), field), + start, + end, + }; + } + _ => { + return Err(self.error_here("expected ident".into())); + } + } + } _ => break Ok(cur), } } diff --git a/src/eval.rs b/src/eval.rs index 0de2f4a..d1d1257 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -1155,21 +1155,13 @@ pub fn evaluate(env: &Rc>, expr: &LocExpr) -> NRes { }) } Expr::Struct(name, field_names) => { - let s = Struct::new(field_names.len(), name.clone()); + let s = Struct::new(name.clone(), field_names.clone()); assign( &env, &EvaluatedLvalue::IndexedIdent((&**name).clone(), Vec::new()), Some(&ObjType::Func), Obj::Func(Func::Type(ObjType::Struct(s.clone())), Precedence::zero()), )?; - for (i, field) in field_names.iter().enumerate() { - assign( - &env, - &EvaluatedLvalue::IndexedIdent((&**field).clone(), Vec::new()), - Some(&ObjType::Func), - Obj::Func(Func::StructField(s.clone(), i), Precedence::zero()), - )?; - } Ok(Obj::Null) } Expr::Freeze(expr) => { @@ -1223,6 +1215,29 @@ pub fn evaluate(env: &Rc>, expr: &LocExpr) -> NRes { Expr::Continue => Err(NErr::Continue), Expr::Return(Some(e)) => Err(NErr::Return(evaluate(env, e)?)), Expr::Return(None) => Err(NErr::Return(Obj::Null)), + Expr::Member(x, field) => { + let res = match evaluate(env, x)? { + Obj::Instance(struc, fields) => { + if let Some(ind) = struc + .fields + .iter() + .enumerate() + .find_map(|(i, f)| (field == &**f).then_some(i)) + { + Ok(fields[ind].clone()) + } else { + let fields: Vec<_> = struc.fields.iter().map(|r| r.as_str()).collect(); + let fields = fields.join(", "); + Err(NErr::type_error(format!( + "instance of struct {}({}) doesn't contain field {}", + struc.name, fields, field + ))) + } + } + _ => Err(NErr::type_error(format!("not a struct instance"))), + }; + add_trace(res, format!("index/slice"), expr.start, expr.end) + } } } diff --git a/src/lex.rs b/src/lex.rs index 243c77a..feeb837 100644 --- a/src/lex.rs +++ b/src/lex.rs @@ -61,6 +61,7 @@ pub enum Token { Import, Literally, Underscore, + Dot, // strip me before parsing Comment(String), @@ -548,6 +549,7 @@ impl<'a> Lexer<'a> { self.emit(Token::Ident(acc)) } ("", '=') => self.emit(Token::Assign), + ("", '.') => self.emit(Token::Dot), (_, '=') => { self.emit_but_last(Token::Ident(acc), Token::Assign); }