This commit is contained in:
TriMill 2022-09-22 08:58:59 -04:00
parent 53bef7b5ed
commit 350f775b6c
7 changed files with 154 additions and 62 deletions

View File

@ -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!()
}
}

View File

@ -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 {

View File

@ -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, "+="),

View File

@ -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()?;

View File

@ -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(),
})
}

View File

@ -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,

View File

@ -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!(),
}
}