diff --git a/juno_scheduler/src/compile.rs b/juno_scheduler/src/compile.rs index 3c288ca7a61c855b3982ba6ed66c215c9e2942fe..7b8c5020de1688bc509a07ed16226c1d3714ecd4 100644 --- a/juno_scheduler/src/compile.rs +++ b/juno_scheduler/src/compile.rs @@ -488,6 +488,29 @@ fn compile_expr( rhs: Box::new(rhs), })) } + parser::Expr::Tuple { + span: _, + exps, + } => { + let exprs = exps.into_iter() + .map(|e| compile_exp_as_expr(e, lexer, macrostab, macros)) + .fold(Ok(vec![]), + |mut res, exp| { + let mut res = res?; + res.push(exp?); + Ok(res) + })?; + Ok(ExprResult::Expr(ir::ScheduleExp::Tuple { exprs })) + } + parser::Expr::TupleField { + span: _, + lhs, + field, + } => { + let lhs = compile_exp_as_expr(*lhs, lexer, macrostab, macros)?; + let field = lexer.span_str(field).parse().expect("Parsing"); + Ok(ExprResult::Expr(ir::ScheduleExp::TupleField { lhs: Box::new(lhs), field })) + } } } diff --git a/juno_scheduler/src/ir.rs b/juno_scheduler/src/ir.rs index 3a087c0d40093c6363e67332c1bd489f22727a42..71a185ba65b4cd5ade237cbfe164cce8067a475c 100644 --- a/juno_scheduler/src/ir.rs +++ b/juno_scheduler/src/ir.rs @@ -127,6 +127,13 @@ pub enum ScheduleExp { lhs: Box<ScheduleExp>, rhs: Box<ScheduleExp>, }, + Tuple { + exprs: Vec<ScheduleExp>, + }, + TupleField { + lhs: Box<ScheduleExp>, + field: usize, + }, // This is used to "box" a selection by evaluating it at one point and then // allowing it to be used as a selector later on Selection { diff --git a/juno_scheduler/src/lang.y b/juno_scheduler/src/lang.y index 3b030e1d42bdb970cdfa67d21c4198dc89edea9e..5903e42941cef535128765cb6ab98601987d6d84 100644 --- a/juno_scheduler/src/lang.y +++ b/juno_scheduler/src/lang.y @@ -56,10 +56,14 @@ Expr -> Expr { Expr::String { span: $span } } | Expr '.' 'ID' { Expr::Field { span: $span, lhs: Box::new($1), field: span_of_tok($3) } } + | Expr '.' 'INT' + { Expr::TupleField { span: $span, lhs: Box::new($1), field: span_of_tok($3) } } | Expr '@' 'ID' { Expr::Field { span: $span, lhs: Box::new($1), field: span_of_tok($3) } } - | '(' Expr ')' - { $2 } + | '(' Exprs ')' + { exprs_to_expr($span, $2) } + | '[' Exprs ']' + { exprs_to_expr($span, $2) } | '{' Schedule '}' { Expr::BlockExpr { span: $span, body: Box::new($2) } } | '<' Fields '>' @@ -73,14 +77,18 @@ Expr -> Expr ; Args -> Vec<Expr> - : { vec![] } - | '[' Exprs ']' { rev($2) } + : { vec![] } + | '[' RExprs ']' { rev($2) } ; Exprs -> Vec<Expr> - : { vec![] } - | Expr { vec![$1] } - | Expr ',' Exprs { snoc($1, $3) } + : RExprs { rev($1) } + ; + +RExprs -> Vec<Expr> + : { vec![] } + | Expr { vec![$1] } + | Expr ',' RExprs { snoc($1, $3) } ; Fields -> Vec<(Span, Expr)> @@ -180,6 +188,8 @@ pub enum Expr { BlockExpr { span: Span, body: Box<OperationList> }, Record { span: Span, fields: Vec<(Span, Expr)> }, SetOp { span: Span, op: SetOp, lhs: Box<Expr>, rhs: Box<Expr> }, + Tuple { span: Span, exps: Vec<Expr> }, + TupleField { span: Span, lhs: Box<Expr>, field: Span }, } pub enum Selector { @@ -193,3 +203,11 @@ pub struct MacroDecl { pub selection_name: Span, pub def: Box<OperationList>, } + +fn exprs_to_expr(span: Span, mut exps: Vec<Expr>) -> Expr { + if exps.len() == 1 { + exps.pop().unwrap() + } else { + Expr::Tuple { span, exps } + } +} diff --git a/juno_scheduler/src/pm.rs b/juno_scheduler/src/pm.rs index 5f2fa4cce02e04ca4499cbff8806356f81cc86bc..ef9ec038c12f10ffb2a025a7aa347a5ee5d02d9d 100644 --- a/juno_scheduler/src/pm.rs +++ b/juno_scheduler/src/pm.rs @@ -294,6 +294,9 @@ pub enum Value { Record { fields: HashMap<String, Value>, }, + Tuple { + values: Vec<Value>, + }, Everything {}, Selection { selection: Vec<Value>, @@ -371,6 +374,11 @@ impl Value { "Expected code selection, found record".to_string(), )); } + Value::Tuple { .. } => { + return Err(SchedulerError::SemanticError( + "Expected code selection, found tuple".to_string(), + )); + } Value::Integer { .. } => { return Err(SchedulerError::SemanticError( "Expected code selection, found integer".to_string(), @@ -1291,6 +1299,7 @@ fn interp_expr( | Value::Integer { .. } | Value::Boolean { .. } | Value::String { .. } + | Value::Tuple { .. } | Value::SetOp { .. } => Err(SchedulerError::UndefinedField(field.clone())), Value::JunoFunction { func } => { match pm.labels.borrow().iter().position(|s| s == field) { @@ -1463,7 +1472,24 @@ fn interp_expr( } Ok((Value::Selection { selection: values }, changed)) } - }, + } + ScheduleExp::Tuple { exprs } => { + let mut vals = vec![]; + let mut changed = false; + for exp in exprs { + let (val, change) = interp_expr(pm, exp, stringtab, env, functions)?; + vals.push(val); + changed = changed || change; + } + Ok((Value::Tuple { values: vals }, changed)) + } + ScheduleExp::TupleField { lhs, field } => { + let (val, changed) = interp_expr(pm, lhs, stringtab, env, functions)?; + match val { + Value::Tuple { values } if *field < values.len() => Ok((vec_take(values, *field), changed)), + _ => Err(SchedulerError::SemanticError(format!("No field at index {}", field))), + } + } } } @@ -1521,6 +1547,15 @@ fn update_value( Some(Value::Record { fields: new_fields }) } } + // For tuples, if we deleted values like we do for records this would mess up the indices + // which would behave very strangely. Instead if any field cannot be updated then we + // eliminate the entire value + Value::Tuple { values } => { + values.into_iter() + .map(|v| update_value(v, func_idx, juno_func_idx)) + .collect::<Option<Vec<_>>>() + .map(|values| Value::Tuple { values }) + } Value::JunoFunction { func } => { juno_func_idx[func.idx] .clone() @@ -3016,3 +3051,7 @@ where }); labels } + +fn vec_take<T>(mut v: Vec<T>, index: usize) -> T { + v.swap_remove(index) +}