Skip to content

Commit 43b0560

Browse files
Chaining member accesses and calling closures inside of lists and objects
1 parent 27e57d6 commit 43b0560

4 files changed

Lines changed: 60 additions & 61 deletions

File tree

docs/crust-language.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ Inline statements are single-line statements that perform a specific action. The
2727
- `nop`: Does absolutely nothing.
2828
- `assert condition`: Checks if the condition is true, and if not, it throws an error. Example: `assert x > 0`
2929
- `function_name(arguments)`: Calls a function or a closure with the given arguments. Example: `goto("mouse")`
30+
31+
!!! note
32+
Closures can be put inside of lists, and calling them looks like this: `list[0](1, 2)`. This calls the closure at index 0 of the list with the arguments 1 and 2. Another example is `object.key(1, 2)`, which calls the closure at the key `key` of the object with the arguments 1 and 2.
33+
3034
- `import "file.crst"`: Imports a Crust file. The file can contain functions and variables. Import statements are defined at the head of the program. Example: `import "utils.crst"`
3135

3236
To assign a list's or object's value, you can use the following syntax:

src/utils/helpers.rs

Lines changed: 15 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -170,32 +170,26 @@ pub fn resolve_expression(expr: &Expression, state: &mut State) -> Value {
170170
}
171171
}
172172
Expression::Call { function, args } => {
173+
// evaluate the function expression
174+
let func_val = resolve_expression(function, state);
175+
176+
// evaluate args
173177
let args = args
174178
.iter()
175179
.map(|arg| resolve_expression(arg, state))
176180
.collect::<Vec<_>>();
177-
if let Some(function_struct) = state.sprite.functions.get(function).cloned() {
178-
function_struct.call(state, &args).unwrap_or_else(|e| {
179-
println!("Error calling function {}(): {}", function, e);
180-
Value::Null
181-
})
182-
} else if let Some(function_struct) = state.project.builtins.get(function).cloned() {
183-
function_struct.call(state, &args).unwrap_or_else(|e| {
184-
println!("Error calling builtin function {}(): {}", function, e);
185-
Value::Null
186-
})
187-
} else if let Some(variable) = state.sprite.variables.get(function).cloned() {
188-
let Value::Closure(closure) = variable else {
189-
println!("Variable '{}' is not a function", function);
190-
return Value::Null;
191-
};
192-
let function_struct = &*closure;
193-
function_struct.call(state, &args).unwrap_or_else(|e| {
194-
println!("Error calling function '{}': {}", function, e);
181+
182+
match func_val {
183+
Value::Closure(callable) => {
184+
callable.call(state, &args).unwrap_or_else(|e| {
185+
println!("Error calling function: {}", e);
186+
Value::Null
187+
})
188+
}
189+
_ => {
190+
println!("Attempted to call non-function: {:?}", func_val);
195191
Value::Null
196-
})
197-
} else {
198-
return Value::Null;
192+
}
199193
}
200194
}
201195
}

src/utils/language/parser.rs

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ pub enum Expression {
3030
operand: Box<Expression>,
3131
},
3232
Call {
33-
function: String,
33+
function: Box<Expression>,
3434
args: Vec<Expression>,
3535
},
3636
}
@@ -599,7 +599,7 @@ impl Parser {
599599
})
600600
}
601601

