Skip to content

Commit 4d1392a

Browse files
committed
Add Variant, add unwrap_none dependency
Variant is ~2x faster for numeric types than doing everything through `Rc<dyn ScripType>`. `unwrap_none` got removed in rust-lang/rust#83349, so a tiny, tiny dependency has been added to provide the functionality.
1 parent c7c7a2b commit 4d1392a

File tree

7 files changed

+150
-111
lines changed

7 files changed

+150
-111
lines changed

Cargo.lock

+7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

script/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ edition = "2018"
66

77
[dependencies]
88
rustc-hash = "*"
9+
unwrap_none = "*"
910

1011
[lib]
1112
name = "ballscript"

script/examples/vec2.bs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ fn main()
22
for _ in 10_000_000
33
#empty()
44
#args(1, 2)
5-
length(0.5, 0.7)
5+
#length(0.5, 0.7)
66
#length_squared(0.5, 0.7)
77
print(length(0.5, 0.7))
88
pass

script/src/bin.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ pub fn main() -> Result<(), io::Error> {
1010
match fs::read_to_string(file) {
1111
Ok(source) => match ballscript::parse(&source) {
1212
Ok(script) => {
13-
let mut script = script.instance();
14-
dbg!(&script);
13+
let script = script.instance();
14+
dbg!(std::mem::size_of::<ballscript::Variant>());
1515
match script.call("main", &[]) {
1616
Ok(_) => Ok(()),
1717
Err(e) => todo!("{:?}", e),

script/src/bytecode.rs

+72-31
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1+
use unwrap_none::UnwrapNone;
12
use crate::ast::{Atom, Expression, Function, Lines, Statement};
23
use crate::script::CallError;
34
use crate::tokenizer::Op;
4-
use crate::{ScriptIter, ScriptType};
5+
use crate::{ScriptIter, ScriptType, ScriptObject, Variant};
56
use core::convert::TryInto;
67
use core::fmt::{self, Debug, Formatter};
78
use rustc_hash::FxHashMap;
@@ -18,14 +19,15 @@ pub(crate) enum Instruction {
1819
Call(Box<(u16, CallArgs)>),
1920
CallSelf(Box<CallArgs>),
2021
CallGlobal(Box<CallArgs>),
21-
//Iter(Box<dyn Iterator<Item = Rc<dyn ScriptType>>>),
22+
//Iter(Box<dyn Iterator<Item = ScriptObject>>),
2223
//Jmp(u32),
2324
//JmpIf(u16, u32),
2425
RetSome,
2526
RetNone,
2627

2728
IterConst(Box<(u16, u32, Box<dyn ScriptIter>)>),
2829
IterJmp(u16, u32),
30+
IterInt(u16, isize),
2931

3032
/*
3133
AndJmp(u16, u16, u32),
@@ -61,7 +63,7 @@ pub(crate) struct ByteCode {
6163
code: Vec<Instruction>,
6264
param_count: u16,
6365
var_count: u16,
64-
consts: Vec<Rc<dyn ScriptType>>,
66+
consts: Vec<Variant>,
6567
}
6668

6769
#[derive(Debug)]
@@ -73,14 +75,15 @@ pub enum RunError {
7375
UndefinedFunction,
7476
CallError(Box<CallError>),
7577
IncorrectArgumentCount,
78+
IncompatibleType,
7679
}
7780

7881
pub struct Environment {
7982
functions: FxHashMap<Box<str>, EnvironmentFunction>,
8083
}
8184

82-
pub type EnvironmentFunction = Box<dyn Fn(&[Rc<dyn ScriptType>]) -> CallResult<RunError>>;
83-
pub type CallResult<E> = Result<Rc<dyn ScriptType>, E>;
85+
pub type EnvironmentFunction = Box<dyn Fn(&[Variant]) -> CallResult<RunError>>;
86+
pub type CallResult<E> = Result<Variant, E>;
8487

8588
#[derive(Debug)]
8689
pub enum EnvironmentError {
@@ -141,7 +144,7 @@ impl ByteCode {
141144
conv(a);
142145
conv(b);
143146
}
144-
IterConst(_) | IterJmp(_, _) | RetSome | RetNone => (),
147+
IterConst(_) | IterInt(_, _) | IterJmp(_, _) | RetSome | RetNone => (),
145148
}
146149
}
147150
}
@@ -160,7 +163,7 @@ impl ByteCode {
160163
locals: &FxHashMap<Box<str>, u16>,
161164
instr: &mut Vec<Instruction>,
162165
vars: &mut FxHashMap<&'a str, u16>,
163-
consts: &mut Vec<Rc<dyn ScriptType>>,
166+
consts: &mut Vec<Variant>,
164167
curr_var_count: &mut u16,
165168
mut min_var_count: u16,
166169
) -> Result<u16, ByteCodeError> {
@@ -182,10 +185,10 @@ impl ByteCode {
182185
args.push(match a {
183186
Expression::Atom(a) => match a {
184187
Atom::String(a) => {
185-
add_const(Rc::new(a.to_string().into_boxed_str()))
188+
add_const(Variant::Object(Rc::new(a.to_string().into_boxed_str())))
186189
}
187-
Atom::Integer(a) => add_const(Rc::new(*a)),
188-
Atom::Real(a) => add_const(Rc::new(*a)),
190+
Atom::Integer(a) => add_const(Variant::Integer(*a)),
191+
Atom::Real(a) => add_const(Variant::Real(*a)),
189192
Atom::Name(a) => todo!("call {:?}", a),
190193
},
191194
Expression::Function {
@@ -201,11 +204,11 @@ impl ByteCode {
201204
match a {
202205
Expression::Atom(a) => {
203206
args.push(match a {
204-
Atom::String(a) => add_const(Rc::new(
207+
Atom::String(a) => add_const(Variant::Object(Rc::new(
205208
a.to_string().into_boxed_str(),
206-
)),
207-
Atom::Integer(a) => add_const(Rc::new(*a)),
208-
Atom::Real(a) => add_const(Rc::new(*a)),
209+
))),
210+
Atom::Integer(a) => add_const(Variant::Integer(*a)),
211+
Atom::Real(a) => add_const(Variant::Real(*a)),
209212
Atom::Name(a) => todo!("call {:?}", a),
210213
});
211214
}
@@ -250,9 +253,7 @@ impl ByteCode {
250253
u32::MAX,
251254
Box::new(a.to_string().into_boxed_str()),
252255
))),
253-
Atom::Integer(a) => {
254-
Instruction::IterConst(Box::new((reg, u32::MAX, Box::new(*a))))
255-
}
256+
Atom::Integer(a) => Instruction::IterInt(reg, *a),
256257
//Atom::Real(a) => Instruction::IterConst(Box::new(a)),
257258
Atom::Real(a) => todo!("for Real({})", a),
258259
Atom::Name(a) => todo!("for {:?}", a),
@@ -274,10 +275,10 @@ impl ByteCode {
274275
)?;
275276
instr.push(Instruction::IterJmp(reg, ip));
276277
let ip = instr.len() as u32;
277-
if let Some(Instruction::IterConst(ic)) = instr.get_mut(ic) {
278-
ic.1 = ip;
279-
} else {
280-
unreachable!();
278+
match instr.get_mut(ic) {
279+
Some(Instruction::IterConst(ic)) => ic.1 = ip,
280+
Some(Instruction::IterInt(..)) => (),
281+
_ => unreachable!(),
281282
}
282283
}
283284
Statement::Return { expr } => {
@@ -429,8 +430,8 @@ impl ByteCode {
429430
pub(crate) fn run(
430431
&self,
431432
functions: &FxHashMap<Box<str>, Self>,
432-
locals: &mut [Rc<dyn ScriptType>],
433-
args: &[Rc<dyn ScriptType>],
433+
locals: &mut [Variant],
434+
args: &[Variant],
434435
env: &Environment,
435436
) -> CallResult<RunError> {
436437
if args.len() != self.param_count as usize {
@@ -440,9 +441,7 @@ impl ByteCode {
440441
for a in args.iter() {
441442
vars.push(a.clone());
442443
}
443-
vars.resize_with(self.var_count as usize, || {
444-
Rc::new(()) as Rc<dyn ScriptType>
445-
});
444+
vars.resize(self.var_count as usize, Variant::default());
446445
vars.extend(self.consts.iter().cloned());
447446
let mut ip = 0;
448447
let mut iterators = Vec::new();
@@ -467,7 +466,7 @@ impl ByteCode {
467466
for &a in args.iter() {
468467
call_args.push(vars.get(a as usize).ok_or(err_roob())?.clone());
469468
}
470-
let obj = vars.get(*reg as usize).ok_or(err_roob())?.as_ref();
469+
let obj = vars.get(*reg as usize).ok_or(err_roob())?;
471470
let r = obj.call(func, &call_args[..]).map_err(err_call)?;
472471
call_args.clear();
473472
if let Some(reg) = store_in {
@@ -505,7 +504,7 @@ impl ByteCode {
505504
}
506505
}
507506
RetSome => break Ok(vars.first().ok_or(err_roob())?.clone()),
508-
RetNone => break Ok(Rc::new(())),
507+
RetNone => break Ok(Variant::None),
509508
IterConst(box (reg, jmp_ip, iter)) => {
510509
let mut iter = iter.iter();
511510
if let Some(e) = iter.next() {
@@ -515,6 +514,15 @@ impl ByteCode {
515514
ip = *jmp_ip;
516515
}
517516
}
517+
IterInt(reg, i) => {
518+
let mut iter = if *i < 0 {
519+
Box::new(((1 - i)..=0).rev().map(Variant::Integer)) as Box<dyn Iterator<Item = Variant>>
520+
} else {
521+
Box::new((0..*i).map(Variant::Integer)) as Box<dyn Iterator<Item = Variant>>
522+
};
523+
*vars.get_mut(*reg as usize).ok_or(err_roob())? = iter.next().unwrap();
524+
iterators.push(iter);
525+
}
518526
IterJmp(reg, jmp_ip) => {
519527
if let Some(iter) = iterators.last_mut() {
520528
if let Some(e) = iter.next() {
@@ -528,13 +536,45 @@ impl ByteCode {
528536
Mul(r, a, b) => {
529537
let a = vars.get(*a as usize).ok_or(err_roob())?;
530538
let b = vars.get(*b as usize).ok_or(err_roob())?;
531-
let e = a.mul(b).map_err(err_call)?;
539+
let err = || Err(RunError::IncompatibleType);
540+
let e = match a {
541+
Variant::None => return err(),
542+
Variant::Real(a) => match b {
543+
Variant::None => return err(),
544+
Variant::Real(b) => Variant::Real(a * b),
545+
&Variant::Integer(b) => Variant::Real(a * b as f64),
546+
Variant::Object(_) => return err(),
547+
}
548+
&Variant::Integer(a) => match b {
549+
Variant::None => return err(),
550+
Variant::Real(b) => Variant::Real(a as f64 * b),
551+
Variant::Integer(b) => Variant::Integer(a * b),
552+
Variant::Object(_) => return err(),
553+
}
554+
Variant::Object(_) => return err(),
555+
};
532556
*vars.get_mut(*r as usize).ok_or(err_roob())? = e;
533557
}
534558
Add(r, a, b) => {
535559
let a = vars.get(*a as usize).ok_or(err_roob())?;
536560
let b = vars.get(*b as usize).ok_or(err_roob())?;
537-
let e = a.add(b).map_err(err_call)?;
561+
let err = || Err(RunError::IncompatibleType);
562+
let e = match a {
563+
Variant::None => return err(),
564+
Variant::Real(a) => match b {
565+
Variant::None => return err(),
566+
Variant::Real(b) => Variant::Real(a + b),
567+
&Variant::Integer(b) => Variant::Real(a + b as f64),
568+
Variant::Object(_) => return err(),
569+
}
570+
&Variant::Integer(a) => match b {
571+
Variant::None => return err(),
572+
Variant::Real(b) => Variant::Real(a as f64 + b),
573+
Variant::Integer(b) => Variant::Integer(a + b),
574+
Variant::Object(_) => return err(),
575+
}
576+
Variant::Object(_) => return err(),
577+
};
538578
*vars.get_mut(*r as usize).ok_or(err_roob())? = e;
539579
}
540580
_ => todo!("{:?}", instr),
@@ -567,7 +607,7 @@ impl Environment {
567607
}
568608
}
569609

570-
pub fn call(&self, func: &str, args: &[Rc<dyn ScriptType>]) -> CallResult<EnvironmentError> {
610+
pub fn call(&self, func: &str, args: &[Variant]) -> CallResult<EnvironmentError> {
571611
Ok(self
572612
.functions
573613
.get(func)
@@ -588,6 +628,7 @@ impl Debug for Instruction {
588628
RetSome => write!(f, "ret 0"),
589629
RetNone => write!(f, "ret none"),
590630
IterConst(box (r, p, i)) => write!(f, "iter {}, {}, {:?}", r, p, i),
631+
IterInt(r, i) => write!(f, "iter {}, {}", r, i),
591632
IterJmp(r, p) => write!(f, "iterjmp {}, {}", r, p),
592633
Add(r, a, b) => write!(f, "add {}, {}, {}", r, a, b),
593634
Mul(r, a, b) => write!(f, "mul {}, {}, {}", r, a, b),

script/src/lib.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
1-
#![feature(option_expect_none)]
2-
#![feature(option_unwrap_none)]
31
#![feature(box_patterns)]
42

3+
use unwrap_none::UnwrapNone;
4+
55
mod ast;
66
mod bytecode;
77
mod script;
88
mod tokenizer;
99

1010
use rustc_hash::FxHashMap;
1111
use script::Script;
12-
pub use script::{Class, ScriptIter, ScriptType};
12+
pub use script::{Class, ScriptIter, ScriptType, ScriptObject, Variant};
1313

1414
use bytecode::ByteCode;
1515
use tokenizer::TokenStream;

0 commit comments

Comments
 (0)