Skip to content

Commit

Permalink
Context-based stacks (#114)
Browse files Browse the repository at this point in the history
* First fully working approach to context.stacks

There's no global stack in Thread anymore, but a stack on every context. This doesn't grow the stack in one big Vec, but splits it into they individual contacts.
Stacks are kept separate, for security and modularity.

* Move context variable $0 as Capture in Context
  • Loading branch information
phorward committed Jul 28, 2023
1 parent 09fb85d commit b8be03a
Show file tree
Hide file tree
Showing 6 changed files with 181 additions and 233 deletions.
4 changes: 2 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,15 +154,15 @@ fn repl(opts: &Opts) {

let mut thread = Thread::new(&program, readers.iter_mut().collect());
thread.debug = compiler.debug;
thread.load_stack(globals);
thread.globals = globals;

match thread.run() {
Ok(Some(value)) => println!("{}", value.repr()),
Err(error) => eprintln!("{}", error),
_ => {}
}

globals = thread.save_stack();
globals = thread.globals;
}
Err(errors) => {
for error in errors {
Expand Down
4 changes: 2 additions & 2 deletions src/value/method.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ impl Object for Method {
nargs: Option<Dict>,
) -> Result<Accept, Reject> {
// A method call injects the relating "this" object into the stack and calls the method afterwards.
context.thread.stack.insert(
context.thread.stack.len() - args,
context.stack.insert(
context.stack.len() - args,
Capture::Value(self.object.clone(), None, 0),
);

Expand Down
196 changes: 95 additions & 101 deletions src/value/parselet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ impl Parselet {
pub fn run(
&self,
thread: &mut Thread,
args: usize,
mut args: Vec<Capture>,
mut nargs: Option<Dict>,
main: bool,
depth: usize,
Expand All @@ -87,106 +87,104 @@ impl Parselet {
if self.consuming.is_some() {
let reader_start = thread.reader.tell();

// Check for a previously memoized result in memo table
// Check for a previously memoized result
// fixme: This doesn't recognize calls to the same parselet with same parameters,
// which might lead in unwanted results. This must be checked! It might become
// a problem when the Repeat<P>(min=0, max=void) generic parselet becomes available.
if let Some((reader_end, result)) = thread.memo.get(&(reader_start.offset, id)) {
thread.reader.reset(*reader_end);
return result.clone();
}
}

// If not, open a new context.
let mut context = Context::new(thread, self, args, depth);
if main {
assert!(self.signature.is_empty());
thread.globals.resize(self.locals, crate::value!(void));
}

if !main {
// Check for provided argument count bounds first
// todo: Not executed when *args-catchall is implemented
if args > self.signature.len() {
return Err(match self.signature.len() {
0 => format!(
"{}() doesn't accept any arguments ({} given)",
self.name, args
),
1 => format!(
"{}() takes exactly one argument ({} given)",
self.name, args
),
_ => format!(
"{}() expected at most {} arguments ({} given)",
self.name,
self.signature.len(),
args
),
}
.into())
.into();
let args_len = args.len();

// Check for provided argument count bounds first
// todo: Not executed when *args-catchall is implemented
if args_len > self.signature.len() {
return Err(match self.signature.len() {
0 => format!(
"{}() doesn't accept any arguments ({} given)",
self.name, args_len
),
1 => format!(
"{}() takes exactly one argument ({} given)",
self.name, args_len
),
_ => format!(
"{}() expected at most {} arguments ({} given)",
self.name,
self.signature.len(),
args_len
),
}
.into())
.into();
}

// Set remaining parameters to their defaults
for (i, arg) in (&self.signature[args..]).iter().enumerate() {
// args parameters are previously pushed onto the stack.
let var = &mut context.thread.stack[context.stack_start + args + i];

//println!("{} {:?} {:?}", i, arg, var);
if matches!(var, Capture::Empty) {
// In case the parameter is empty, try to get it from nargs...
if let Some(ref mut nargs) = nargs {
if let Some(value) = nargs.remove_str(&arg.0) {
*var = Capture::Value(value, None, 0);
continue;
}
}
// Initialize local variables
args.resize(self.locals, Capture::Empty);

// Otherwise, use default value if available.
if let Some(addr) = arg.1 {
// fixme: This might leak the immutable static value to something mutable...
*var =
Capture::Value(context.thread.program.statics[addr].clone(), None, 0);
//println!("{} receives default {:?}", arg.0, var);
// Set remaining parameters to their defaults
for (i, arg) in (&self.signature[args_len..]).iter().enumerate() {
// args parameters are previously pushed onto the stack.
let var = &mut args[args_len + i];

//println!("{} {:?} {:?}", i, arg, var);
if matches!(var, Capture::Empty) {
// In case the parameter is empty, try to get it from nargs...
if let Some(ref mut nargs) = nargs {
if let Some(value) = nargs.remove_str(&arg.0) {
*var = Capture::Value(value, None, 0);
continue;
}

return Error::new(
None,
format!("{}() expected argument '{}'", self.name, arg.0),
)
.into();
}
}

// Check for remaining nargs
// todo: Not executed when **nargs-catchall is implemented
if let Some(mut nargs) = nargs {
if let Some((name, _)) = nargs.pop() {
return Err(match nargs.len() {
0 => format!(
"{}() doesn't accept named argument '{}'",
self.name,
name.to_string()
),
n => format!(
"{}() doesn't accept named arguments ({} given)",
self.name,
n + 1
),
}
.into())
.into();
// Otherwise, use default value if available.
if let Some(addr) = arg.1 {
// fixme: This might leak the immutable static value to something mutable...
*var = Capture::Value(thread.program.statics[addr].clone(), None, 0);
//println!("{} receives default {:?}", arg.0, var);
continue;
}

return Error::new(
None,
format!("{}() expected argument '{}'", self.name, arg.0),
)
.into();
}
} else
/* main */
{
assert!(self.signature.len() == 0)
}

// Initialize locals
for i in 0..self.locals {
if let Capture::Empty = context.thread.stack[context.stack_start + i] {
context.thread.stack[context.stack_start + i] =
Capture::Value(crate::value!(void), None, 0);
// Check for remaining nargs
// todo: Not executed when **nargs-catchall is implemented
if let Some(mut nargs) = nargs {
if let Some((name, _)) = nargs.pop() {
return Err(match nargs.len() {
0 => format!(
"{}() doesn't accept named argument '{}'",
self.name,
name.to_string()
),
n => format!(
"{}() doesn't accept named arguments ({} given)",
self.name,
n + 1
),
}
.into())
.into();
}
}

// Create a new conrext
let mut context = Context::new(thread, self, depth, args);

//println!("remaining {:?}", nargs);
let reader_start = context.frame0().reader_start;

Expand Down Expand Up @@ -244,9 +242,8 @@ impl Parselet {

// Reset reader & stack
context.thread.reader.reset(reader_start);
context.thread.stack.truncate(context.stack_start); //fixme: context.frame0()?
context.stack.clear();
context
.thread
.stack
.resize(context.frame0().capture_start, Capture::Empty);
}
Expand Down Expand Up @@ -289,11 +286,6 @@ impl Parselet {
}
*/

if main {
// main parselet keeps locals on stack
context.stack_start += self.locals;
}

result
}
}
Expand Down Expand Up @@ -341,17 +333,15 @@ impl Object for ParseletRef {
nargs: Option<Dict>,
) -> Result<Accept, Reject> {
match context {
Some(context) => {
let len = args.len();
for arg in args {
//context.push(arg)?; //yeah...doesn't work...GRRR
context.thread.stack.push(Capture::Value(arg, None, 0));
}

self.0
.borrow()
.run(context.thread, len, nargs, false, context.depth + 1)
}
Some(context) => self.0.borrow().run(
context.thread,
args.into_iter()
.map(|arg| Capture::Value(arg, None, 0))
.collect(),
nargs,
false,
context.depth + 1,
),
None => panic!("{} needs a context to operate", self.repr()),
}
}
Expand All @@ -362,9 +352,13 @@ impl Object for ParseletRef {
args: usize,
nargs: Option<Dict>,
) -> Result<Accept, Reject> {
self.0
.borrow()
.run(context.thread, args, nargs, false, context.depth + 1)
self.0.borrow().run(
context.thread,
context.stack.split_off(context.stack.len() - args),
nargs,
false,
context.depth + 1,
)
}
}

Expand Down
Loading

0 comments on commit b8be03a

Please sign in to comment.