nekovm

2014年05月21日 10:10GMT+8

一些示例:

命令行

neko [options] file.n

# options必须要放在 file 前边
options: -interp  关闭 jit再执行
         -stats   执行状态(性能)
		 -version 显示当前 neko 版本退出

黑魔法

http://old.haxe.org/doc/advanced/magic#neko-magic

package neko

这里只是描述几个类, 在 cpp 包中都有相对应的类(只少数几个没有)

  • vm.Ui UI线程同步队列, 似乎没什么作用???

neko.Lib


// 从NDLL库中加载 Neko primitive 方法
public static function load( lib : String, prim : String, nargs : Int ) : Dynamic;

// 同上, 只是如果加载出错将返回一个空的方法而不抛出异常。
public static function loadLazy(lib,prim,nargs) : Dynamic

// 重新抛出异常
public static function rethrow( e : Dynamic ) : Dynamic;

// 使用 Neko 源生的序列化方法, 序列化方法在速度上有优化而不是数据大小.
public static function serialize( v : Dynamic ) : haxe.io.Bytes;

// 使用 Neko 源生的**反**序列化方法.
public static function unserialize( s : haxe.io.Bytes ) : Dynamic;

// 使用 Neko 源生的**反**序列化方法. 这个方法假设所有的序列化数据都由当前模块序列化,
// 即使模块名不同. 这可能发生在如果你反序列化一些数据到mod_neko, 而
// 那些数据的序列化是在不同的服务器上使用了不同的文件路径
public static function localUnserialize( s : haxe.io.Bytes ) : Dynamic;

// 转换 neko 值为等效的 haxe 值. 用于将wrapping的 String,Arrays 的raw值型式转换成haxe对象型式
public static function nekoToHaxe( v : Dynamic ) : Dynamic;

// 转换 haxe 值为等效的 neko 值. 用于将unwrap的 String 和 Arrays 的对象型式转换成neko的raw值型式
public static function haxeToNeko( v : Dynamic ) : Dynamic;

//返回一个对象包含所有编译了(import导入了或者其它引入方式)的packages和classes,
//例: Lib.getClasses().Main, 返回 Main.hx 类(Class<Main>)
//例: Reflect.fields(Lib.getClasses().pkg), [@Some,Some], 如果是包则带有 @的是成员,而另一个则是静态.
//当你需要初使化一堆 SPOD 表时可能会用到这个方法, 而不需要一个一个的指定
public static function getClasses() : Dynamic;

// 返回字符串引用提供的参数 byte, 因此如果修改了 byte, 则字符串的值也将会同时改变.
public inline static function stringReference( b : haxe.io.Bytes ):String;

// 参考 stringReference, 只是反过来了.
public inline static function bytesReference( s : String ) : haxe.io.Bytes;

vm.Thread

多线程示例: http://old.haxe.org/doc/neko/threads , 包含了 Deque, Mutex ;

  • sendMessage( msg : Dynamic ):Void 注意简单数据类型传拷贝,复杂类型传引用. 你可以直接传递线程

    var t1 = Thread.create(fn);
    t1.sendMessage(Thread.current());
    
    function fn(){
    	var main:Thread = Thread.readMessage(true);
    	//......
    }
    
  • readMessage(block = true):Dynamic 读取 sendMessage 传递的变量

vm.Deque

重要: pop(block) 当 block 为 true, 将为阻塞模式, 也就是说调用 pop(true) 时, 当没有元素可用于pop( 类似于调用空数组的pop方法 ), 将阻塞当前进程直至有元素可以弹出. 用于多线程编程.

var q = new Deque<Int>();
Thread.create(function() { trace(q.pop(true)); });
Thread.create(function() { trace(q.pop(true)); });
q.add(1);
q.add(2);
Sys.sleep(1)

vm.Mutex

http://old.haxe.org/api/neko/vm/mutex, 示例可以参考上边 Thread 处的示例.

