Move fiber functions to module Fiber (#113)

This commit is contained in:
Tiago Cavalcante Trindade 2021-06-23 01:18:15 -03:00 committed by GitHub
parent fe45ba3485
commit 792d08eae7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 128 additions and 125 deletions

View File

@ -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.

View File

@ -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)
```

View File

@ -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)
```

View File

@ -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;

View File

@ -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')