mirror of
https://github.com/zekexiao/pocketlang.git
synced 2025-02-05 20:26:53 +08:00
Move fiber functions to module Fiber (#113)
This commit is contained in:
parent
fe45ba3485
commit
792d08eae7
@ -1,7 +1,6 @@
|
||||
|
||||
// In good first issue
|
||||
- Refactor fiber into a module and is_done as an attribute.
|
||||
- floats like ".5".
|
||||
- Refactor is_done as an attribute.
|
||||
- Implement argparse.
|
||||
|
||||
// To implement.
|
||||
|
@ -78,12 +78,14 @@ str_lower(str_strip('Foo ')) # This can be written as below
|
||||
# Fibers & Coroutine
|
||||
#-------------------
|
||||
|
||||
import Fiber
|
||||
|
||||
def fn(p1, p2)
|
||||
print(yield(42)) # Prints 3.14
|
||||
end
|
||||
|
||||
fb = fiber_new(fn)
|
||||
val = fiber_run(fb, 1, 2)
|
||||
fb = Fiber.new(fn)
|
||||
val = Fiber.run(fb, 1, 2)
|
||||
print(val) ## Prints 42
|
||||
fiber_resume(fb, 3.14)
|
||||
Fiber.resume(fb, 3.14)
|
||||
```
|
||||
|
@ -8,12 +8,14 @@ instruction pointer) for that function, which can be run and once yielded
|
||||
resumed.
|
||||
|
||||
```ruby
|
||||
import Fiber
|
||||
|
||||
def fn(h, w)
|
||||
print(h, w)
|
||||
end
|
||||
|
||||
fb = fiber_new(fn) # Create a fiber.
|
||||
fiber_run(fb, 'hello', 'world') # Run the fiber.
|
||||
fb = Fiber.new(fn) # Create a fiber.
|
||||
Fiber.run(fb, 'hello', 'world') # Run the fiber.
|
||||
```
|
||||
|
||||
## %% Yielding %%
|
||||
@ -24,36 +26,42 @@ from (not the caller of the function). And you can pass values between
|
||||
fibers when they yield.
|
||||
|
||||
```ruby
|
||||
import Fiber
|
||||
|
||||
def fn()
|
||||
print('running')
|
||||
yield()
|
||||
print('resumed')
|
||||
end
|
||||
|
||||
fb = fiber_new(fn)
|
||||
fiber_run(fb) # Prints 'running'.
|
||||
fb = Fiber.new(fn)
|
||||
Fiber.run(fb) # Prints 'running'.
|
||||
print('before resumed')
|
||||
fiber_resume(fb) # Prints 'resumed'.
|
||||
Fiber.resume(fb) # Prints 'resumed'.
|
||||
```
|
||||
|
||||
Yield from the fiber with a value.
|
||||
|
||||
```ruby
|
||||
import Fiber
|
||||
|
||||
def fn()
|
||||
print('running')
|
||||
yield(42) # Return 42.
|
||||
print('resumed')
|
||||
end
|
||||
|
||||
fb = fiber_new(fn)
|
||||
val = fiber_run(fb) # Prints 'running'.
|
||||
fb = Fiber.new(fn)
|
||||
val = Fiber.run(fb) # Prints 'running'.
|
||||
print(val) # Prints 42.
|
||||
fiber_resume(fb) # Prints 'resumed'.
|
||||
Fiber.resume(fb) # Prints 'resumed'.
|
||||
```
|
||||
|
||||
Resume the fiber with a value.
|
||||
|
||||
```ruby
|
||||
import Fiber
|
||||
|
||||
def fn()
|
||||
print('running')
|
||||
val = yield() # Resumed value.
|
||||
@ -61,28 +69,30 @@ def fn()
|
||||
print('resumed')
|
||||
end
|
||||
|
||||
fb = fiber_new(fn)
|
||||
val = fiber_run(fb) # Prints 'running'.
|
||||
fiber_resume(fb, 42) # Resume with 42, Prints 'resumed'.
|
||||
fb = Fiber.new(fn)
|
||||
val = Fiber.run(fb) # Prints 'running'.
|
||||
Fiber.resume(fb, 42) # Resume with 42, Prints 'resumed'.
|
||||
```
|
||||
|
||||
Once a fiber is done execution, trying to resume it will cause a runtime
|
||||
error. To check if the fiber is finished it's execution use the function
|
||||
`fiber_is_done` and use the function `fiber_get_func` to get it's function,
|
||||
error. To check if the fiber is finished check its attribute
|
||||
`is_done` and use the attribute `function` to get it's function,
|
||||
which could be used to create a new fiber to "re-start" the fiber.
|
||||
|
||||
```ruby
|
||||
fiber_run(fb = fiber_new(
|
||||
import Fiber
|
||||
|
||||
Fiber.run(fb = Fiber.new(
|
||||
func()
|
||||
for i in 0..5 do
|
||||
yield(i)
|
||||
end
|
||||
end))
|
||||
|
||||
while not fiber_is_done(fb)
|
||||
fiber_resume(fb)
|
||||
while not fb.is_done
|
||||
Fiber.resume(fb)
|
||||
end
|
||||
|
||||
# Get the function from the fiber.
|
||||
fn = fiber_get_func(fb)
|
||||
fn = Fiber.get_func(fb)
|
||||
```
|
||||
|
170
src/pk_core.c
170
src/pk_core.c
@ -663,87 +663,6 @@ DEF(coreMapRemove,
|
||||
RET(mapRemoveKey(vm, map, key));
|
||||
}
|
||||
|
||||
// Fiber functions.
|
||||
// ----------------
|
||||
|
||||
DEF(coreFiberNew,
|
||||
"fiber_new(fn:Function) -> fiber\n"
|
||||
"Create and return a new fiber from the given function [fn].") {
|
||||
|
||||
Function* fn;
|
||||
if (!validateArgFunction(vm, 1, &fn)) return;
|
||||
RET(VAR_OBJ(newFiber(vm, fn)));
|
||||
}
|
||||
|
||||
DEF(coreFiberGetFunc,
|
||||
"fiber_get_func(fb:Fiber) -> function\n"
|
||||
"Retruns the fiber's functions. Which is usefull if you want to re-run the "
|
||||
"fiber, you can get the function and crate a new fiber.") {
|
||||
|
||||
Fiber* fb;
|
||||
if (!validateArgFiber(vm, 1, &fb)) return;
|
||||
RET(VAR_OBJ(fb->func));
|
||||
}
|
||||
|
||||
DEF(coreFiberIsDone,
|
||||
"fiber_is_done(fb:Fiber) -> bool\n"
|
||||
"Returns true if the fiber [fb] is done running and can no more resumed.") {
|
||||
|
||||
Fiber* fb;
|
||||
if (!validateArgFiber(vm, 1, &fb)) return;
|
||||
RET(VAR_BOOL(fb->state == FIBER_DONE));
|
||||
}
|
||||
|
||||
DEF(coreFiberRun,
|
||||
"fiber_run(fb:Fiber, ...) -> var\n"
|
||||
"Runs the fiber's function with the provided arguments and returns it's "
|
||||
"return value or the yielded value if it's yielded.") {
|
||||
|
||||
int argc = ARGC;
|
||||
if (argc == 0) // Missing the fiber argument.
|
||||
RET_ERR(newString(vm, "Missing argument - fiber."));
|
||||
|
||||
Fiber* fb;
|
||||
if (!validateArgFiber(vm, 1, &fb)) return;
|
||||
|
||||
// Buffer of argument to call vmPrepareFiber().
|
||||
Var* args[MAX_ARGC];
|
||||
|
||||
// ARG(1) is fiber, function arguments are ARG(2), ARG(3), ... ARG(argc).
|
||||
for (int i = 1; i < argc; i++) {
|
||||
args[i - 1] = &ARG(i + 1);
|
||||
}
|
||||
|
||||
// Switch fiber and start execution.
|
||||
if (vmPrepareFiber(vm, fb, argc - 1, args)) {
|
||||
ASSERT(fb == vm->fiber, OOPS);
|
||||
fb->state = FIBER_RUNNING;
|
||||
}
|
||||
}
|
||||
|
||||
DEF(coreFiberResume,
|
||||
"fiber_resume(fb:Fiber) -> var\n"
|
||||
"Resumes a yielded function from a previous call of fiber_run() function. "
|
||||
"Return it's return value or the yielded value if it's yielded.") {
|
||||
|
||||
int argc = ARGC;
|
||||
if (argc == 0) // Missing the fiber argument.
|
||||
RET_ERR(newString(vm, "Expected at least 1 argument(s)."));
|
||||
if (argc > 2) // Can only accept 1 argument for resume.
|
||||
RET_ERR(newString(vm, "Expected at most 2 argument(s)."));
|
||||
|
||||
Fiber* fb;
|
||||
if (!validateArgFiber(vm, 1, &fb)) return;
|
||||
|
||||
Var value = (argc == 1) ? VAR_NULL : ARG(2);
|
||||
|
||||
// Switch fiber and resume execution.
|
||||
if (vmSwitchFiber(vm, fb, &value)) {
|
||||
ASSERT(fb == vm->fiber, OOPS);
|
||||
fb->state = FIBER_RUNNING;
|
||||
}
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* CORE MODULE METHODS */
|
||||
/*****************************************************************************/
|
||||
@ -822,6 +741,65 @@ static void moduleAddFunctionInternal(PKVM* vm, Script* script,
|
||||
// 'lang' library methods.
|
||||
// -----------------------
|
||||
|
||||
DEF(stdFiberNew,
|
||||
"new(fn:Function) -> fiber\n"
|
||||
"Create and return a new fiber from the given function [fn].") {
|
||||
|
||||
Function* fn;
|
||||
if (!validateArgFunction(vm, 1, &fn)) return;
|
||||
RET(VAR_OBJ(newFiber(vm, fn)));
|
||||
}
|
||||
|
||||
DEF(stdFiberRun,
|
||||
"run(fb:Fiber, ...) -> var\n"
|
||||
"Runs the fiber's function with the provided arguments and returns it's "
|
||||
"return value or the yielded value if it's yielded.") {
|
||||
|
||||
int argc = ARGC;
|
||||
if (argc == 0) // Missing the fiber argument.
|
||||
RET_ERR(newString(vm, "Missing argument - fiber."));
|
||||
|
||||
Fiber* fb;
|
||||
if (!validateArgFiber(vm, 1, &fb)) return;
|
||||
|
||||
// Buffer of argument to call vmPrepareFiber().
|
||||
Var* args[MAX_ARGC];
|
||||
|
||||
// ARG(1) is fiber, function arguments are ARG(2), ARG(3), ... ARG(argc).
|
||||
for (int i = 1; i < argc; i++) {
|
||||
args[i - 1] = &ARG(i + 1);
|
||||
}
|
||||
|
||||
// Switch fiber and start execution.
|
||||
if (vmPrepareFiber(vm, fb, argc - 1, args)) {
|
||||
ASSERT(fb == vm->fiber, OOPS);
|
||||
fb->state = FIBER_RUNNING;
|
||||
}
|
||||
}
|
||||
|
||||
DEF(stdFiberResume,
|
||||
"fiber_resume(fb:Fiber) -> var\n"
|
||||
"Resumes a yielded function from a previous call of fiber_run() function. "
|
||||
"Return it's return value or the yielded value if it's yielded.") {
|
||||
|
||||
int argc = ARGC;
|
||||
if (argc == 0) // Missing the fiber argument.
|
||||
RET_ERR(newString(vm, "Expected at least 1 argument(s)."));
|
||||
if (argc > 2) // Can only accept 1 argument for resume.
|
||||
RET_ERR(newString(vm, "Expected at most 2 argument(s)."));
|
||||
|
||||
Fiber* fb;
|
||||
if (!validateArgFiber(vm, 1, &fb)) return;
|
||||
|
||||
Var value = (argc == 1) ? VAR_NULL : ARG(2);
|
||||
|
||||
// Switch fiber and resume execution.
|
||||
if (vmSwitchFiber(vm, fb, &value)) {
|
||||
ASSERT(fb == vm->fiber, OOPS);
|
||||
fb->state = FIBER_RUNNING;
|
||||
}
|
||||
}
|
||||
|
||||
DEF(stdLangClock,
|
||||
"clock() -> num\n"
|
||||
"Returns the number of seconds since the application started") {
|
||||
@ -1038,13 +1016,6 @@ void initializeCore(PKVM* vm) {
|
||||
// Map functions.
|
||||
INITIALIZE_BUILTIN_FN("map_remove", coreMapRemove, 2);
|
||||
|
||||
// Fiber functions.
|
||||
INITIALIZE_BUILTIN_FN("fiber_new", coreFiberNew, 1);
|
||||
INITIALIZE_BUILTIN_FN("fiber_get_func", coreFiberGetFunc, 1);
|
||||
INITIALIZE_BUILTIN_FN("fiber_run", coreFiberRun, -1);
|
||||
INITIALIZE_BUILTIN_FN("fiber_is_done", coreFiberIsDone, 1);
|
||||
INITIALIZE_BUILTIN_FN("fiber_resume", coreFiberResume, -1);
|
||||
|
||||
// Core Modules /////////////////////////////////////////////////////////////
|
||||
|
||||
Script* lang = newModuleInternal(vm, "lang");
|
||||
@ -1070,6 +1041,11 @@ void initializeCore(PKVM* vm) {
|
||||
|
||||
// TODO: low priority - sinh, cosh, tanh, asin, acos, atan.
|
||||
|
||||
Script* Fiber = newModuleInternal(vm, "Fiber");
|
||||
MODULE_ADD_FN(Fiber, "new", stdFiberNew, 1);
|
||||
MODULE_ADD_FN(Fiber, "run", stdFiberRun, -1);
|
||||
MODULE_ADD_FN(Fiber, "resume", stdFiberResume, -1);
|
||||
|
||||
// Note that currently it's mutable (since it's a global variable, not
|
||||
// constant and pocketlang doesn't support constant) so the user shouldn't
|
||||
// modify the PI, like in python.
|
||||
@ -1459,8 +1435,22 @@ Var varGetAttrib(PKVM* vm, Var on, String* attrib) {
|
||||
}
|
||||
|
||||
case OBJ_FIBER:
|
||||
TODO;
|
||||
UNREACHABLE();
|
||||
{
|
||||
Fiber* fb = (Fiber*)obj;
|
||||
SWITCH_ATTRIB(attrib->data) {
|
||||
|
||||
CASE_ATTRIB("is_done", 0x789c2706):
|
||||
return VAR_BOOL(fb->state == FIBER_DONE);
|
||||
|
||||
CASE_ATTRIB("function", 0x9ed64249):
|
||||
return VAR_OBJ(fb->func);
|
||||
|
||||
CASE_DEFAULT:
|
||||
ERR_NO_ATTRIB(vm, on, attrib);
|
||||
return VAR_NULL;
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
case OBJ_CLASS:
|
||||
TODO;
|
||||
|
@ -3,19 +3,21 @@ def f0()
|
||||
yield("yield value")
|
||||
end
|
||||
|
||||
fiber = fiber_new(f0)
|
||||
yield_value = fiber_run(fiber)
|
||||
import Fiber
|
||||
|
||||
fiber = Fiber.new(f0)
|
||||
yield_value = Fiber.run(fiber)
|
||||
assert(yield_value == "yield value")
|
||||
assert(!fiber_is_done(fiber))
|
||||
assert(!fiber.is_done)
|
||||
|
||||
def f1()
|
||||
assert(yield("y") == "r")
|
||||
yield()
|
||||
end
|
||||
fiber = fiber_new(f1)
|
||||
assert(fiber_run(fiber) == "y")
|
||||
fiber_resume(fiber, "r")
|
||||
assert(!fiber_is_done(fiber))
|
||||
fiber = Fiber.new(f1)
|
||||
assert(Fiber.run(fiber) == "y")
|
||||
Fiber.resume(fiber, "r")
|
||||
assert(!fiber.is_done)
|
||||
|
||||
def f2(p1, p2, p3)
|
||||
r1 = yield(p3); assert(r1 == 'r1')
|
||||
@ -25,12 +27,12 @@ def f2(p1, p2, p3)
|
||||
return p1 + p2 * p3
|
||||
end
|
||||
|
||||
fiber = fiber_new(f2)
|
||||
p3 = fiber_run(fiber, 1, 2, 3); assert(p3 == 3)
|
||||
p2 = fiber_resume(fiber, 'r1'); assert(p2 == 2)
|
||||
p1 = fiber_resume(fiber, 'r2'); assert(p1 == 1)
|
||||
pa = fiber_resume(fiber, 'r3'); assert(pa == 7)
|
||||
assert(fiber_is_done(fiber))
|
||||
fiber = Fiber.new(f2)
|
||||
p3 = Fiber.run(fiber, 1, 2, 3); assert(p3 == 3)
|
||||
p2 = Fiber.resume(fiber, 'r1'); assert(p2 == 2)
|
||||
p1 = Fiber.resume(fiber, 'r2'); assert(p1 == 1)
|
||||
pa = Fiber.resume(fiber, 'r3'); assert(pa == 7)
|
||||
assert(fiber.is_done)
|
||||
|
||||
# If we got here, that means all test were passed.
|
||||
print('All TESTS PASSED')
|
||||
|
Loading…
Reference in New Issue
Block a user