用于阻塞个多个线程同时操作相同一个变量

vm.Lock

wait(?sec)用于阻塞进程.直到已经到了指定时间或者其它进程调用了 release(). 示例显示了 wait 的时间已过而退出 阻塞,

var lk = new Lock();
Thread.create(function() {
	Sys.sleep(2);
	trace("sub thread call release!");
	lk.release();
});
lk.wait(1);
trace(lk);
Sys.sleep(3);

vm.Tls

作为 thread 的局部变量, 为解决多线程程序的并发问题提供了一种新的思路, 示例见 std/haxe/io/FPHelper.hx

声明静态绑定线程的本地对象和变量时必须遵守下列原则:原文见 https://msdn.microsoft.com/zh-cn/library/6yh4a9k1.aspx

  • 只能应用于 数据声明和定义, 不能用于函数声明或定义

  • 只能用在具有 static 作用域的数据项上(haxe 的 static 只能作用于类字段)

net.Poll

在单个线程中轮询多个 非阻塞 socket, http://old.haxe.org/api/neko/net/poll

class Poll {
	var readIndexes: ArrayAccess<Int>;	// 如果指定的索引不存在则会返回 -1
	var writeIndexes: ArrayAccess<Int>;

	function new( n : Int );
	function events( ?t: Float ):Void;
	// 预置socket数组用于轮询,
	function prepare( read: Array<Socket>, write: Array<Socket> ):Void;

	// 轮询指定的socket数组并返回其中有效的, 这个类似于 Socket.select, 但没有 Socket.select 那样的连接数量限制
	// 这个方法等于 prepare(a, []) + events(t), 也不需要关心 readIndexes 和 writeIndexes的值
	function poll( a: Array<Socket>, ?t: Float ): Array<Socket>;
}
// example: copy from tora/Tora.hx
var changed = false;
var poll = new neko.net.Poll(4096);
while(true){
	....
	if( changed ) {
		poll.prepare(socks,[]);
		changed = false;
	}
	// check if some clients sent a message or have been disconnected
	poll.events(0.05);
	while(true){
		var idx = poll.readIndexes[i++];
		if( idx == -1 ) break;
		socks[idx].....
	}
}

vm.Loader

Loader(加载器) 可以用于动态(运行时)地从 NDLL 库中加载 primitives(调用代码的胶水函数).

Loader 可以用于动态地加载其它 neko 模块(.n文件), 默认的 Loader 首先在其 cache(inst.getCache) 中查找, 然后在路径(inst.getPath)和当前路径查找指定的名称,从路径中查找的模块会被立刻执行

Loader 可用于沙箱安全。当模块已经加载到指定的 loader, 这个 loader可以管理被加载的模块安全通过过滤哪些 primitives 可以被加载

通常通过 Loader.local() 获得当前 neko Loader(加载器), 然后 loader.getCache() 取得这个 loader 下的所有模块, 例如: app.n => [Module Object]

loader.loadModule, 如果 cache 中不在,则将在路径中搜索指定模块并在成功加载之后立刻执行.

loader.primitive 是用来加载 C 代码端生成的胶水函数的, 而非 haxe 生成的.

vm.Module

Neko 虚拟机的可执行文件, 一个 Module 对应一个 .n 文件

静态方法:

  • static local():Module: 返回当前Module实例

  • static read(i:Input, l:Loader):Module: 从 Input 中读取 Module 到指定的 Loader, Module 将被初使化但还没有执行

  • static readBytes(i:Byte, l:Loader):Module: 同上,初使化的模块还没有执行

  • static readPath(name:String, path:Array<String>, l:Loder):Module: 在指定的路径(path)中加载指定模块(name),初使化的模块还没有执行。

  • static readGlobalsNames(i:Input):Array<String>:

