From 350f775b6c7e1a88362b04eaa347ad7e5f2e7cbb Mon Sep 17 00:00:00 2001 From: TriMill Date: Thu, 22 Sep 2022 08:58:59 -0400 Subject: [PATCH] structs --- complexpr/src/eval.rs | 124 +++++++++++++++++++++++++----------- complexpr/src/expr.rs | 16 +++-- complexpr/src/lexer.rs | 2 +- complexpr/src/parser.rs | 33 ++++++++-- complexpr/src/stdlib/mod.rs | 2 +- complexpr/src/token.rs | 2 +- complexpr/src/value/mod.rs | 37 +++++++---- 7 files changed, 154 insertions(+), 62 deletions(-) diff --git a/complexpr/src/eval.rs b/complexpr/src/eval.rs index 75d9ba4..9bb0436 100644 --- a/complexpr/src/eval.rs +++ b/complexpr/src/eval.rs @@ -2,7 +2,7 @@ use std::{collections::HashMap, rc::Rc, cell::RefCell}; use num_traits::Pow; -use crate::{value::{Value, Complex, func::{Func, CIterator}}, expr::{Stmt, Expr}, token::{TokenType, Token, OpType}, RuntimeError, Position, env::{EnvRef, Environment}}; +use crate::{value::{Value, Complex, func::{Func, CIterator}, TypeData, CxprStruct}, expr::{Stmt, Expr}, token::{TokenType, Token, OpType}, RuntimeError, Position, env::{EnvRef, Environment}}; thread_local!(static PIPE_NAME: Option> = Some(Rc::from(""))); thread_local!(static FORLOOP_NAME: Option> = Some(Rc::from(""))); @@ -127,7 +127,7 @@ pub fn eval_stmt(stmt: &Stmt, env: EnvRef) -> Result<(), Unwind> { let value = eval_expr(expr, env)?; return Err(Unwind::Return { pos: pos.clone(), value }) }, - Stmt::Struct { .. } => todo!(), + Stmt::StructDef { name, ty } => env.borrow_mut().declare(name.ty.clone().as_ident().unwrap(), Value::Type(ty.clone())) } Ok(()) } @@ -169,6 +169,19 @@ pub fn eval_expr(expr: &Expr, env: EnvRef) -> Result { step.as_ref().map(|x| x.as_ref()), *incl, env), Expr::BoxedInfix { func } => Ok(Value::Func(func.clone())), + Expr::FieldAccess { target, name, .. } => { + let target = eval_expr(target, env)?; + match target { + Value::Struct(s) => { + if let Some(v) = s.data.clone().borrow().get(name.as_ref()) { + Ok(v.clone()) + } else { + Err(format!("Struct {} has no field '{}'", Value::Struct(s).repr(), name).into()) + } + }, + _ => Err(format!("{} is not a struct", target.repr()).into()) + } + }, Expr::List { items } => { let mut list = Vec::with_capacity(items.len()); for item in items { @@ -210,6 +223,29 @@ pub fn eval_expr(expr: &Expr, env: EnvRef) -> Result { }; Ok(Value::Func(func)) }, + Expr::StructInit { ty, args, .. } => { + let ty_val = eval_expr(&ty, env.clone())?; + let ty = match ty_val { + Value::Type(ty) => ty, + _ => return Err(format!("'{}' is not a type", ty_val.repr()).into()) + }; + match ty.typedata { + TypeData::None => Err(format!("'{}' is not a struct type", Value::Type(ty).repr()).into()), + TypeData::StructFields(ref fields) => if fields.len() == args.len() { + let mut data = HashMap::new(); + for (k, v) in fields.iter().zip(args.iter()) { + data.insert(k.to_owned(), eval_expr(v, env.clone())?); + } + let result = CxprStruct { + ty: ty.clone(), + data: Rc::new(RefCell::new(data)) + }; + Ok(Value::Struct(result)) + } else { + Err(format!("Wrong number of fields for type '{}', expected {}, got {}", ty.name, fields.len(), args.len()).into()) + } + } + } } } @@ -251,44 +287,60 @@ fn compound_assignment_inner(l: Value, r: Value, op: &Token) -> Result Result { - // lhs must be an identifier (checked in parser) - if let Expr::Ident{value: Token{ty: TokenType::Ident(name),..}} = lhs { - if op.ty == TokenType::Equal { - // plain assignment - let r = eval_expr(rhs, env.clone())?; - env.borrow_mut() - .set(name.clone(), r.clone()) - .map_err(|_| RuntimeError::new("Variable not declared before assignment", op.pos.clone()))?; - Ok(r) - } else { - // compound assignment - let prev_value = env.borrow_mut() - .get(name) - .ok_or_else(|| RuntimeError::new("Variable not defined in scope", op.pos.clone()))?; - let r = eval_expr(rhs, env.clone())?; + match lhs { + Expr::Ident{value, ..} => { + let name = value.ty.clone().as_ident().unwrap(); + if op.ty == TokenType::Equal { + // plain assignment + let r = eval_expr(rhs, env.clone())?; + env.borrow_mut() + .set(name.clone(), r.clone()) + .map_err(|_| RuntimeError::new("Variable not declared before assignment", op.pos.clone()))?; + Ok(r) + } else { + // compound assignment + let prev_value = env.borrow_mut() + .get(&name) + .ok_or_else(|| RuntimeError::new("Variable not defined in scope", op.pos.clone()))?; + let r = eval_expr(rhs, env.clone())?; - let result = compound_assignment_inner(prev_value, r, op)?; + let result = compound_assignment_inner(prev_value, r, op)?; - env.borrow_mut() - .set(name.clone(), result.clone()).expect("unreachable"); - Ok(result) - } - } else if let Expr::Index { lhs, index, pos } = lhs { - let l = eval_expr(lhs, env.clone())?; - let idx = eval_expr(index, env.clone())?; - if op.ty == TokenType::Equal { + env.borrow_mut() + .set(name, result.clone()).expect("unreachable"); + Ok(result) + } + }, + Expr::Index { lhs, index, pos } => { + let l = eval_expr(lhs, env.clone())?; + let idx = eval_expr(index, env.clone())?; + if op.ty == TokenType::Equal { + let r = eval_expr(rhs, env)?; + l.assign_index(&idx, r.clone()).map_err(|e| RuntimeError::new(e, pos.clone()))?; + Ok(r) + } else { + let prev_value = l.index(&idx).map_err(|e| RuntimeError::new(e, pos.clone()))?; + let r = eval_expr(rhs, env)?; + let result = compound_assignment_inner(prev_value, r, op)?; + l.assign_index(&idx, result.clone()).map_err(|e| RuntimeError::new(e, pos.clone()))?; + Ok(result) + } + }, + Expr::FieldAccess { target, name, pos } => { + let target = eval_expr(target, env.clone())?; let r = eval_expr(rhs, env)?; - l.assign_index(&idx, r.clone()).map_err(|e| RuntimeError::new(e, pos.clone()))?; - Ok(r) - } else { - let prev_value = l.index(&idx).map_err(|e| RuntimeError::new(e, pos.clone()))?; - let r = eval_expr(rhs, env)?; - let result = compound_assignment_inner(prev_value, r, op)?; - l.assign_index(&idx, result.clone()).map_err(|e| RuntimeError::new(e, pos.clone()))?; - Ok(result) + if let Value::Struct(s) = target { + if s.data.borrow().contains_key(name.as_ref()) { + s.data.borrow_mut().insert(name.to_string(), r.clone()); + Ok(r) + } else { + Err(RuntimeError::new(format!("Struct {} does not have field {}", Value::Struct(s).repr(), name), pos.clone())) + } + } else { + Err(RuntimeError::new(format!("'{}' is not a struct", target.repr()), pos.clone())) + } } - } else { - unreachable!() + _ => unreachable!() } } diff --git a/complexpr/src/expr.rs b/complexpr/src/expr.rs index 3146a85..a125a1b 100644 --- a/complexpr/src/expr.rs +++ b/complexpr/src/expr.rs @@ -1,4 +1,4 @@ -use std::fmt; +use std::{fmt, rc::Rc}; use crate::{token::{Token, OpType}, Position, value::{Type, func::Func}}; @@ -14,7 +14,7 @@ pub enum Stmt { Continue { pos: Position }, Return { pos: Position, expr: Expr }, Fn { name: Token, args: Vec, body: Box }, - Struct { name: Token, ty: Type, items: Vec } + StructDef { name: Token, ty: Type }, } impl fmt::Debug for Stmt { @@ -30,7 +30,7 @@ impl fmt::Debug for Stmt { Self::Continue { .. } => write!(f, "(continue)"), Self::Return { expr, .. } => write!(f, "(return {:?})", expr), Self::Fn { name, args, body } => write!(f, "(fn {:?} {:?} {:?})", name, args, body), - Self::Struct { name, ty, items } => write!(f, "(struct {:?} #{:?} {:?})", name, ty.id, items), + Self::StructDef { name, ty } => write!(f, "(struct {:?} #{:?})", name, ty.id), } } } @@ -41,14 +41,16 @@ pub enum Expr { Ternary { arg1: Box, arg2: Box, arg3: Box, op: Token }, Unary { arg: Box, op: Token }, Range { start: Box, end: Option>, step: Option>, incl: bool }, + FieldAccess { target: Box, name: Rc, pos: Position }, BoxedInfix { func: Func }, Ident { value: Token }, Literal { value: Token }, List { items: Vec }, Map { items: Vec<(Expr,Expr)> }, + Fn { args: Vec, body: Box }, FuncCall { func: Box, args: Vec, pos: Position }, Index { lhs: Box, index: Box, pos: Position }, - Fn { args: Vec, body: Box }, + StructInit { ty: Box, args: Vec, pos: Position }, } impl fmt::Debug for Expr { @@ -58,6 +60,7 @@ impl fmt::Debug for Expr { Self::Ternary { arg1, arg2, arg3, op} => write!(f, "({:?} {:?} {:?} {:?})", op, arg1, arg2, arg3), Self::Unary { arg, op } => write!(f, "({:?} {:?})", op, arg), Self::Range { start, end, step, incl } => write!(f, "(range {:?}..{:?} step {:?} incl {:?})", start, end, step, incl), + Self::FieldAccess { target, name, .. } => write!(f, "(fieldaccess {:?} {:?})", target, name), Self::BoxedInfix { func } => write!(f, "(boxed-infix {:?})", func), Self::Ident { value } => write!(f, "(ident {:?})", value), Self::Literal { value } => write!(f, "(lit {:?})", value), @@ -65,14 +68,15 @@ impl fmt::Debug for Expr { Self::Map { items } => write!(f, "(map {:?})", items), Self::FuncCall { func, args, .. } => write!(f, "(call {:?} {:?})", func, args), Self::Index { lhs, index, .. } => write!(f, "(index {:?} {:?})", lhs, index), - Self::Fn { args, body } => write!(f, "(fn {:?} {:?})", args, body) + Self::Fn { args, body } => write!(f, "(fn {:?} {:?})", args, body), + Self::StructInit { ty, args, .. } => write!(f, "(mk-struct {:?} {:?})", ty, args), } } } impl Expr { pub fn is_lvalue(&self) -> bool { - matches!(self, Expr::Ident{..} | Expr::Index{..}) + matches!(self, Expr::Ident{..} | Expr::Index{..} | Expr::FieldAccess{..}) } pub fn is_assignment(&self) -> bool { diff --git a/complexpr/src/lexer.rs b/complexpr/src/lexer.rs index d615647..7d654c9 100644 --- a/complexpr/src/lexer.rs +++ b/complexpr/src/lexer.rs @@ -150,7 +150,7 @@ impl Lexer { match self.next() { '.' => match self.expect(&['.']) { Some('.') => self.add_token(TokenType::DoubleDot, ".."), - _ => return Err(self.mk_error("Expected '.' after previous '.'")) + _ => self.add_token(TokenType::Dot, ".") }, '+' => match self.expect(&['=']) { Some('=') => self.add_token(TokenType::PlusEqual, "+="), diff --git a/complexpr/src/parser.rs b/complexpr/src/parser.rs index 3ac226a..1671d44 100644 --- a/complexpr/src/parser.rs +++ b/complexpr/src/parser.rs @@ -273,8 +273,8 @@ impl Parser { } self.err_on_eof()?; let items = self.commalist(TokenType::RBrace, Self::ident)?; - let ty = value::generate_type(name); - Ok(Stmt::Struct { name: tok_name, ty, items }) + let ty = value::generate_struct_type(name, items.iter().map(|x| x.ty.clone().as_ident().unwrap().to_string()).collect()); + Ok(Stmt::StructDef { name: tok_name, ty }) } /////////////////// @@ -407,19 +407,33 @@ impl Parser { self.err_on_eof()?; if matches!(self.peek().ty, TokenType::Bang | TokenType::Minus) { let op = self.next(); - Ok(Expr::Unary { arg: Box::new(self.fncall()?), op }) + Ok(Expr::Unary { arg: Box::new(self.fieldaccess()?), op }) } else { - self.fncall() + self.fieldaccess() } } - // function calls and array access - fn fncall(&mut self) -> Result { + // dot notation for field access + fn fieldaccess(&mut self) -> Result { + let target = self.suffix()?; + if !self.at_end() && self.peek().ty == TokenType::Dot { + let pos = self.next().pos; + self.err_on_eof()?; + let name = self.ident()?.ty.as_ident().unwrap(); + Ok(Expr::FieldAccess { target: Box::new(target), name, pos }) + } else { + Ok(target) + } + } + + // function calls, array access, struct initializaiton + fn suffix(&mut self) -> Result { let mut expr = self.expr_base()?; while !self.at_end() { match self.peek().ty { TokenType::LParen => expr = self.fncall_inner(expr)?, TokenType::LBrack => expr = self.arrindex_inner(expr)?, + TokenType::LBrace => expr = self.structinit_inner(expr)?, _ => return Ok(expr) } } @@ -444,6 +458,13 @@ impl Parser { Ok(Expr::Index { lhs: Box::new(expr), index: Box::new(index), pos: lbrack.pos }) } + // struct initialization: A { b } + fn structinit_inner(&mut self, expr: Expr) -> Result { + let lbrace = self.next(); + let args = self.commalist(TokenType::RBrace, Self::assignment)?; + Ok(Expr::StructInit { ty: Box::new(expr), args, pos: lbrace.pos }) + } + // key-value pairs for maps fn kv_pair(&mut self) -> Result<(Expr, Expr), ParserError> { let key = self.assignment()?; diff --git a/complexpr/src/stdlib/mod.rs b/complexpr/src/stdlib/mod.rs index a779b9f..ef5d702 100644 --- a/complexpr/src/stdlib/mod.rs +++ b/complexpr/src/stdlib/mod.rs @@ -55,7 +55,7 @@ fn fn_copy(args: Vec) -> Result { Value::List(l) => Value::from(l.borrow().clone()), Value::Map(m) => Value::from(m.borrow().clone()), // Value::Func(f) => Value::Func(f.make_copy()) // TODO copy functions - Value::Data(_) => todo!(), + Value::Struct(_) => todo!(), a => a.clone(), }) } diff --git a/complexpr/src/token.rs b/complexpr/src/token.rs index 6907ed6..2566c1c 100644 --- a/complexpr/src/token.rs +++ b/complexpr/src/token.rs @@ -25,7 +25,7 @@ pub enum TokenType { Bang, DoubleAmper, DoublePipe, Tilde, Amper, Pipe, - DoubleDot, + Dot, DoubleDot, Equal, PlusEqual, MinusEqual, StarEqual, SlashEqual, PercentEqual, DoubleSlashEqual, CaretEqual, DoubleEqual, BangEqual, Greater, GreaterEqual, Less, LessEqual, Spaceship, diff --git a/complexpr/src/value/mod.rs b/complexpr/src/value/mod.rs index 827b328..778a4e4 100644 --- a/complexpr/src/value/mod.rs +++ b/complexpr/src/value/mod.rs @@ -11,10 +11,18 @@ pub type Rational = num_rational::Ratio; pub type Complex = num_complex::Complex64; static TYPE_COUNTER: AtomicUsize = AtomicUsize::new(Value::COUNT); -#[derive(Clone, Debug, Eq)] + +#[derive(Clone, Debug)] +pub enum TypeData { + None, + StructFields(Rc>), +} + +#[derive(Clone, Debug)] pub struct Type { pub name: Rc, - pub id: usize + pub id: usize, + pub typedata: TypeData } impl PartialEq for Type { @@ -23,11 +31,13 @@ impl PartialEq for Type { } } +impl Eq for Type {} -pub fn generate_type(name: Rc) -> Type { +pub fn generate_struct_type(name: Rc, fields: Vec) -> Type { Type { name, - id: TYPE_COUNTER.fetch_add(1, atomic::Ordering::Relaxed) + id: TYPE_COUNTER.fetch_add(1, atomic::Ordering::Relaxed), + typedata: TypeData::StructFields(Rc::new(fields)) } } @@ -37,6 +47,7 @@ pub fn generate_builtin_types() -> Vec { types.push(Type { name: Rc::from(x.as_ref()), id: x as usize, + typedata: TypeData::None, }) } types @@ -112,7 +123,7 @@ fn repr_char(c: char) -> String { #[derive(Clone, Debug)] pub struct CxprStruct { pub ty: Type, - pub data: Vec, + pub data: Rc>>, } #[derive(Clone, Debug, EnumCount, EnumDiscriminants)] @@ -128,7 +139,7 @@ pub enum Value { List(Rc>>), Map(Rc>>), Func(Func), - Data(CxprStruct), + Struct(CxprStruct), } impl Value { @@ -192,7 +203,10 @@ impl Value { Some(name) => format!("", name), None => "".into(), }, - Self::Data(_) => todo!(), + Self::Struct(CxprStruct { ty, data }) + => format!("{} {{ {} }}", ty.name, + data.borrow().iter().map(|(k, v)| format!("{}: {}", k, v.repr())) + .collect::>().join(", ")) } } @@ -276,12 +290,13 @@ impl Value { pub fn get_type(&self) -> Type { let discr = ValueDiscriminants::from(self); - if let Self::Data(_) = self { + if let Self::Struct(_) = self { todo!() } else { Type { name: Rc::from(discr.as_ref()), - id: discr as usize + id: discr as usize, + typedata: TypeData::None, } } } @@ -336,7 +351,7 @@ impl PartialEq for Value { ) => (*f1 as *const ()) == (*f2 as *const ()) && c1 == c2, _ => false } - (Self::Data(_), Self::Data(_)) => todo!("Can't compare data yet"), + (Self::Struct(_), Self::Struct(_)) => todo!("Can't compare data yet"), _ => false } } @@ -399,7 +414,7 @@ impl Hash for Value { Self::List(l) => l.borrow().hash(state), Self::Map(_) => todo!(), Self::Func(f) => f.hash(state), - Self::Data(_) => todo!(), + Self::Struct(_) => todo!(), } }