Giter Site home page Giter Site logo

pecan's Introduction

pecan's People

Contributors

aurel300 avatar

Stargazers

Flainn avatar William Fernandes avatar Dmitriy Pyalov avatar Ranhao Kang avatar  avatar  avatar Elliott Stoneham avatar Monad avatar David Bruce avatar  avatar Nikita avatar Wartman avatar paling avatar francesco agati avatar Viachaslau Tratsiak avatar Jérémy Faivre avatar Sam Morrison avatar Jordon Replogle avatar Datee avatar Constantine Teplyakov avatar  avatar Uldis Baurovskis avatar  avatar Kevin Leung avatar Chris Anderson avatar Joey avatar Michael Schmalle avatar Peter Achberger avatar David Klein avatar Dmytro Hryppa avatar Alex avatar Joohun, Maeng avatar  avatar Jean-Denis Boivin avatar Dan Korostelev avatar Mr zhan avatar Zaphod avatar Rudy Ges avatar Benjamin Stern avatar  avatar Anders Nissen avatar sinh-png avatar Jonas Nyström avatar

Watchers

Dan Korostelev avatar francesco agati avatar  avatar Constantine Teplyakov avatar Mr zhan avatar Zaphod avatar  avatar

pecan's Issues

Flatten states

Currently, the "instruction pointer" is an Array<Int>, indicating the nested action index within arrays of CoAction. It would be better to perform code flow analysis to turn all CoActions into a single array/Vector with the action + the index of the next action (conditionals and loops would have two indices for branching). This should simplify the implementation of the coroutine runner, simplify CoAction, etc.

Optimise CFA

  • A -> B can be merged IF:
    • A is Sync
    • there are no jumps to B
  • rearrange actions so that 0 is always the entry point, remove entryPosition variables
  • remove unused actions (dead code)

No-API mode

Add a way for macros to use pecan without the default API (self, suspend, terminate, etc). Could be an extra argument on CoContext.build.

Method declarations

It should be possible to declare coroutines as class methods:

class Foo {
  @:pecan.co static function bar(x:String) {
    trace("hello");
    suspend();
    trace(x);
  }

  public static function main():Void {
    var c = bar.run("world");
    c.tick(); // hello
    c.wakeup(); // world
  }
}

The return type of the function can be used as the type for yields. The accept type must be declared as an argument to the :pecan.co metadata (since the function arguments are arguments to the run call).

Arguments

Allow a more function-like syntax for coroutine declarations:

var c = co((foo, bar) -> {
  trace('called with arguments $foo and $bar');
});
c.run("foo", "bar").tick();

For type-safety, a different type should be defined for each coroutine factory, with an appropriate run method.

`accept` in any expression

Allow an accept call in any expression, unfold as necessary:

var foo = accept() + accept();

Transform into:

Accept((self, value) -> self.vars._tmp1 = value),
Accept((self, value) -> self.vars._tmp2 = value),
Sync((self) -> self.vars.foo = self.vars._tmp1 + self.vars_tmp2)

Labels

co({
  label("start");
  yield("starting...");
  label("repeat");
  yield("repeating...");
  goto("repeat");
}, null, (_:String));

Looks much more awkward than a regular loop, but a coroutine can be moved to an arbitrary label externally. Would be useful to manage e.g. the state machines of enemy AI. Could also make it more practical to hot-plug coroutines or have coroutine "interfaces" based on labels.

#3 should be implemented first, so a label is indexed with a single integer.

Minor error in manual Getting started page

Spotted a minor error - nothing that anyone trying this out wouldn't figure out, but anyway...

var factory = pecan.Co.co({   // (1)
  var greeted = 0;
  trace("Greeter online!");
  suspend();                  // (2)
  while (true) {              // (3)
    var name = accept();      // (4)
    if (name == null)
      break;
    trace('Hello, $name!');
    yield(++greeted);         // (5)
  }
  trace("Bye!");
}, (_ : String), (_ : Int));  // (6)

var instance = co.run();      // (7)    <----  should be factory.run()

Better type inference

Type annotations are currently required for I/O and returns. This is because the type of the coroutine instance must be available while typing its body. This should be avoidable with a better choice of arguments in CoContext.typeExpr (since access to these is replaced anyway).

state is not correctly switched when @:pecan.accept returns synchronously

@:pecan.accept function sync(?ret:pecan.Void->Void, ?co:pecan.ICo<Any, Any, Any>):pecan.Void {
	ret(Void);
	return null;
}

@:pecan.accept function async(?ret:pecan.Void->Void, ?co:pecan.ICo<Any, Any, Any>):pecan.Void {
	haxe.Timer.delay(() -> ret(Void), 10);
	return null;
}

function main() {
	pecan.Co.co({
		sync();
		trace("hallo");
		async();
	}).run();
}

Output:

src/Main.hx:14: hallo
src/Main.hx:14: hallo

Magic keywords

yield, accept, etc. are currently magical calls that will always shadow variables or functions with the same name. It might be better to instead make these static functions of pecan.Co, then resolve them similarly to #5.

Array and map comprehension with suspending blocks

Allow array and map comprehension of suspending loops:

var foo = [ for (i in 0...3) accept() ];

Transform into:

var foo = [];
for (i in 0...3) foo.push(accept());

Ideally #1 should be implemented first so foo.push(accept()); is not a special case.

User-defined syntax for suspending functions

Macro-in-macro forbids the following syntax:

class MyCo extends pecan.Co {
  @:co.suspend function wait(delay:Int, wakeup:()->Void):Void {
    haxe.Timer.delay(delay, wakeup);
  }
}

There should still be a convenient way to declare wrappers over co which define their own suspending functions:

// (in macro context)
class MyCo {
  public static function co(block:Expr, tin:Expr, tout:Expr):Expr {
    return pecan.Co.wrap(macro class MyCoFunctions {
      @:co.suspend function wait(delay:Int, wakeup:()->Void):Void {
        haxe.Timer.delay(delay, wakeup);
      }
    }, block, tin, tout);
  }
}

Then used with:

MyCo.co({
  trace("a");
  wait(200);
  trace("b");
});

De/serialisation

It should be possible to automatically generate the Haxe de/serialisation methods for a coroutine. It should be enough to store:

  • the state (CFG position + flags)
  • any state-crossing locals (which are already detected anyway)

As an optimisation, only the state-crossing locals which are actually read by reachable states could be stored.

Serialisation of a coroutine that depends on a closure-captured variable should probably be forbidden as well.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.