一些成员方法: 这些方法不需要过多的关注,

  • execute():Dynamic: 一个模块可以被执行多次, 每次都会初使化。 注意: 这个方法 并非 返回 Sys.exit(i) 的值, 因为被加载的模块如果调用了 Sys.exit 则所有的模块将退出, 即不会再执行后边的任何代码.

    • 对于 haxe 编译的 neko 来说, 将返回 main 函数的返回值.
  • getExports():Map<String,Dynamic> , exportsTable():Dynamic 二个方法, 前者通过 Reflect 将后者的返回用 Map 进行了包装。除了__module字段, 其它所有字段需要在 执行后 才有值

    • __module: TODOS(未知的抽像类型)

    • __classes: 包含所有haxe类,可以直接引用这些值或调用这些函数, 通过这个字段调用其中的方法,比如 main, 除非再次运行 execute(), 否则值将被保留,d而不会初使化

    • __unserialize ??? 对应为一个函数,似乎不用理会

  • globalsCount():Int, setGlobal(n:Int, v:Dynamic):Void, getGlobal(n:Int):Dynamic: int 值分别对应组成neko Module的每个元素(字符串, 函数……等等) 似乎和 neko 的数据结构有关.

std.ndll

通过 neko.Lib.load("std",@name,@args) 加载的方法, 源码在: https://github.com/HaxeFoundation/neko/tree/master/libs/std ,

  • print_redirect(print:Dynamic->Void):Void: 重定向 print 的输出

  • set_trusted : bool -> void : 优化一些操作,


nekovm

从这里开始的内容并非是 haxe 代码。http://nekovm.org/doc

语法

neko 语言的语法被设计成易于解析与生成,并不适用于直接编程,而是要从更高层的语言生成。 例如, php-to-neko 或 java-to-neko 。 这里有一个 nekoml 的编译器, 但 ml 的语法实在是不友好。

随记:

  • _ 表示一个空的表达式

  • while 循环外, 不允许有 continuebreak

  • 算术运算有下列优先级(从下到上)

    • 赋值
    • ++=--=
    • &&||
    • 比较运算符
    • +-
    • */
    • |,&,^
    • <<,>>,>>>%

neko 中的值可以是下列之一:

  • Null: 特殊值 null 用于未初使化的变量
  • 整数, 常见的十进制形式(123或-12)或十六进制形式(0x1A2B)
  • 浮点数,
  • 布尔值: 由二个小写的 truefalse
  • 字符串: 双引号包车的字符串(例: “foo”, “hello,\nworld!”)。neko 字符串是可变的,意味着可以修改它们。
  • 数组: 索引从 0 开始的整数索引表。
  • Object: 一个表,将标识符或字符串与值相关联。 例如: {i1 => v1, i2 => v2}
  • 函数: 在 neko 中函数也是值,
  • Abstract: 不能从 neko 访问的 C 数据

一些注意事项:

  • 由于虚拟机性能方面的原因整数为31位,An API for full 32-bit integers is available through a standard library.

  • 浮点数为64位双精度

  • 字符串为8位的字节序列,可以包含 \0字符。 字符串的长度必须明确而不是 \0 前的字符串长。

流程

  • if 与 while 的条件表达式只接受布尔值, 因此会经常用到 $istrue

    • $istrue(null) 为 false, $istrue(0) 也为 false, 其它都为 true。

    • $not$istrue 的反函数。 $not(0) 将返回 true.

  • 需要注意的是 while 中的 break 语句, 由于任何表达式都是值, 因此 break value,可以作为 while 的返回值。

  • var 声明的局部变量与未加 var 声明的全局变量被局部引用时有不一样的结果.

  • 二个整数相除将返回浮点数, 可以使用 $idiv 作整数除法运算

  • 和 JS 类似, &&|| 可以被短路. 这意味着如果 第一个操作数为 true, 那么不会计算第二个表达式. 例: "hello" && "world" 将只返回 “hello”

  • $nargs 返回一个函数的参数个数, 如果 -1,则表示为可变参数

  • $call 类似于 JS中的 apply, 例: $call(func, context, $array(3,4))

  • $closure, 有点类似于 haxe 中的 function.bind, 但是又绑定了上下文

    var add = function(x,y) { return x + y };
    var plus5 = $closure(add,null,5); // null context and 5 as first argument
    $print( plus5(2) ); // 7
    
    // 处理 this
    var f = function() { $print(this) };
    f = $closure(f,55);
    f(); // prints 55
    
  • $apply, 可以直接调用函数, 如果函数需要额外的参数将推迟到获得更多参数.

    var f = function(x,y) { return x + y };
    f(1,2);
    $apply(f,1)(2); // equivalent
    $apply(f,1,2); // equivalent
    

    …..

