Update Files

This commit is contained in:
2025-01-22 16:18:30 +01:00
parent ed4603cf95
commit a36294b518
16718 changed files with 2960346 additions and 0 deletions

View File

@ -0,0 +1,60 @@
package webidl;
typedef Data = Array<Definition>;
typedef Position = {
var file : String;
var line : Int;
var pos : Int;
}
typedef Definition = {
var pos : Position;
var kind : DefinitionKind;
}
enum DefinitionKind {
DInterface( name : String, attrs : Array<Attrib>, fields : Array<Field> );
DImplements( type : String, interfaceName : String );
DEnum( name : String, values : Array<String> );
}
typedef Field = {
var name : String;
var kind : FieldKind;
var pos : Position;
}
enum FieldKind {
FMethod( args : Array<FArg>, ret : TypeAttr );
FAttribute( t : TypeAttr );
DConst( name : String, type : Type, value : String );
}
typedef FArg = { name : String, opt : Bool, t : TypeAttr };
typedef TypeAttr = { var t : Type; var attr : Array<Attrib>; };
enum Type {
TVoid;
TInt;
TShort;
TFloat;
TDouble;
TBool;
TAny;
TVoidPtr;
TCustom( id : String );
TArray( t : Type );
}
enum Attrib {
// fields
AValue;
ARef;
AConst;
AOperator( op : String );
// interfaces
ANoDelete;
APrefix( prefix : String );
AJSImplementation( name : String );
}

View File