602-
fn parse_function_call(&mut self, name: String) -> Result<Expression, String> {
602+
fn parse_function_call(&mut self, base: Expression) -> Result<Expression, String> {
603603
let mut args = vec![];
604604
while self.peek().token_type != TokenType::Symbol(")".to_string()) {
605605
if self.eat(&TokenType::Newline) {
@@ -618,12 +618,12 @@ impl Parser {
618618
));
619619
}
620620
Ok(Expression::Call {
621-
function: name,
621+
function: Box::new(base),
622622
args,
623623
})
624624
}
625625

626-
fn parse_bracket_access(&mut self, name: String) -> Result<Expression, String> {
626+
fn parse_bracket_access(&mut self, base: Expression) -> Result<Expression, String> {
627627
let index = self.parse_binary(0)?;
628628
if !self.eat(&TokenType::Symbol("]".to_string())) {
629629
return Err(format!(
@@ -632,20 +632,20 @@ impl Parser {
632632
));
633633
}
634634
Ok(Expression::ListMemberAccess {
635-
list: Box::new(Expression::Identifier(name)),
635+
list: Box::new(base),
636636
index: Box::new(index),
637637
})
638638
}
639639

640-
fn parse_dot_access(&mut self, name: String) -> Result<Expression, String> {
640+
fn parse_dot_access(&mut self, base: Expression) -> Result<Expression, String> {
641641
let index = self.parse_primary()?;
642642
match index {
643643
Expression::Identifier(index_name) => Ok(Expression::ListMemberAccess {
644-
list: Box::new(Expression::Identifier(name)),
644+
list: Box::new(base),
645645
index: Box::new(Expression::Value(Value::String(index_name))),
646646
}),
647647
Expression::Value(Value::Number(num)) => Ok(Expression::ListMemberAccess {
648-
list: Box::new(Expression::Identifier(name)),
648+
list: Box::new(base),
649649
index: Box::new(Expression::Value(Value::Number(num))),
650650
}),
651651
_ => Err(format!(
@@ -656,15 +656,21 @@ impl Parser {
656656
}
657657

658658
fn parse_identifier_expr(&mut self, name: String) -> Result<Expression, String> {
659-
if self.eat(&TokenType::Symbol("(".to_string())) {
660-
self.parse_function_call(name)
661-
} else if self.eat(&TokenType::Symbol("[".to_string())) {
662-
self.parse_bracket_access(name)
663-
} else if self.eat(&TokenType::Symbol(".".to_string())) {
664-
self.parse_dot_access(name)
665-
} else {
666-
Ok(Expression::Identifier(name))
659+
let mut expr = Expression::Identifier(name);
660+
661+
loop {
662+
if self.eat(&TokenType::Symbol("(".to_string())) {
663+
expr = self.parse_function_call(expr)?;
664+
} else if self.eat(&TokenType::Symbol("[".to_string())) {
665+
expr = self.parse_bracket_access(expr)?;
666+
} else if self.eat(&TokenType::Symbol(".".to_string())) {
667+
expr = self.parse_dot_access(expr)?;
668+
} else {
669+
break;
670+
}
667671
}
672+
673+
Ok(expr)
668674
}
669675

670676
fn parse_pre_incdec(&mut self, op: &str) -> Result<Expression, String> {

src/utils/sprite/sprite.rs

Lines changed: 19 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -543,7 +543,9 @@ impl Sprite {
543543
value,
544544
} => {
545545
let value = crate::utils::resolve_expression(value, state);
546-
crate::utils::assign_expression(identifier, value, state, *is_global);
546+
crate::utils::assign_expression(identifier, value, state, *is_global).unwrap_or_else(|e| {
547+
println!("Error assigning variable '{}': {}", identifier, e);
548+
});
547549
}
548550
Statement::Nop => {}
549551
Statement::Assert { condition } => {
@@ -637,32 +639,25 @@ impl Sprite {
637639
}
638640
Statement::Call(c) => {
639641
if let Expression::Call { function, args } = c {
642+
// evaluate the function expression
643+
let func_val = resolve_expression(function, state);
644+
645+
// evaluate args
640646
let args = args
641647
.iter()
642-
.map(|arg| crate::utils::resolve_expression(arg, state))
648+
.map(|arg| resolve_expression(arg, state))
643649
.collect::<Vec<_>>();
644-
if let Some(callable) = state.sprite.functions.clone().get(function) {
645-
callable.call(state, &args).unwrap_or_else(|e| {
646-
println!("Error calling {}(): {}", function, e);
647-
Value::Null
648-
});
649-
} else if let Some(callable) = state.project.builtins.get(function).cloned() {
650-
callable.call(state, &args).unwrap_or_else(|e| {
651-
println!("Error calling builtin function '{}': {}", function, e);
652-
Value::Null
653-
});
654-
} else if let Some(variable) = state.sprite.variables.get(function).cloned() {
655-
let Value::Closure(closure) = variable else {
656-
println!("Variable '{}' is not a function", function);
657-
return;
658-
};
659-
let function_struct = *closure;
660-
function_struct.call(state, &args).unwrap_or_else(|e| {
661-
println!("Error calling closure '{}': {}", function, e);
662-
Value::Null
663-
});
664-
} else {
665-
println!("Unknown function or variable '{}'", function);
650+
651+
match func_val {
652+
Value::Closure(callable) => {
653+
let _ = callable.call(state, &args).unwrap_or_else(|e| {
654+
println!("Error calling function: {}", e);
655+
Value::Null
656+
});
657+
}
658+
_ => {
659+
println!("Attempted to call non-function: {:?}", func_val);
660+
}
666661
}
667662
}
668663
}

0 commit comments

Comments
 (0)