my_basic icon indicating copy to clipboard operation
my_basic copied to clipboard

Eval a line of code without resetting the interpreter?

Open EternityForest opened this issue 5 years ago • 7 comments

So I was writing some bindings to control Arduino functions with this, and thought it would be really cool if you could do something suspending a running interpreter, evaluating a line of code, and then returning to normal execution.

This would let you interact with a running program during development, change variables, etc, and would be great for using it like the early home computers that you used through a BASIC prompt.

Is this possible with the current API?

EternityForest avatar Jul 30 '18 04:07 EternityForest

The interpreter is not designed to work as REPL, but it's possible to load and run without resetting the interpreter, eg:

int main(int argc, char* argv[]) {
	struct mb_interpreter_t* bas = 0;

	mb_init();

	mb_open(&bas);

	mb_load_string(
		bas,
		"n = n + 1\n"
		"print \"entry \", n;\n",
		false
	);
	mb_load_string(bas, "a = 22", false);
	mb_run(bas, false);
	mb_load_string(bas, "b = 7", false);
	mb_run(bas, false);
	mb_load_string(bas, "c = a / b", false);
	mb_run(bas, false);
	mb_load_string(bas, "print c;", false);
	mb_run(bas, false);

	mb_close(&bas);

	mb_dispose();

	return 0;
}

You would see each mb_run is a top-down execution with previous values reserved in the variables. Although this is not reentrant.

It's also possible to inspect variables with the mb_debug_get and mb_debug_set API, if this was the only interaction you were looking for.

paladin-t avatar Jul 30 '18 05:07 paladin-t

Oh thanks! Those 2 APIs actually do cover 90% of what I'd like to do interactively.

I started working on a fork to allow multiple parsing contexts and stacks per interpreter, so you can load multiple "threads" (probably with a python style GIL ) and do traditional reentrant REPL, but that's a fairly big project.

EternityForest avatar Jul 30 '18 06:07 EternityForest

Update: In my ESP32 fork. I've added a function mb_reset_preserve which doesn't clear the variables after you reset. I then added a function mb_open_child, which opens the child and makes it's running context point to the parent as a prev.

When you call mb_open, it adds a global function EXPORT(var) that you can use to assign the value of a variable to one with the same name in the parent interpreter's global scope.

I've also modified _clear_scope_chain to stop when it hits the root, so it doesn't mess with the parent scope.

With this I've been able to implement most of a proper REPL on the ESP32, and everything seems to work.

Are any of these changes anything you'd be interested in merging? The code is here if you want to take a look: https://github.com/EternityForest/mybasic_esp32

EternityForest avatar Jul 31 '18 06:07 EternityForest

Nice work! I appreciate your efforts, and I believe it would help others a lot.

Here's the dev roadmap:

  1. Releasing current v1.2
  2. Rewriting a new kernal as v2.0

So for the current branch, it's kinda feature-frozen. But it inspired me, I would consider adding REPL for v2.0.

I prefer to keep this open so others will find.

paladin-t avatar Jul 31 '18 07:07 paladin-t

Awesome! I'll probably be following this project for a while, I'm using it to allow remote code updates on an open source IoT platform I'm doing, and I might try to port it to a handheld game console at some point.

It's a great language! Definitely one of the easiest to embed interpreters out there, and it doesn't use too much RAM on embedded systems.

EternityForest avatar Jul 31 '18 07:07 EternityForest

Thanks! Looking forward to your sharing of your creations.

paladin-t avatar Jul 31 '18 08:07 paladin-t

it's such a shame there is no REPL :(

atheros avatar Jan 13 '20 19:01 atheros