Skip to content

Commit cc2143d

Browse files
committed
Allow calling functions on arbitrary script types
1 parent 2e988b1 commit cc2143d

File tree

5 files changed

+110
-22
lines changed

5 files changed

+110
-22
lines changed

script/examples/vec2.bs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,4 @@ fn main()
33

44

55
fn length(x, y)
6-
#return (x * x + y * y).sqrt()
7-
return x * x + y * y
6+
return (x * x + y * y).sqrt()

script/src/ast.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ pub(crate) enum Expression<'src> {
6060
right: Box<Expression<'src>>,
6161
},
6262
Function {
63+
expr: Option<Box<Expression<'src>>>,
6364
name: &'src str,
6465
arguments: Vec<Expression<'src>>,
6566
},
@@ -292,7 +293,8 @@ impl<'src> Expression<'src> {
292293
None => err!(UnexpectedEOF, 0, 0),
293294
}
294295
}
295-
(Expression::Function { name, arguments }, None)
296+
let expr = None;
297+
(Expression::Function { expr, name, arguments }, None)
296298
}
297299
Some((tk, ..)) => (Expression::Atom(Atom::Name(name)), Some(tk)),
298300
e => todo!("{:?}", e),
@@ -303,6 +305,7 @@ impl<'src> Expression<'src> {
303305
match last_tk {
304306
Token::Op(opl) => match tokens.next() {
305307
Some((Token::Name(mid), ..)) => {
308+
let og_mid = mid;
306309
let mid = Expression::Atom(Atom::Name(mid));
307310
match tokens.next() {
308311
Some((Token::Op(opr), ..)) => match tokens.next() {
@@ -332,6 +335,20 @@ impl<'src> Expression<'src> {
332335
}
333336
e => todo!("{:?}", e),
334337
},
338+
Some((Token::BracketRoundOpen, ..)) => {
339+
// FIXME handle function args & check for ending brace
340+
tokens.next();
341+
match opl {
342+
Op::Access => {
343+
Ok((Expression::Function {
344+
expr: Some(Box::new(lhs)),
345+
name: og_mid,
346+
arguments: Vec::new(),
347+
}, Some(Token::BracketRoundClose)))
348+
}
349+
e => todo!("{:?}", e),
350+
}
351+
}
335352
Some((tk, ..)) if tk == Token::BracketRoundClose => Ok((
336353
Expression::Operation {
337354
left: Box::new(lhs),

script/src/bytecode.rs

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,7 @@ pub(crate) struct CallArgs {
1515

1616
#[derive(Debug)]
1717
pub(crate) enum Instruction {
18-
// TODO avoid box
19-
//Call(Box<(u16, Box<str>)>),
18+
Call(Box<(u16, CallArgs)>),
2019
CallSelf(Box<CallArgs>),
2120
CallGlobal(Box<CallArgs>),
2221
//Iter(Box<dyn Iterator<Item = Box<dyn ScriptType>>>),
@@ -168,7 +167,7 @@ impl ByteCode {
168167
}
169168
Atom::Name(a) => todo!("call {:?}", a),
170169
},
171-
Expression::Function { name, arguments } => {
170+
Expression::Function { name, arguments, expr } => {
172171
let mut args = Vec::with_capacity(arguments.len());
173172
let store_in = *curr_var_count;
174173
*curr_var_count += 1;
@@ -357,6 +356,42 @@ impl ByteCode {
357356
}
358357
a => todo!("{:?}", a),
359358
},
359+
Expression::Function { expr, name, arguments } => {
360+
let expr = if let Some(e) = expr { e } else { todo!("none expr in fn") };
361+
let og_cvc = *curr_var_count;
362+
let r = *curr_var_count;
363+
*curr_var_count += 1;
364+
let e = Self::parse_expression(r, expr, locals, instr, vars, min_var_count, curr_var_count)?;
365+
let expr = if let Some(e) = e { e } else { r };
366+
let mut args = Vec::with_capacity(arguments.len());
367+
for a in arguments {
368+
let r = *curr_var_count;
369+
*curr_var_count += 1;
370+
let e = Self::parse_expression(
371+
r,
372+
a,
373+
locals,
374+
instr,
375+
vars,
376+
min_var_count,
377+
curr_var_count,
378+
)?;
379+
if let Some(e) = e {
380+
args.push(e);
381+
*curr_var_count -= 1;
382+
} else {
383+
args.push(r);
384+
}
385+
}
386+
let ca = CallArgs {
387+
store_in: Some(store),
388+
func: (*name).into(),
389+
args: args.into_boxed_slice(),
390+
};
391+
instr.push(Instruction::Call(Box::new((expr, ca))));
392+
*curr_var_count = og_cvc;
393+
Ok(None)
394+
}
360395
e => todo!("{:#?}", e),
361396
}
362397
}
@@ -387,10 +422,24 @@ impl ByteCode {
387422
let err_roob = || RunError::RegisterOutOfBounds;
388423
let err_uf = || RunError::UndefinedFunction;
389424
let err_env = |e| RunError::EnvironmentError(e);
425+
let err_call = |e| RunError::CallError(Box::new(e));
390426
if let Some(instr) = self.code.get(ip as usize) {
391427
ip += 1;
392428
use Instruction::*;
393429
match instr {
430+
Call(box (reg, CallArgs { store_in, func, args })) => {
431+
let r = {
432+
let mut ca = Vec::with_capacity(args.len());
433+
for &a in args.iter() {
434+
ca.push(vars.get(a as usize).ok_or(err_roob())?.as_ref());
435+
}
436+
let obj = vars.get(*reg as usize).ok_or(err_roob())?.as_ref();
437+
obj.call(func, &ca[..]).map_err(err_call)?
438+
};
439+
if let Some(reg) = store_in {
440+
*vars.get_mut(*reg as usize).ok_or(err_roob())? = r;
441+
}
442+
}
394443
CallGlobal(box CallArgs {
395444
store_in,
396445
func,

script/src/script.rs

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use core::fmt::Debug;
44
use core::ops::{Add, Mul};
55
use rustc_hash::FxHashMap;
66
use std::sync::Arc;
7+
use std::cell::RefCell;
78

89
pub struct Class(Arc<Script>);
910

@@ -16,7 +17,20 @@ pub(crate) struct Script {
1617
#[derive(Debug)]
1718
pub struct Instance {
1819
script: Arc<Script>,
19-
variables: Box<[Box<dyn ScriptType>]>,
20+
// TODO we have 3 options to solve the `A.foo() -> B.bar() -> A.foo()` problem:
21+
// * We use `Cell<Box<[_]>>`. This *should* have relatively little overhead but is
22+
// very unintuitive, and the second `A.foo()` call will only see old variable names.
23+
// It is also likely still less efficient than `RefCell` due to an extra alloc.
24+
// * We use `RefCell<Box<[_]>>`. This is arguably the most "Rusty" way in terms of
25+
// "don't alias stuff", but is not very intuitive compared to other languages
26+
// and may also end up being impractical.
27+
// * We use `Box<[Cell<_>]>`. This will allow mimicking other scripting languages in
28+
// terms of (expected) behaviour but may be less efficient than `RefCell`.
29+
// The second and third option should be measured for performance. If the latter is
30+
// fast enough (or perhaps even faster) we should use that.
31+
// For now, the second option is chosen as the third can't be undone without being a
32+
// massive breaking change.
33+
variables: RefCell<Box<[Box<dyn ScriptType>]>>,
2034
}
2135

2236
#[derive(Debug)]
@@ -29,10 +43,11 @@ pub enum CallError {
2943
IsEmpty,
3044
InvalidOperator,
3145
IncompatibleType,
46+
AlreadyBorrowed,
3247
}
3348

3449
pub trait ScriptType: Debug + Any {
35-
fn call(&mut self, function: &str, args: &[&dyn ScriptType]) -> CallResult<CallError>;
50+
fn call(&self, function: &str, args: &[&dyn ScriptType]) -> CallResult<CallError>;
3651

3752
fn dup(&self) -> Box<dyn ScriptType>;
3853

@@ -129,7 +144,7 @@ impl Class {
129144
pub fn instance(&self) -> Instance {
130145
Instance {
131146
script: self.0.clone(),
132-
variables: Box::new([]),
147+
variables: RefCell::new(Box::new([])),
133148
}
134149
}
135150
}
@@ -141,8 +156,12 @@ impl From<Script> for Class {
141156
}
142157

143158
impl ScriptType for Instance {
144-
fn call(&mut self, function: &str, args: &[&dyn ScriptType]) -> CallResult<CallError> {
145-
self.script.call(function, &mut self.variables, args)
159+
fn call(&self, function: &str, args: &[&dyn ScriptType]) -> CallResult<CallError> {
160+
if let Ok(mut vars) = self.variables.try_borrow_mut() {
161+
self.script.call(function, &mut vars, args)
162+
} else {
163+
Err(CallError::AlreadyBorrowed)
164+
}
146165
}
147166

148167
fn dup(&self) -> Box<dyn ScriptType> {
@@ -155,24 +174,27 @@ impl ScriptType for Instance {
155174
}
156175

157176
impl ScriptType for () {
158-
fn call(&mut self, _: &str, _: &[&dyn ScriptType]) -> CallResult<CallError> {
177+
fn call(&self, _: &str, _: &[&dyn ScriptType]) -> CallResult<CallError> {
159178
Err(CallError::IsEmpty)
160179
}
161180

162181
impl_dup!();
163182
}
164183

165184
impl ScriptType for isize {
166-
fn call(&mut self, _: &str, _: &[&dyn ScriptType]) -> CallResult<CallError> {
185+
fn call(&self, _: &str, _: &[&dyn ScriptType]) -> CallResult<CallError> {
167186
todo!()
168187
}
169188

170189
impl_dup!();
171190
}
172191

173192
impl ScriptType for f64 {
174-
fn call(&mut self, _: &str, _: &[&dyn ScriptType]) -> CallResult<CallError> {
175-
todo!()
193+
fn call(&self, func: &str, _: &[&dyn ScriptType]) -> CallResult<CallError> {
194+
Ok(Box::new(match func {
195+
"sqrt" => self.sqrt(),
196+
_ => return Err(CallError::UndefinedFunction),
197+
}))
176198
}
177199

178200
impl_dup!();
@@ -193,23 +215,23 @@ impl ScriptType for f64 {
193215
}
194216

195217
impl ScriptType for Box<str> {
196-
fn call(&mut self, _: &str, _: &[&dyn ScriptType]) -> CallResult<CallError> {
218+
fn call(&self, _: &str, _: &[&dyn ScriptType]) -> CallResult<CallError> {
197219
todo!()
198220
}
199221

200222
impl_dup!();
201223
}
202224

203225
impl ScriptType for char {
204-
fn call(&mut self, _: &str, _: &[&dyn ScriptType]) -> CallResult<CallError> {
226+
fn call(&self, _: &str, _: &[&dyn ScriptType]) -> CallResult<CallError> {
205227
todo!()
206228
}
207229

208230
impl_dup!();
209231
}
210232

211233
impl ScriptType for String {
212-
fn call(&mut self, _: &str, _: &[&dyn ScriptType]) -> CallResult<CallError> {
234+
fn call(&self, _: &str, _: &[&dyn ScriptType]) -> CallResult<CallError> {
213235
todo!()
214236
}
215237

script/src/tokenizer.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ pub enum Op {
1717
Greater,
1818
ShiftLeft,
1919
ShiftRight,
20+
Access,
2021
}
2122

2223
#[derive(Copy, Clone, Debug, PartialEq)]
@@ -57,7 +58,6 @@ pub enum Token<'src> {
5758
Return,
5859
Comma,
5960
Pass,
60-
Dot,
6161
}
6262

6363
#[derive(Debug, PartialEq)]
@@ -84,6 +84,7 @@ impl Op {
8484
fn precedence(&self) -> i8 {
8585
use Op::*;
8686
match *self {
87+
Access => 12,
8788
Not => 11,
8889
Mul | Div | Rem => 10,
8990
Add | Sub => 9,
@@ -104,7 +105,7 @@ impl PartialOrd for Op {
104105
}
105106

106107
impl Token<'_> {
107-
const OPERATORS: &'static str = "=+-*/%&|^!<>";
108+
const OPERATORS: &'static str = "=+-*/%&|^!<>.";
108109
const BRACKETS: &'static str = "()[]{}";
109110

110111
fn parse(source: &str, start_of_file: bool) -> Result<(Token, usize), TokenError> {
@@ -141,7 +142,6 @@ impl Token<'_> {
141142
'{' => Ok((Token::BracketCurlyOpen, start + 1)),
142143
'}' => Ok((Token::BracketCurlyClose, start + 1)),
143144
',' => Ok((Token::Comma, start + 1)),
144-
'.' => Ok((Token::Dot, start + 1)),
145145
'"' => loop {
146146
if let Some((i, c)) = chars.next() {
147147
if c == '"' {
@@ -187,7 +187,8 @@ impl Token<'_> {
187187
'<' => Ok((Token::Op(Op::Less), i)),
188188
'>' => Ok((Token::Op(Op::Greater), i)),
189189
'!' => Ok((Token::Op(Op::Not), i)),
190-
_ => unreachable!(),
190+
'.' => Ok((Token::Op(Op::Access), i)),
191+
c => unreachable!("operator '{}' not covered", c),
191192
}
192193
}
193194
_ if c.is_digit(10) => {

0 commit comments

Comments
 (0)