Neko Libraries

http://nekovm.org/doc/libs

对于 Builtins 中的方法可以直接调用, 而 Std 中的库需要导出如:

var date_now = $loader.loadprim("std@date_now",0);
$print(date_now())

对于其它库如 regexp.ndll 则:

var regexp_new = $loader.loadprim("regexp@regexp_new",1);

CFFI



nekoml

nekoml.exe 用于编译 nml 代码

NekoML Compiler v2.0.0 - (c)2005-2012 Motion-Twin
 Usage : nekoml [options] files...
 Options :
  -p <path> : additional file search path
  -v : verbose mode
  -n : generate intermediate .neko files  # 只输出 .neko 代码
  -pack <file> : build module packages    # ???
  -use <file> : use this module package   # ???
  -nostd : disable std lib

nekoc.exe 用于连接各模块(nekoml编译出来的文件会有多个 .n 文件)

Neko Compiler v2.0.0 - (c)2005-2012 Motion-Twin
 Usage : neko [options] files...
 Options :
  -d <file> : dump bytecode(将 .n 文件解压为 .dump 文件(JIT汇编码???)-z <file> : make bytecode release(???移除一些调试信息?)
  -p <file> : parse and print neko source
  -doc <file> : make documentation
  -o <dir> : set output directory
  -console : run the console(进入到一个奇怪的控制台, 不知用来做什么)
  -link <file> : link bytecodes files(注意第一个文件为输出文件, 第二个为主入口文件就可以了)
  -v : verbose mode
  -version : set the bytecode version

基础语法 http://nekovm.org/doc/nekoml

库及API https://github.com/HaxeFoundation/neko/tree/master/src/core

一些区别

  • 和 ocaml 一样除了数组内的值可变或声明为 ref, var 的值都不可变
  • 没有 return 和 ocaml 一样
  • 参数或返回值可以使用 i:int 这样的语法来显示明确类型
  • 和 ocaml 一样, int 的值无法自动隐式地转换成 float.
  • match 表达式中的 [< NN; NNN >] 为 stream 类型, 通常会在 Parse 文件的地方看到这种表达式
  • 运算符, 不支持三元运算符
  • 变量或方法名不可以用大写字母开头
  • 正则如果需要 options 需要自已从 neko(“”) 中调用
  • 变量可以通过 var 重新定义, 但普通变量不能更改其值

    ===/!==/==/!=    #
    ||/&&            # 条件, 左右边必须为 bool
    or/and/xor       # 整数位运算,好像没有按位取反(not) ??
    >>/<</>>>        # 位移
    
var list = [1; 2; 3; 4; 5]; //
var wl = &[1; 2; 3; 4; 5];  // ref list

100::list;
100::*wl;            // 但是这样 wl 的值并没有改变
wl := 100 :: *wl;
//list = 100 :: list // 错误, 感觉这蛮怪异的.和 ocaml 比起来
// 在底层 &(ref) 相当于把这个值放到 [] 中去, 例: var s = &"abc" => $array("abc")

// 对比二个是否为同一个对象
print([1] == [1])  // (int list)true
print([1] === [1]) // false

// 注意这种多变量赋值为形式为 Tuple
var a:int, b:int, c:bool = (1, 2, false);