structs
This commit is contained in:
parent
53bef7b5ed
commit
350f775b6c
7 changed files with 154 additions and 62 deletions
|
@ -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<Rc<str>> = Some(Rc::from("<pipeline>")));
|
||||
thread_local!(static FORLOOP_NAME: Option<Rc<str>> = Some(Rc::from("<for loop>")));
|
||||
|
@ -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<Value, RuntimeError> {
|
|||
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<Value, RuntimeError> {
|
|||
};
|
||||
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<Value, Ru
|
|||
}
|
||||
|
||||
pub fn eval_assignment(lhs: &Expr, rhs: &Expr, op: &Token, env: EnvRef) -> Result<Value, RuntimeError> {
|
||||
// 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!()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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<Token>, body: Box<Stmt> },
|
||||
Struct { name: Token, ty: Type, items: Vec<Token> }
|
||||
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<Expr>, arg2: Box<Expr>, arg3: Box<Expr>, op: Token },
|
||||
Unary { arg: Box<Expr>, op: Token },
|
||||
Range { start: Box<Expr>, end: Option<Box<Expr>>, step: Option<Box<Expr>>, incl: bool },
|
||||
FieldAccess { target: Box<Expr>, name: Rc<str>, pos: Position },
|
||||
BoxedInfix { func: Func },
|
||||
Ident { value: Token },
|
||||
Literal { value: Token },
|
||||
List { items: Vec<Expr> },
|
||||
Map { items: Vec<(Expr,Expr)> },
|
||||
Fn { args: Vec<Token>, body: Box<Stmt> },
|
||||
FuncCall { func: Box<Expr>, args: Vec<Expr>, pos: Position },
|
||||
Index { lhs: Box<Expr>, index: Box<Expr>, pos: Position },
|
||||
Fn { args: Vec<Token>, body: Box<Stmt> },
|
||||
StructInit { ty: Box<Expr>, args: Vec<Expr>, 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 {
|
||||
|
|
|
@ -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, "+="),
|
||||
|
|
|
@ -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<Expr, ParserError> {
|
||||
// dot notation for field access
|
||||
fn fieldaccess(&mut self) -> Result<Expr, ParserError> {
|
||||
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<Expr, ParserError> {
|
||||
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<Expr, ParserError> {
|
||||
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()?;
|
||||
|
|
|
@ -55,7 +55,7 @@ fn fn_copy(args: Vec<Value>) -> Result<Value, RuntimeError> {
|
|||
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(),
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -11,10 +11,18 @@ pub type Rational = num_rational::Ratio<i64>;
|
|||
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<Vec<String>>),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Type {
|
||||
pub name: Rc<str>,
|
||||
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<str>) -> Type {
|
||||
pub fn generate_struct_type(name: Rc<str>, fields: Vec<String>) -> 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<Type> {
|
|||
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<Value>,
|
||||
pub data: Rc<RefCell<HashMap<String, Value>>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, EnumCount, EnumDiscriminants)]
|
||||
|
@ -128,7 +139,7 @@ pub enum Value {
|
|||
List(Rc<RefCell<Vec<Value>>>),
|
||||
Map(Rc<RefCell<HashMap<Value,Value>>>),
|
||||
Func(Func),
|
||||
Data(CxprStruct),
|
||||
Struct(CxprStruct),
|
||||
}
|
||||
|
||||
impl Value {
|
||||
|
@ -192,7 +203,10 @@ impl Value {
|
|||
Some(name) => format!("<fn {}>", name),
|
||||
None => "<anonymous fn>".into(),
|
||||
},
|
||||
Self::Data(_) => todo!(),
|
||||
Self::Struct(CxprStruct { ty, data })
|
||||
=> format!("{} {{ {} }}", ty.name,
|
||||
data.borrow().iter().map(|(k, v)| format!("{}: {}", k, v.repr()))
|
||||
.collect::<Vec<String>>().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!(),
|
||||
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue