Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rest params for function declarations #205

Closed
wants to merge 11 commits into from
6 changes: 3 additions & 3 deletions src/lib/builtins/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,14 @@ pub struct RegularFunction {
pub object: Object,
/// This function's expression
pub expr: Expr,
/// The argument names of the function
pub args: Vec<String>,
/// The argument declarations of the function
pub args: Vec<Expr>,
}

impl RegularFunction {
/// Make a new regular function
#[allow(clippy::cast_possible_wrap)]
pub fn new(expr: Expr, args: Vec<String>) -> Self {
pub fn new(expr: Expr, args: Vec<Expr>) -> Self {
let mut object = Object::default();
object.properties.insert(
"arguments".to_string(),
Expand Down
10 changes: 9 additions & 1 deletion src/lib/builtins/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -849,7 +849,15 @@ impl Display for ValueData {
ValueData::Function(ref v) => match *v.borrow() {
Function::NativeFunc(_) => write!(f, "function() {{ [native code] }}"),
Function::RegularFunc(ref rf) => {
write!(f, "function({}){}", rf.args.join(", "), rf.expr)
write!(f, "function(")?;
let last_index = rf.args.len() - 1;
for (index, arg) in rf.args.iter().enumerate() {
write!(f, "{}", arg)?;
if index != last_index {
write!(f, ", ")?;
}
}
write!(f, "){}", rf.expr)
}
},
}
Expand Down
49 changes: 38 additions & 11 deletions src/lib/exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,13 @@ impl Executor for Interpreter {
));

for i in 0..data.args.len() {
let name = data.args.get(i).expect("Could not get data argument");
let arg_expr =
data.args.get(i).expect("Could not get data argument");
let name = match arg_expr.def {
ExprDef::Local(ref n) => Some(n),
_ => None,
}
.expect("Could not get argument");
let expr = v_args.get(i).expect("Could not get argument");
env.create_mutable_binding(
name.clone(),
Expand Down Expand Up @@ -520,16 +526,37 @@ impl Interpreter {
Some(env.get_current_environment_ref().clone()),
));
for i in 0..data.args.len() {
let name = data.args.get(i).expect("Could not get data argument");
let expr: &Value = arguments_list.get(i).expect("Could not get argument");
self.realm.environment.create_mutable_binding(
name.clone(),
false,
VariableScope::Function,
);
self.realm
.environment
.initialize_binding(name, expr.clone());
let arg_expr = data.args.get(i).expect("Could not get data argument");
match arg_expr.def {
ExprDef::Local(ref name) => {
let expr: &Value =
arguments_list.get(i).expect("Could not get argument");
self.realm.environment.create_mutable_binding(
name.clone(),
false,
VariableScope::Function,
);
self.realm
.environment
.initialize_binding(name, expr.clone());
}
ExprDef::UnaryOp(UnaryOp::Spread, ref expr) => {
if let ExprDef::Local(ref name) = expr.def {
let array = array::new_array(self)?;
array::add_to_array_object(&array, &arguments_list[i..])?;

self.realm.environment.create_mutable_binding(
name.clone(),
false,
VariableScope::Function,
);
self.realm.environment.initialize_binding(name, array);
} else {
panic!("Unsupported function argument declaration")
}
}
_ => panic!("Unsupported function argument declaration"),
}
}

// Add arguments object
Expand Down
23 changes: 15 additions & 8 deletions src/lib/syntax/ast/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ pub enum ExprDef {
Construct(Box<Expr>, Vec<Expr>),
/// Run several expressions from top-to-bottom
Block(Vec<Expr>),
/// Load a reference to a value
/// Load a reference to a value, or a function argument
Local(String),
/// Gets the constant field of a value
GetConstField(Box<Expr>, String),
Expand All @@ -61,9 +61,9 @@ pub enum ExprDef {
/// Create an array with items inside
ArrayDecl(Vec<Expr>),
/// Create a function with the given name, arguments, and expression
FunctionDecl(Option<String>, Vec<String>, Box<Expr>),
FunctionDecl(Option<String>, Vec<Expr>, Box<Expr>),
/// Create an arrow function with the given arguments and expression
ArrowFunctionDecl(Vec<String>, Box<Expr>),
ArrowFunctionDecl(Vec<Expr>, Box<Expr>),
/// Return the expression from a function
Return(Option<Box<Expr>>),
/// Throw a value
Expand Down Expand Up @@ -181,12 +181,19 @@ impl Display for ExprDef {
join_expr(f, arr)?;
f.write_str("]")
}
ExprDef::FunctionDecl(ref name, ref args, ref expr) => match name {
Some(val) => write!(f, "function {}({}){}", val, args.join(", "), expr),
None => write!(f, "function ({}){}", args.join(", "), expr),
},
ExprDef::FunctionDecl(ref name, ref args, ref expr) => {
write!(f, "function ")?;
if let Some(func_name) = name {
f.write_fmt(format_args!("{}", func_name))?;
}
write!(f, "{{")?;
join_expr(f, args)?;
write!(f, "}} {}", expr)
}
ExprDef::ArrowFunctionDecl(ref args, ref expr) => {
write!(f, "({}) => {}", args.join(", "), expr)
write!(f, "(")?;
join_expr(f, args)?;
write!(f, ") => {}", expr)
}
ExprDef::BinOp(ref op, ref a, ref b) => write!(f, "{} {} {}", a, op, b),
ExprDef::UnaryOp(ref op, ref a) => write!(f, "{}{}", op, a),
Expand Down
3 changes: 3 additions & 0 deletions src/lib/syntax/ast/op.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ pub enum UnaryOp {
Not,
/// `~a` - bitwise-not of the value
Tilde,
/// `...a` - spread an iterable value
Spread,
}

impl Display for UnaryOp {
Expand All @@ -80,6 +82,7 @@ impl Display for UnaryOp {
UnaryOp::Minus => "-",
UnaryOp::Not => "!",
UnaryOp::Tilde => "~",
UnaryOp::Spread => "...",
}
)
}
Expand Down
1 change: 1 addition & 0 deletions src/lib/syntax/lexer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,7 @@ impl<'a> Lexer<'a> {
if self.next_is('.') {
if self.next_is('.') {
self.push_punc(Punctuator::Spread);
self.column_number += 2;
} else {
return Err(LexerError::new("Expecting Token ."));
}
Expand Down
Loading