@ -0,0 +1,447 @@
package webidl;
import webidl.Data;
class Generate {
static var HEADER_EMSCRIPTEN = "
#include <emscripten.h>
#define HL_PRIM
#define HL_NAME(n) EMSCRIPTEN_KEEPALIVE eb_##n
#define DEFINE_PRIM(ret, name, args)
#define _OPT(t) t*
#define _GET_OPT(value,t) *value
";
static var HEADER_HL = "
#include <hl.h>
#define _IDL _BYTES
#define _OPT(t) vdynamic *
#define _GET_OPT(value,t) (value)->v.t
";
static var HEADER_NO_GC = "
#define alloc_ref(r, _) r
#define alloc_ref_const(r,_) r
#define _ref(t) t
#define _unref(v) v
#define free_ref(v) delete (v)
#define HL_CONST const
";
static var HEADER_GC = "
template <typename T> struct pref {
void *finalize;
T *value;
};
#define _ref(t) pref<t>
#define _unref(v) v->value
#define alloc_ref(r,t) _alloc_ref(r,finalize_##t)
#define alloc_ref_const(r, _) _alloc_const(r)
#define HL_CONST
template<typename T> void free_ref( pref<T> *r ) {
if( !r->finalize ) hl_error(\"delete() is not allowed on const value.\");
delete r->value;
r->value = NULL;
r->finalize = NULL;
}
template<typename T> pref<T> *_alloc_ref( T *value, void (*finalize)( pref<T> * ) ) {
pref<T> *r = (pref<T>*)hl_gc_alloc_finalizer(sizeof(pref<T>));
r->finalize = finalize;
r->value = value;
return r;
}
template<typename T> pref<T> *_alloc_const( const T *value ) {
pref<T> *r = (pref<T>*)hl_gc_alloc_noptr(sizeof(pref<T>));
r->finalize = NULL;
r->value = (T*)value;
return r;
}
";
static function initOpts( opts : Options ) {
if( opts.outputDir == null )
opts.outputDir = "";
else if( !StringTools.endsWith(opts.outputDir,"/") )
opts.outputDir += "/";
}
public static function generateCpp( opts : Options ) {
initOpts(opts);
var file = opts.idlFile;
var content = sys.io.File.getBytes(file);
var parse = new webidl.Parser();
var decls = null;
var gc = opts.autoGC;
try {
decls = parse.parseFile(file, new haxe.io.BytesInput(content));
} catch( msg : String ) {
throw msg + "(" + file+" line " + parse.line+")";
}
var output = new StringBuf();
function add(str:String) {
output.add(str.split("\r\n").join("\n") + "\n");
}
add("#ifdef EMSCRIPTEN");
add("");
add(StringTools.trim(HEADER_EMSCRIPTEN));
add(StringTools.trim(HEADER_NO_GC));
add("");
add("#else");
add("");
add('#define HL_NAME(x) ${opts.nativeLib}_##x');
add(StringTools.trim(HEADER_HL));
add(StringTools.trim(gc ? HEADER_GC : HEADER_NO_GC));
add("");
add("#endif");
if( opts.includeCode != null ) {
add("");
add(StringTools.trim(opts.includeCode));
}
add("");
add('extern "C" {');
add("");
var typeNames = new Map();
var enumNames = new Map();
// ignore "JSImplementation" interfaces (?)
for( d in decls.copy() )
switch( d.kind ) {
case DInterface(_, attrs, _):
for( a in attrs )
switch( a ) {
case AJSImplementation(_):
decls.remove(d);
break;
default:
}
default:
}
for( d in decls ) {
switch( d.kind ) {
case DInterface(name, attrs, _):
var prefix = "";
for( a in attrs )
switch( a ) {
case APrefix(name): prefix = name;
default:
}
var fullName = "_ref(" + prefix + name+")*";
typeNames.set(name, { full : fullName, constructor : prefix + name });
if( attrs.indexOf(ANoDelete) >= 0 )
continue;
add('static void finalize_$name( $fullName _this ) { free_ref(_this); }');
add('HL_PRIM void HL_NAME(${name}_delete)( $fullName _this ) {\n\tfree_ref(_this);\n}');
add('DEFINE_PRIM(_VOID, ${name}_delete, _IDL);');
case DEnum(name, values):
enumNames.set(name, true);
typeNames.set(name, { full : "int", constructor : null });
add('static $name ${name}__values[] = { ${values.join(",")} };');
case DImplements(_):
}
}
function getEnumName( t : webidl.Data.Type ) {
return switch( t ) {
case TCustom(id): enumNames.exists(id) ? id : null;
default: null;
}
}
function makeType( t : webidl.Data.Type ) {
return switch( t ) {
case TFloat: "float";
case TDouble: "double";
case TShort: "short";
case TInt: "int";
case TVoid: "void";
case TAny, TVoidPtr: "void*";
case TArray(t): makeType(t) + "*";
case TBool: "bool";
case TCustom(id): typeNames.get(id).full;
}
}
function defType( t ) {
return switch( t ) {
case TFloat: "_F32";
case TDouble: "_F64";
case TShort: "_I16";
case TInt: "_I32";
case TVoid: "_VOID";
case TAny, TVoidPtr: "_BYTES";
case TArray(t): "_BYTES";
case TBool: "_BOOL";
case TCustom(name): enumNames.exists(name) ? "_I32" : "_IDL";
}
}
function dynamicAccess(t) {
return switch( t ) {
case TFloat: "f";
case TDouble: "d";
case TShort: "ui16";
case TInt: "i";
case TBool: "b";
default: throw "assert";
}
}
function makeTypeDecl( td : TypeAttr ) {
var prefix = "";
for( a in td.attr ) {
switch( a ) {
case AConst: prefix += "HL_CONST ";
default:
}
}
return prefix + makeType(td.t);
}
function isDyn( arg : { opt : Bool, t : TypeAttr } ) {
return arg.opt && !arg.t.t.match(TCustom(_));
}
for( d in decls ) {
switch( d.kind ) {
case DInterface(name, attrs, fields):
for( f in fields ) {
switch( f.kind ) {
case FMethod(margs, ret):
var isConstr = f.name == name;
var args = isConstr ? margs : [{ name : "_this", t : { t : TCustom(name), attr : [] }, opt : false }].concat(margs);
var tret = isConstr ? { t : TCustom(name), attr : [] } : ret;
var funName = name + "_" + (isConstr ? "new" + args.length : f.name + (args.length - 1));
output.add('HL_PRIM ${makeTypeDecl(tret)} HL_NAME($funName)(');
var first = true;
for( a in args ) {
if( first ) first = false else output.add(", ");
if( isDyn(a) )
output.add("_OPT("+makeType(a.t.t)+")");
else
output.add(makeType(a.t.t));
output.add(" " + a.name);
}
add(') {');
function addCall(margs : Array<{ name : String, opt : Bool, t : TypeAttr }> ) {
var refRet = null;
var enumName = getEnumName(tret.t);
if( isConstr ) {
refRet = name;
output.add('return alloc_ref((new ${typeNames.get(refRet).constructor}(');
} else {
if( tret.t != TVoid ) output.add("return ");
for( a in ret.attr ) {
switch( a ) {
case ARef, AValue:
refRet = switch(tret.t) {
case TCustom(id): id;
default: throw "assert";
}
if( a == ARef && tret.attr.indexOf(AConst) >= 0 )
output.add('alloc_ref_const(&('); // we shouldn't call delete() on this one !
else
output.add('alloc_ref(new ${typeNames.get(refRet).constructor}(');
default:
}
}
if( enumName != null )
output.add('make__$enumName(');
else if( refRet == null && ret.t.match(TCustom(_)) ) {
refRet = switch(tret.t) {
case TCustom(id): id;
default: throw "assert";
}
if( tret.attr.indexOf(AConst) >= 0 )
output.add('alloc_ref_const((');
else
output.add('alloc_ref((');
}
switch( f.name ) {
case "op_mul":
output.add("*_unref(_this) * (");
case "op_add":
output.add("*_unref(_this) + (");
case "op_sub":
output.add("*_unref(_this) - (");
case "op_div":
output.add("*_unref(_this) / (");
case "op_mulq":
output.add("*_unref(_this) *= (");
default:
output.add("_unref(_this)->" + f.name+"(");
}
}
var first = true;
for( a in margs ) {
if( first ) first = false else output.add(", ");
for( a in a.t.attr ) {
switch( a ) {
case ARef: output.add("*"); // unref
default:
}
}
var e = getEnumName(a.t.t);
if( e != null )
output.add('${e}__values[${a.name}]');
else switch( a.t.t ) {
case TCustom(_):
output.add('_unref(${a.name})');
default:
if( isDyn(a) ) {
output.add("_GET_OPT("+a.name+","+dynamicAccess(a.t.t)+")");
} else
output.add(a.name);
}
}
if( enumName != null ) output.add(')');
if( refRet != null ) output.add(')),$refRet');
add(");");
}
var hasOpt = false;
for( i in 0...margs.length )
if( margs[i].opt ) {
hasOpt = true;
break;
}
if( hasOpt ) {
for( i in 0...margs.length )
if( margs[i].opt ) {
add("\tif( !" + margs[i].name+" )");
output.add("\t\t");
addCall(margs.slice(0, i));
add("\telse");
}
output.add("\t\t");
addCall(margs);
} else {
output.add("\t");
addCall(margs);
}
add('}');
output.add('DEFINE_PRIM(${defType(tret.t)}, $funName,');
for( a in args )
output.add(' ' + (isDyn(a) ? "_NULL(" + defType(a.t.t)+")" : defType(a.t.t)));
add(');');
add('');
case FAttribute(t):
var isVal = t.attr.indexOf(AValue) >= 0;
var tname = switch( t.t ) { case TCustom(id): id; default: null; };
var isRef = tname != null;
var enumName = getEnumName(t.t);
var isConst = t.attr.indexOf(AConst) >= 0;
if( enumName != null ) throw "TODO : enum attribute";
add('HL_PRIM ${makeTypeDecl(t)} HL_NAME(${name}_get_${f.name})( ${typeNames.get(name).full} _this ) {');
if( isVal ) {
var fname = typeNames.get(tname).constructor;
add('\treturn alloc_ref(new $fname(_unref(_this)->${f.name}),$tname);');
} else if( isRef )
add('\treturn alloc_ref${isConst?'_const':''}(_unref(_this)->${f.name},$tname);');
else
add('\treturn _unref(_this)->${f.name};');
add('}');
add('HL_PRIM ${makeTypeDecl(t)} HL_NAME(${name}_set_${f.name})( ${typeNames.get(name).full} _this, ${makeTypeDecl(t)} value ) {');
add('\t_unref(_this)->${f.name} = ${isVal?"*":""}${isRef?"_unref":""}(value);');
add('\treturn value;');
add('}');
var td = defType(t.t);
add('DEFINE_PRIM($td,${name}_get_${f.name},_IDL);');
add('DEFINE_PRIM($td,${name}_set_${f.name},_IDL $td);');
add('');
case DConst(_, _, _):
}
}
case DEnum(_), DImplements(_):
}
}
add("}"); // extern C
sys.io.File.saveContent(opts.outputDir + opts.nativeLib+".cpp", output.toString());
}
static function command( cmd, args : Array<String> ) {
Sys.println("> " + cmd + " " + args.join(" "));
var ret = Sys.command(cmd, args);
if( ret != 0 ) throw "Command '" + cmd + "' has exit with error code " + ret;
}
public static function generateJs( opts : Options, sources : Array<String>, ?params : Array<String> ) {
if( params == null )
params = [];
initOpts(opts);
var hasOpt = false;
for( p in params )
if( p.substr(0, 2) == "-O" )
hasOpt = true;
if( !hasOpt )
params.push("-O2");
var lib = opts.nativeLib;
var emSdk = Sys.getEnv("EMSCRIPTEN");
if( emSdk == null )
throw "Missing EMSCRIPTEN environment variable. Install emscripten";
var emcc = emSdk + "/emcc";
// build sources BC files
var outFiles = [];
sources.push(lib+".cpp");
for( cfile in sources ) {
var out = opts.outputDir + cfile.substr(0, -4) + ".bc";
var args = params.concat(["-c", cfile, "-o", out]);
command( emcc, args);
outFiles.push(out);
}
// link : because too many files, generate Makefile
var tmp = opts.outputDir + "Makefile.tmp";
var args = params.concat([
"-s", 'EXPORT_NAME="\'$lib\'"', "-s", "MODULARIZE=1",
"--memory-init-file", "0",
"-o", '$lib.js'
]);
var output = "SOURCES = " + outFiles.join(" ") + "\n";
output += "all:\n";
output += "\t"+emcc+" $(SOURCES) " + args.join(" ");
sys.io.File.saveContent(tmp, output);
command("make", ["-f", tmp]);
sys.FileSystem.deleteFile(tmp);
}
}

View File

@ -0,0 +1,459 @@
package webidl;
#if macro
import webidl.Data;
import haxe.macro.Context;
import haxe.macro.Expr;
class Module {
var p : Position;
var hl : Bool;
var pack : Array<String>;
var opts : Options;
var types : Array<TypeDefinition> = [];
function new(p, pack, hl, opts) {
this.p = p;
this.pack = pack;
this.hl = hl;
this.opts = opts;
}
function makeName( name : String ) {
if( opts.chopPrefix != null && StringTools.startsWith(name, opts.chopPrefix) ) name = name.substr(opts.chopPrefix.length);
return capitalize(name);
}
function buildModule( decls : Array<Definition> ) {
for( d in decls )
buildDecl(d);
return types;
}
function makeType( t : TypeAttr ) : ComplexType {
return switch( t.t ) {
case TVoid: macro : Void;
case TInt: macro : Int;
case TShort: hl ? macro : hl.UI16 : macro : Int;
case TFloat: hl ? macro : Single : macro : Float;
case TDouble: macro : Float;
case TBool: macro : Bool;
case TAny: macro : webidl.Types.Any;
case TArray(t):
var tt = makeType({ t : t, attr : [] });
macro : webidl.Types.NativePtr<$tt>;
case TVoidPtr: macro : webidl.Types.VoidPtr;
case TCustom(id): TPath({ pack : [], name : makeName(id) });
}
}
function defVal( t : TypeAttr ) : Expr {
return switch( t.t ) {
case TVoid: throw "assert";
case TInt, TShort: { expr : EConst(CInt("0")), pos : p };
case TFloat, TDouble: { expr : EConst(CFloat("0.")), pos : p };
case TBool: { expr : EConst(CIdent("false")), pos : p };
default: { expr : EConst(CIdent("null")), pos : p };
}
}
function makeNative( name : String ) : MetadataEntry {
return { name : ":hlNative", params : [{ expr : EConst(CString(opts.nativeLib)), pos : p },{ expr : EConst(CString(name)), pos : p }], pos : p };
}
function makeEither( arr : Array<ComplexType> ) {
var i = 0;
var t = arr[i++];
while( i < arr.length ) {
var t2 = arr[i++];
t = TPath({ pack : ["haxe", "extern"], name : "EitherType", params : [TPType(t), TPType(t2)] });
}
return t;
}
function makePosition( pos : webidl.Data.Position ) {
if( pos == null )
return p;
return Context.makePosition({ min : pos.pos, max : pos.pos + 1, file : pos.file });
}
function makeNativeField( iname : String, f : webidl.Data.Field, args : Array<FArg>, ret : TypeAttr, pub : Bool ) : Field {
var name = f.name;
var isConstr = name == iname;
if( isConstr ) {
name = "new";
ret = { t : TCustom(iname), attr : [] };
}
var expr = if( ret.t == TVoid )
{ expr : EBlock([]), pos : p };
else
{ expr : EReturn(defVal(ret)), pos : p };
var access : Array<Access> = [];
if( !hl ) access.push(AInline);
if( pub ) access.push(APublic);
if( isConstr ) access.push(AStatic);
return {
pos : makePosition(f.pos),
name : pub ? name : name + args.length,
meta : [makeNative(iname+"_" + name + (name == "delete" ? "" : ""+args.length))],
access : access,
kind : FFun({
ret : makeType(ret),
expr : expr,
args : [for( a in args ) { name : a.name, opt : a.opt, type : makeType(a.t) }],
}),
};
}
function buildDecl( d : Definition ) {
var p = makePosition(d.pos);
switch( d.kind ) {
case DInterface(iname, attrs, fields):
var dfields : Array<Field> = [];
var variants = new Map();
function getVariants( name : String ) {
if( variants.exists(name) )
return null;
variants.set(name, true);
var fl = [];
for( f in fields )
if( f.name == name )
switch( f.kind ) {
case FMethod(args, ret):
fl.push({args:args, ret:ret,pos:f.pos});
default:
}
return fl;
}
for( f in fields ) {
switch( f.kind ) {
case FMethod(_):
var vars = getVariants(f.name);
if( vars == null ) continue;
var isConstr = f.name == iname;
if( vars.length == 1 && !isConstr ) {
var f = makeNativeField(iname, f, vars[0].args, vars[0].ret, true);
dfields.push(f);
} else {
// create dispatching code
var maxArgs = 0;
for( v in vars ) if( v.args.length > maxArgs ) maxArgs = v.args.length;
if( vars.length > 1 && maxArgs == 0 )
Context.error("Duplicate method declaration", makePosition(vars.pop().pos));
var targs : Array<FunctionArg> = [];
var argsTypes = [];
for( i in 0...maxArgs ) {
var types : Array<{t:TypeAttr,sign:String}>= [];
var names = [];
var opt = false;
for( v in vars ) {
var a = v.args[i];
if( a == null ) {
opt = true;
continue;
}
var sign = haxe.Serializer.run(a.t);
var found = false;
for( t in types )
if( t.sign == sign ) {
found = true;
break;
}
if( !found ) types.push({ t : a.t, sign : sign });
if( names.indexOf(a.name) < 0 )
names.push(a.name);
if( a.opt )
opt = true;
}
argsTypes.push(types);
targs.push({
name : names.join("_"),
opt : opt,
type : makeEither([for( t in types ) makeType(t.t)]),
});
}
// native impls
var retTypes : Array<{t:TypeAttr,sign:String}> = [];
for( v in vars ) {
var f = makeNativeField(iname, f, v.args, v.ret, false );
var sign = haxe.Serializer.run(v.ret);
var found = false;
for( t in retTypes )
if( t.sign == sign ) {
found = true;
break;
}
if( !found ) retTypes.push({ t : v.ret, sign : sign });
dfields.push(f);
}
vars.sort(function(v1, v2) return v1.args.length - v2.args.length);
// dispatch only on args count
function makeCall( v : { args : Array<FArg>, ret : TypeAttr } ) : Expr {
var ident = { expr : EConst(CIdent( (f.name == iname ? "new" : f.name) + v.args.length )), pos : p};
var e : Expr = { expr : ECall(ident, [for( i in 0...v.args.length ) { expr : ECast({ expr : EConst(CIdent(targs[i].name)), pos : p }, null), pos : p }]), pos : p };
if( v.ret.t != TVoid )
e = { expr : EReturn(e), pos : p };
else if( isConstr )
e = macro this = $e;
return e;
}
var expr = makeCall(vars[vars.length - 1]);
for( i in 1...vars.length ) {
var v = vars[vars.length - 1 - i];
var aname = targs[v.args.length].name;
var call = makeCall(v);
expr = macro if( $i{aname} == null ) $call else $expr;
}
dfields.push({
name : isConstr ? "new" : f.name,
pos : makePosition(f.pos),
access : [APublic, AInline],
kind : FFun({
expr : expr,
args : targs,
ret : makeEither([for( t in retTypes ) makeType(t.t)]),
}),
});
}
case FAttribute(t):
var tt = makeType(t);
dfields.push({
pos : p,
name : f.name,
kind : FProp("get", "set", tt),
access : [APublic],
});
dfields.push({
pos : p,
name : "get_" + f.name,
meta : [makeNative(iname+"_get_" + f.name)],
kind : FFun({
ret : tt,
expr : macro return ${defVal(t)},
args : [],
}),
});
dfields.push({
pos : p,
name : "set_" + f.name,
meta : [makeNative(iname+"_set_" + f.name)],
kind : FFun({
ret : tt,
expr : macro return ${defVal(t)},
args : [{ name : "_v", type : tt }],
}),
});
case DConst(name, type, value):
dfields.push({
pos : p,
name : name,
access : [APublic, AStatic, AInline],
kind : FVar(
makeType({t : type, attr : []}),
macro $i{value}
)
});
}
}
var td : TypeDefinition = {
pos : p,
pack : pack,
name : makeName(iname),
meta : [],
kind : TDAbstract(macro : webidl.Types.Ref, [], [macro : webidl.Types.Ref]),
fields : dfields,
};
if( attrs.indexOf(ANoDelete) < 0 )
dfields.push(makeNativeField(iname, { name : "delete", pos : null, kind : null }, [], { t : TVoid, attr : [] }, true));
if( !hl ) {
for( f in dfields )
if( f.meta != null )
for( m in f.meta )
if( m.name == ":hlNative" ) {
if( f.access == null ) f.access = [];
switch( f.kind ) {
case FFun(df):
var call = opts.nativeLib + "._eb_" + switch( m.params[1].expr ) { case EConst(CString(name)): name; default: throw "!"; };
var args : Array<Expr> = [for( a in df.args ) { expr : EConst(CIdent(a.name)), pos : p }];
if( f.access.indexOf(AStatic) < 0 )
args.unshift(macro this);
df.expr = macro return untyped $i{call}($a{args});
default: throw "assert";
}
if (f.access.indexOf(AInline) == -1) f.access.push(AInline);
f.meta.remove(m);
break;
}
}
types.push(td);
case DImplements(name,intf):
var name = makeName(name);
var intf = makeName(intf);
var found = false;
for( t in types )
if( t.name == name ) {
found = true;
switch( t.kind ) {
case TDAbstract(a, _):
t.fields.push({
pos : p,
name : "_to" + intf,
meta : [{ name : ":to", pos : p }],
access : [AInline],
kind : FFun({
args : [],
expr : macro return cast this,
ret : TPath({ pack : [], name : intf }),
}),
});
var toImpl = [intf];
while( toImpl.length > 0 ) {
var intf = toImpl.pop();
var td = null;
for( t2 in types ) {
if( t2.name == intf )
switch( t2.kind ) {
case TDAbstract(a2, _, to):
for ( inheritedField in t2.fields ) {
// Search for existing field
var fieldExists = false;
for ( existingField in t.fields ) {
if ( inheritedField.name == existingField.name ) {
fieldExists = true;
break;
}
}
if ( !fieldExists ) {
t.fields.push(inheritedField);
}
}
default:
}
}
}
default:
Context.warning("Cannot have " + name+" extends " + intf, p);
}
break;
}
if( !found )
Context.warning("Class " + name+" not found for implements " + intf, p);
case DEnum(name, values):
var index = 0;
types.push({
pos : p,
pack : pack,
name : makeName(name),
meta : [{ name : ":enum", pos : p }],
kind : TDAbstract(macro : Int),
fields : [for( v in values ) { pos : p, name : v, kind : FVar(null,{ expr : EConst(CInt(""+(index++))), pos : p }) }],
});
}
}
public static function buildTypes( opts : Options, hl : Bool = false ):Array<TypeDefinition> {
var p = Context.currentPos();
if( opts.nativeLib == null ) {
Context.error("Missing nativeLib option for HL", p);
return null;
}
// load IDL
var file = opts.idlFile;
var content = try {
file = Context.resolvePath(file);
sys.io.File.getBytes(file);
} catch( e : Dynamic ) {
Context.error("" + e, p);
return null;
}
// parse IDL
var parse = new webidl.Parser();
var decls = null;
try {
decls = parse.parseFile(file,new haxe.io.BytesInput(content));
} catch( msg : String ) {
var lines = content.toString().split("\n");
var start = lines.slice(0, parse.line-1).join("\n").length + 1;
Context.error(msg, Context.makePosition({ min : start, max : start + lines[parse.line-1].length, file : file }));
return null;
}
var module = Context.getLocalModule();
var pack = module.split(".");
pack.pop();
return new Module(p, pack, hl, opts).buildModule(decls);
}
public static function build( opts : Options ) {
var file = opts.idlFile;
var module = Context.getLocalModule();
var types = buildTypes(opts, Context.defined("hl"));
if (types == null) return macro : Void;
// Add an init function for initializing the JS module
if (Context.defined("js")) {
types.push(macro class Init {
public static function init(onReady:Void->Void) {
untyped __js__('${opts.nativeLib} = ${opts.nativeLib}().then(onReady)');
}
});
// For HL no initialization is required so execute the callback immediately
} else if (Context.defined("hl")) {
types.push(macro class Init {
public static function init(onReady:Void->Void) {
onReady();
}
});
}
Context.defineModule(module, types);
Context.registerModuleDependency(module, file);
return macro : Void;
}
/**
* Capitalize the first letter of a string
* @param text The string to capitalize
*/
private static function capitalize(text:String) {
return text.charAt(0).toUpperCase() + text.substring(1);
}
}
#end

View File

@ -0,0 +1,10 @@
package webidl;
typedef Options = {
var idlFile : String;
var nativeLib : String;
@:optional var outputDir : String;
@:optional var includeCode : String;
@:optional var chopPrefix : String;
@:optional var autoGC : Bool;
}

View File

@ -0,0 +1,509 @@
package webidl;
import webidl.Data;
private enum Token {
TEof;
TId( s : String );
TPOpen;
TPClose;
TBrOpen;
TBrClose;
TBkOpen;
TBkClose;
TSemicolon;
TComma;
TOp( op : String );
TString( str : String );
}
class Parser {
public var line : Int;
var input : haxe.io.Input;
var char : Int;
var ops : Array<Bool>;
var idents : Array<Bool>;
var tokens : Array<Token>;
var pos = 0;
var fileName : String;
public function new() {
var opChars = "+*/-=!><&|^%~";
var identChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_";
idents = new Array();
ops = new Array();
for( i in 0...identChars.length )
idents[identChars.charCodeAt(i)] = true;
for( i in 0...opChars.length )
ops[opChars.charCodeAt(i)] = true;
}
public function parseFile( fileName : String, input : haxe.io.Input ) {
this.fileName = fileName;
pos = 0;
line = 1;
char = -1;
tokens = [];
this.input = input;
var out = [];
while( true ) {
var tk = token();
if( tk == TEof ) break;
push(tk);
out.push(parseDecl());
}
return out;
}
function parseDecl() {
var attr = attributes();
var pmin = this.pos;
switch( token() ) {
case TId("interface"):
var name = ident();
ensure(TBrOpen);
var fields = [];
while( true ) {
var tk = token();
if( tk == TBrClose ) break;
push(tk);
fields.push(parseField());
}
ensure(TSemicolon);
return { pos : makePos(pmin), kind : DInterface(name, attr, fields) };
case TId("enum"):
var name = ident();
ensure(TBrOpen);
var values = [];
if( !maybe(TBrClose) )
while( true ) {
switch( token() ) {
case TString(str): values.push(str);
case var tk: unexpected(tk);
}
switch( token() ) {
case TBrClose: break;
case TComma: continue;
case var tk: unexpected(tk);
}
}
ensure(TSemicolon);
return { pos : makePos(pmin), kind : DEnum(name, values) };
case TId(name):
if( attr.length > 0 )
throw "assert";
ensure(TId("implements"));
var intf = ident();
ensure(TSemicolon);
return { pos : makePos(pmin), kind : DImplements(name, intf) };
case var tk:
return unexpected(tk);
}
}
function attributes() {
if( !maybe(TBkOpen) )
return [];
var attrs = [];
while( true ) {
var attr = switch( ident() ) {
case "Value": AValue;
case "Ref": ARef;
case "Const": AConst;
case "NoDelete": ANoDelete;
case "Prefix":
ensure(TOp("="));
APrefix(switch( token() ) { case TString(s): s; case var tk: unexpected(tk); });
case "JSImplementation":
ensure(TOp("="));
AJSImplementation(switch( token() ) { case TString(s): s; case var tk: unexpected(tk); });
case "Operator":
ensure(TOp("="));
AOperator(switch( token() ) { case TString(s): s; case var tk: unexpected(tk); });
case var attr:
error("Unsupported attribute " + attr);
null;
}
attrs.push(attr);
if( !maybe(TComma) ) break;
}
ensure(TBkClose);
return attrs;
}
function type() : Type {
var id = ident();
var t = switch( id ) {
case "void": TVoid;
case "float": TFloat;
case "double": TDouble;
case "long", "int": TInt; // long ensures 32 bits
case "short": TShort;
case "boolean", "bool": TBool;
case "any": TAny;
case "VoidPtr": TVoidPtr;
default: TCustom(id);
};
if( maybe(TBkOpen) ) {
ensure(TBkClose);
t = TArray(t);
}
return t;
}
function makePos( pmin : Int ) {
return { file : fileName, line : line, pos : pmin };
}
function parseField() : Field {
var attr = attributes();
var pmin = this.pos;
if( maybe(TId("attribute")) ) {
var t = type();
var name = ident();
ensure(TSemicolon);
return { name : name, kind : FAttribute({ t : t, attr : attr }), pos : makePos(pmin) };
}
if( maybe(TId("const")) ) {
var type = type();
var name = ident();
ensure(TOp("="));
var value = tokenString(token());
ensure(TSemicolon);
return { name: name, kind : DConst(name, type, value), pos : makePos(pmin) };
}
var tret = type();
var name = ident();
ensure(TPOpen);
var args = [];
if( !maybe(TPClose) ) {
while( true ) {
var attr = attributes();
var opt = maybe(TId("optional"));
var t = type();
var name = ident();
args.push({ name : name, t : { t : t, attr : attr }, opt : opt });
switch( token() ) {
case TPClose:
break;
case TComma:
continue;
case var tk:
unexpected(tk);
}
}
}
ensure(TSemicolon);
return { name : name, kind : FMethod(args, { attr : attr, t : tret }), pos : makePos(pmin) };
}
// --- Lexing
function invalidChar(c:Int) {
error("Invalid char "+c+"("+String.fromCharCode(c)+")");
}
function error( msg : String ) {
throw msg+" line "+line;
}
function unexpected( tk ) : Dynamic {
error("Unexpected " + tokenString(tk));
return null;
}
function tokenString( tk ) {
return switch( tk ) {
case TEof: "<eof>";
case TId(id): id;
case TPOpen: "(";
case TPClose: ")";
case TBkOpen: "[";
case TBkClose: "]";
case TBrOpen: "{";
case TBrClose: "}";
case TComma: ",";
case TSemicolon: ";";
case TOp(op): op;
case TString(str): '"' + str + '"';
}
}
inline function push(tk) {
tokens.push(tk);
}
function ensure(tk) {
var t = token();
if( t != tk && !std.Type.enumEq(t,tk) ) unexpected(t);
}
function maybe(tk) {
var t = token();
if( t == tk || std.Type.enumEq(t,tk) )
return true;
push(t);
return false;
}
function ident() {
var tk = token();
switch( tk ) {
case TId(id): return id;
default:
unexpected(tk);
return null;
}
}
function readChar() {
pos++;
return try input.readByte() catch( e : Dynamic ) 0;
}
function token() : Token {
if( tokens.length > 0 )
return tokens.shift();
var char;
if( this.char < 0 )
char = readChar();
else {
char = this.char;
this.char = -1;
}
while( true ) {
switch( char ) {
case 0: return TEof;
case 32,9,13: // space, tab, CR
case 10: line++; // LF
/* case 48,49,50,51,52,53,54,55,56,57: // 0...9
var n = (char - 48) * 1.0;
var exp = 0.;
while( true ) {
char = readChar();
exp *= 10;
switch( char ) {
case 48,49,50,51,52,53,54,55,56,57:
n = n * 10 + (char - 48);
case 46:
if( exp > 0 ) {
// in case of '...'
if( exp == 10 && readChar() == 46 ) {
push(TOp("..."));
var i = Std.int(n);
return TConst( (i == n) ? CInt(i) : CFloat(n) );
}
invalidChar(char);
}
exp = 1.;
case 120: // x
if( n > 0 || exp > 0 )
invalidChar(char);
// read hexa
#if haxe3
var n = 0;
while( true ) {
char = readChar();
switch( char ) {
case 48,49,50,51,52,53,54,55,56,57: // 0-9
n = (n << 4) + char - 48;
case 65,66,67,68,69,70: // A-F
n = (n << 4) + (char - 55);
case 97,98,99,100,101,102: // a-f
n = (n << 4) + (char - 87);
default:
this.char = char;
return TConst(CInt(n));
}
}
#else
var n = haxe.Int32.ofInt(0);
while( true ) {
char = readChar();
switch( char ) {
case 48,49,50,51,52,53,54,55,56,57: // 0-9
n = haxe.Int32.add(haxe.Int32.shl(n,4), cast (char - 48));
case 65,66,67,68,69,70: // A-F
n = haxe.Int32.add(haxe.Int32.shl(n,4), cast (char - 55));
case 97,98,99,100,101,102: // a-f
n = haxe.Int32.add(haxe.Int32.shl(n,4), cast (char - 87));
default:
this.char = char;
// we allow to parse hexadecimal Int32 in Neko, but when the value will be
// evaluated by Interpreter, a failure will occur if no Int32 operation is
// performed
var v = try CInt(haxe.Int32.toInt(n)) catch( e : Dynamic ) CInt32(n);
return TConst(v);
}
}
#end
default:
this.char = char;
var i = Std.int(n);
return TConst( (exp > 0) ? CFloat(n * 10 / exp) : ((i == n) ? CInt(i) : CFloat(n)) );
}
}*/
case 59: return TSemicolon;
case 40: return TPOpen;
case 41: return TPClose;
case 44: return TComma;
/* case 46:
char = readChar();
switch( char ) {
case 48,49,50,51,52,53,54,55,56,57:
var n = char - 48;
var exp = 1;
while( true ) {
char = readChar();
exp *= 10;
switch( char ) {
case 48,49,50,51,52,53,54,55,56,57:
n = n * 10 + (char - 48);
default:
this.char = char;
return TConst( CFloat(n/exp) );
}
}
case 46:
char = readChar();
if( char != 46 )
invalidChar(char);
return TOp("...");
default:
this.char = char;
return TDot;
}*/
case 123: return TBrOpen;
case 125: return TBrClose;
case 91: return TBkOpen;
case 93: return TBkClose;
case 39: return TString(readString(39));
case 34: return TString(readString(34));
// case 63: return TQuestion;
// case 58: return TDoubleDot;
case '='.code:
char = readChar();
if( char == '='.code )
return TOp("==");
else if ( char == '>'.code )
return TOp("=>");
this.char = char;
return TOp("=");
default:
if( ops[char] ) {
var op = String.fromCharCode(char);
var prev = -1;
while( true ) {
char = readChar();
if( !ops[char] || prev == '='.code ) {
if( op.charCodeAt(0) == '/'.code )
return tokenComment(op,char);
this.char = char;
return TOp(op);
}
prev = char;
op += String.fromCharCode(char);
}
}
if( idents[char] ) {
var id = String.fromCharCode(char);
while( true ) {
char = readChar();
if( !idents[char] ) {
this.char = char;
return TId(id);
}
id += String.fromCharCode(char);
}
}
invalidChar(char);
}
char = readChar();
}
return null;
}
function tokenComment( op : String, char : Int ) {
var c = op.charCodeAt(1);
var s = input;
if( c == '/'.code ) { // comment
try {
while( char != '\r'.code && char != '\n'.code ) {
pos++;
char = s.readByte();
}
this.char = char;
} catch( e : Dynamic ) {
}
return token();
}
if( c == '*'.code ) { /* comment */
var old = line;
if( op == "/**/" ) {
this.char = char;
return token();
}
try {
while( true ) {
while( char != '*'.code ) {
if( char == '\n'.code ) line++;
pos++;
char = s.readByte();
}
pos++;
char = s.readByte();
if( char == '/'.code )
break;
}
} catch( e : Dynamic ) {
line = old;
error("Unterminated comment");
}
return token();
}
this.char = char;
return TOp(op);
}
function readString( until ) {
var c = 0;
var b = new haxe.io.BytesOutput();
var esc = false;
var old = line;
var s = input;
while( true ) {
try {
pos++;
c = s.readByte();
} catch( e : Dynamic ) {
line = old;
error("Unterminated string");
}
if( esc ) {
esc = false;
switch( c ) {
case 'n'.code: b.writeByte(10);
case 'r'.code: b.writeByte(13);
case 't'.code: b.writeByte(9);
case "'".code, '"'.code, '\\'.code: b.writeByte(c);
case '/'.code: b.writeByte(c);
default: invalidChar(c);
}
} else if( c == 92 )
esc = true;
else if( c == until )
break;
else {
if( c == 10 ) line++;
b.writeByte(c);
}
}
return b.getBytes().toString();
}
}

View File

@ -0,0 +1,13 @@
package webidl;
abstract Ref(#if hl hl.Bytes #else Dynamic #end) {
}
abstract Any(#if hl hl.Bytes #else Dynamic #end) {
}
abstract VoidPtr(#if hl hl.Bytes #else Dynamic #end) {
}
abstract NativePtr<T>(#if hl hl.BytesAccess<T> #else Dynamic #end) {
}