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

support for optional arguments #260

Merged
merged 18 commits into from
Jul 11, 2024
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 31 additions & 6 deletions src/modules/function/declaration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ use std::mem::swap;

use heraclitus_compiler::prelude::*;
use itertools::izip;
use crate::modules::types::Type;
use crate::modules::expression::expr::Expr;
use crate::modules::types::{Type, Typed};
use crate::modules::variable::variable_name_extensions;
use crate::utils::cc_flags::get_ccflag_by_name;
use crate::utils::function_cache::FunctionInstance;
use crate::utils::function_interface::FunctionInterface;
use crate::utils::metadata::{ParserMetadata, TranslateMetadata};
use crate::translate::module::TranslateModule;
use crate::modules::types::parse_type;

use super::declaration_utils::*;

#[derive(Debug, Clone)]
Expand All @@ -20,6 +20,7 @@ pub struct FunctionDeclaration {
pub arg_refs: Vec<bool>,
pub arg_names: Vec<String>,
pub arg_types: Vec<Type>,
pub arg_optionals: Vec<Expr>,
pub returns: Type,
pub id: usize,
pub is_public: bool
Expand Down Expand Up @@ -56,6 +57,7 @@ impl SyntaxModule<ParserMetadata> for FunctionDeclaration {
arg_names: vec![],
arg_types: vec![],
arg_refs: vec![],
arg_optionals: vec![],
returns: Type::Generic,
id: 0,
is_public: false
Expand Down Expand Up @@ -88,6 +90,7 @@ impl SyntaxModule<ParserMetadata> for FunctionDeclaration {
let tok = meta.get_current_token();
self.name = variable(meta, variable_name_extensions())?;
handle_existing_function(meta, tok.clone())?;
let mut optional = false;
context!({
// Set the compiler flags
swap(&mut meta.context.cc_flags, &mut flags);
Expand All @@ -98,24 +101,45 @@ impl SyntaxModule<ParserMetadata> for FunctionDeclaration {
break
}
let is_ref = token(meta, "ref").is_ok();
let name_token = meta.get_current_token();
let name = variable(meta, variable_name_extensions())?;
// Optionally parse the argument type
let mut arg_type = Type::Generic;
match token(meta, ":") {
Ok(_) => {
self.arg_refs.push(is_ref);
self.arg_names.push(name.clone());
self.arg_types.push(parse_type(meta)?);
arg_type = parse_type(meta)?;
self.arg_types.push(arg_type.clone());
},
Err(_) => {
self.arg_refs.push(is_ref);
self.arg_names.push(name.clone());
self.arg_types.push(Type::Generic);
}
}
let tok = meta.get_current_token();
if token(meta, "=").is_ok() {
return error!(meta, tok, "Default values for function arguments are not yet supported")
match token(meta,"=") {
Ok(_) => {
if is_ref {
return error!(meta, name_token, "A ref cannot be optional");
}
optional = true;
let mut expr = Expr::new();
syntax(meta, &mut expr)?;
if arg_type != Type::Generic {
if arg_type != expr.get_type() {
return error!(meta, name_token, "Optional argument does not match annotated type");
}
}
self.arg_optionals.push(expr);
},
Err(_) => {
if optional {
return error!(meta, name_token, "All arguments following an optional argument must also be optional");
}
},
}

match token(meta, ")") {
Ok(_) => break,
Err(_) => token(meta, ",")?
Expand All @@ -141,6 +165,7 @@ impl SyntaxModule<ParserMetadata> for FunctionDeclaration {
arg_types: self.arg_types.clone(),
arg_refs: self.arg_refs.clone(),
returns: self.returns.clone(),
arg_optionals: self.arg_optionals.clone(),
is_public: self.is_public,
is_failable
}, ctx)?;
Expand Down
17 changes: 17 additions & 0 deletions src/modules/function/invocation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,22 @@ impl SyntaxModule<ParserMetadata> for FunctionInvocation {
};
}
let function_unit = meta.get_fun_declaration(&self.name).unwrap().clone();
let expected_arg_count = function_unit.arg_refs.len();
let actual_arg_count = self.args.len();
let optional_count = function_unit.arg_optionals.len();

// Case when function call is missing arguments
if actual_arg_count < expected_arg_count {
// Check if we can compensate with optional arguments stored in fun_unit
if actual_arg_count >= expected_arg_count - optional_count {
let missing = expected_arg_count - actual_arg_count;
let provided_optional = optional_count - missing;
for exp in function_unit.arg_optionals.iter().skip(provided_optional){
self.args.push(exp.clone());
}
}
Mte90 marked this conversation as resolved.
Show resolved Hide resolved
}

self.is_failable = function_unit.is_failable;
if self.is_failable {
match syntax(meta, &mut self.failed) {
Expand Down Expand Up @@ -136,6 +152,7 @@ impl TranslateModule for FunctionInvocation {
.unwrap_or(translation)
}
}).collect::<Vec<String>>().join(" ");

meta.stmt_queue.push_back(format!("{name} {args}{silent}"));
let invocation_return = &format!("__AF_{}{}_v{}", self.name, self.id, self.variant_id);
let invocation_instance = &format!("__AF_{}{}_v{}__{}_{}", self.name, self.id, self.variant_id, self.line, self.col);
Expand Down
7 changes: 5 additions & 2 deletions src/modules/function/invocation_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,14 @@ fn ordinal_number(index: usize) -> String {
fn run_function_with_args(meta: &mut ParserMetadata, mut fun: FunctionDecl, args: &[Type], tok: Option<Token>) -> Result<(Type, usize), Failure> {
// Check if there are the correct amount of arguments
if fun.arg_names.len() != args.len() {
let max_args = fun.arg_names.len();
let min_args = fun.arg_names.len() - fun.arg_optionals.len();
let opt_argument = if max_args > min_args {&format!(" ({} optional)",max_args)} else {""};
Ph0enixKM marked this conversation as resolved.
Show resolved Hide resolved
// Determine the correct grammar
let txt_arguments = if fun.arg_names.len() == 1 { "argument" } else { "arguments" };
let txt_arguments = if min_args == 1 { "argument" } else { "arguments" };
let txt_given = if args.len() == 1 { "was given" } else { "were given" };
// Return an error
return error!(meta, tok, format!("Function '{}' expects {} {txt_arguments}, but {} {txt_given}", fun.name, fun.arg_names.len(), args.len()))
return error!(meta, tok, format!("Function '{}' expects {} {txt_arguments}{opt_argument}, but {} {txt_given}", fun.name, min_args, args.len()))
ramseyharrison marked this conversation as resolved.
Show resolved Hide resolved
}
// Check if the function argument types match
if fun.is_args_typed {
Expand Down
58 changes: 58 additions & 0 deletions src/tests/validity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1112,3 +1112,61 @@ fn unsafe_function_call() {
";
test_amber!(code, "6, 0")
}


#[test]
fn optional_argument_int_default(){
let code = "
fun addition(a: Num, b: Num = 100): Num {
return a + b
}
let result = addition(10)
echo \"{result}\"
";
test_amber!(code, "110")
}

#[test]
fn optional_argument_int(){
let code = "
fun addition(a: Num, b: Num = 100): Num {
return a + b
}
let result = addition(10,1000)
echo \"{result}\"
";
test_amber!(code, "1010")
}

#[test]
fn optional_argument_array_default(){
let code = "
fun sum_array(a : [Num] = [100,200,300,400]): Num {
let sum = 0
loop n in a {
sum += n;
}
return sum;
}
let result = sum_array();
echo \"{1000}\"
";
test_amber!(code, "1000")
}
#[test]
fn optional_argument_array(){
let code = "
fun sum_array(a : [Num] = [Num]): Num {
let sum = 0
loop n in a {
sum += n;
}
return sum;
}
let x = [100,1000]
let result = sum_array(x)
echo \"{1100}\"
";
test_amber!(code, "1100")
}

3 changes: 3 additions & 0 deletions src/utils/context.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use heraclitus_compiler::prelude::*;
use std::collections::{HashMap, HashSet};
use crate::modules::expression::expr::Expr;
use crate::modules::types::Type;

use super::{function_interface::FunctionInterface, cc_flags::CCFlags};
Expand All @@ -10,6 +11,7 @@ pub struct FunctionDecl {
pub arg_names: Vec<String>,
pub arg_types: Vec<Type>,
pub arg_refs: Vec<bool>,
pub arg_optionals : Vec<Expr>,
pub returns: Type,
pub is_args_typed: bool,
pub is_public: bool,
Expand All @@ -25,6 +27,7 @@ impl FunctionDecl {
arg_names: self.arg_names,
arg_types: self.arg_types,
arg_refs: self.arg_refs,
arg_optionals: self.arg_optionals,
returns: self.returns,
is_public: self.is_public,
is_failable: self.is_failable
Expand Down
3 changes: 3 additions & 0 deletions src/utils/function_interface.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::modules::{types::Type, block::Block};
use crate::modules::expression::expr::Expr;
use super::{context::FunctionDecl, function_cache::FunctionInstance};


Expand All @@ -10,6 +11,7 @@ pub struct FunctionInterface {
pub arg_names: Vec<String>,
pub arg_types: Vec<Type>,
pub arg_refs: Vec<bool>,
pub arg_optionals : Vec<Expr>,
pub returns: Type,
pub is_public: bool,
pub is_failable: bool,
Expand All @@ -23,6 +25,7 @@ impl FunctionInterface {
arg_names: self.arg_names,
arg_types: self.arg_types,
arg_refs: self.arg_refs,
arg_optionals: self.arg_optionals,
returns: self.returns,
is_args_typed,
is_public: self.is_public,
Expand Down
Loading