Created
May 7, 2011 15:17
-
-
Save banthar/960575 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
enum Type | |
{ | |
Void; | |
Primitive(name:String); | |
Pointer(to:Type); | |
Function(args:Array<NamedType>,ret:Type); | |
} | |
class NamedType | |
{ | |
public var type:Type; | |
public var name:String; | |
public var optional:Bool; | |
public function new(name,type) | |
{ | |
this.name=name; | |
this.type=type; | |
} | |
} | |
class Declaration | |
{ | |
public var name:String; | |
public var type:Type; | |
public var value:String; | |
public var constant:Bool; | |
public var readonly:Bool; | |
public function new(?name,?type,?value) | |
{ | |
this.name=name; | |
this.type=type; | |
this.value=value; | |
} | |
private static function printType(type:Type):String | |
{ | |
switch(type) | |
{ | |
case Void: | |
return "Void"; | |
case Primitive(name): | |
return name; | |
case Function(args,ret): | |
var s=""; | |
if(args.length==0) | |
s+="Void -> "; | |
else | |
for(a in args) | |
s+=printType(a.type)+" -> "; | |
s+=printType(ret); | |
return "("+s+")"; | |
case Pointer(to): | |
return "Pointer<"+printType(to)+">"; | |
default: | |
return "Dynamic"; | |
} | |
} | |
public function toHaxeString():String | |
{ | |
var s="public "; | |
if(constant) | |
s+="static inline "; | |
switch(type) | |
{ | |
case Function(args,ret): | |
s+="function "+name+"("; | |
var first=true; | |
for(a in args) | |
{ | |
s+=(first?"":", ")+(a.optional?"?":"")+a.name+":"+printType(a.type); | |
first=false; | |
} | |
s+="):"+printType(ret); | |
default: | |
s+="var "+name; | |
if(readonly) | |
s+="(default,null)"; | |
s+=":"+printType(type); | |
} | |
if(value!=null) | |
s+=" = "+value; | |
return s; | |
} | |
} | |
class Interface | |
{ | |
public var name:String; | |
public var declarations:Array<Declaration>; | |
public var base:Array<String>; | |
public function new(name:String) | |
{ | |
this.name=name; | |
declarations=[]; | |
} | |
public function toHaxeString():String | |
{ | |
var s="extern class "+name; | |
var first=true; | |
if(base!=null) | |
{ | |
for(b in base) | |
{ | |
s+=(first?" extends ":", ")+b; | |
first=false; | |
} | |
} | |
s+="\n"; | |
s+="{\n"; | |
for(d in declarations) | |
{ | |
s+="\t"+d.toHaxeString()+";\n"; | |
} | |
s+="}\n"; | |
return s; | |
} | |
} | |
class Module | |
{ | |
public var name:String; | |
public var interfaces:Array<Interface>; | |
public function new(name:String) | |
{ | |
this.name=name; | |
interfaces=[]; | |
} | |
public function toHaxeString():String | |
{ | |
var s=""; | |
if(name!=null) | |
s+="package "+name+";\n\n"; | |
for(i in interfaces) | |
{ | |
s+=i.toHaxeString(); | |
} | |
return s; | |
} | |
} | |
class Stream | |
{ | |
public var filename:String; | |
public var position:Int; | |
public var data:String; | |
public function new(filename:String) | |
{ | |
this.filename=filename; | |
position=0; | |
this.data=neko.io.File.getContent(filename); | |
} | |
public function isDigit():Bool | |
{ | |
return data.charAt(position)>='0' && data.charAt(position)<='9'; | |
} | |
public function isAlpha():Bool | |
{ | |
if(data.charAt(position)>='A' && data.charAt(position)<='Z') | |
return true; | |
else if(data.charAt(position)>='a' && data.charAt(position)<='z') | |
return true; | |
else | |
return data.charAt(position)=="_"; | |
} | |
public function isChar(c:String):Bool | |
{ | |
return data.charAt(position)==c; | |
} | |
public function isEof():Bool | |
{ | |
return position>=data.length; | |
} | |
public function readNumber():String | |
{ | |
readWhiteSpace(); | |
if(!isDigit()) | |
return null; | |
var start=position; | |
while(isDigit() || isAlpha() || isChar(".")) | |
position++; | |
return data.substr(start,position-start); | |
} | |
public function readIdentifier(?pattern:String):String | |
{ | |
readWhiteSpace(); | |
if(!isAlpha()) | |
return null; | |
var start=position; | |
while(isAlpha() || isDigit()) | |
position++; | |
var s=data.substr(start,position-start); | |
if(pattern==null || s==pattern) | |
return s; | |
else | |
{ | |
position=start; | |
return null; | |
} | |
} | |
private function readString(pattern:String):String | |
{ | |
var s=data.substr(position,pattern.length); | |
if(s!=pattern) | |
return null; | |
position+=pattern.length; | |
return s; | |
} | |
public function expect(key:String) | |
{ | |
readWhiteSpace(); | |
if(readString(key)==null) | |
panic("expected: "+key+" not '"+data.substr(position,12)+"...'"); | |
} | |
public function read(key:String) | |
{ | |
readWhiteSpace(); | |
return readString(key); | |
} | |
public function panic(description:String) | |
{ | |
var line=1; | |
var column=0; | |
for(i in 0...position) | |
{ | |
if(data.charAt(i)=="\n") | |
{ | |
line++; | |
column=0; | |
} | |
column++; | |
} | |
throw filename+":"+line+":"+column+": "+description; | |
} | |
public function readQuotedString():String | |
{ | |
readWhiteSpace(); | |
if(!isChar("\"")) | |
return null; | |
var start=position; | |
position++; | |
while(true) | |
{ | |
if(isChar("\\")) | |
position+=2; | |
if(isChar("\"")) | |
{ | |
position++; | |
break; | |
} | |
if(isEof()) | |
panic("unexpected enf of file"); | |
position++; | |
} | |
return data.substr(start,position-start); | |
} | |
public function readWhiteSpace():Void | |
{ | |
while(true) | |
{ | |
if(isChar(" ") || isChar("\t") || isChar("\n") || isChar("\r")) | |
position++; | |
else if(readString("//")!=null || readString("#")!=null) | |
while(readString("\n")==null && !isEof()) | |
position++; | |
else if(readString("/*")!=null ) | |
while(readString("*/")==null && !isEof()) | |
position++; | |
else if(readString("[")!=null ) | |
while(readString("]")==null && !isEof()) | |
position++; | |
else | |
return; | |
} | |
} | |
} | |
class HxIdl | |
{ | |
public static function parseConstant(s:Stream):String | |
{ | |
s.readWhiteSpace(); | |
if(s.isChar("\"")) | |
return s.readQuotedString(); | |
if(s.read("-")!=null) | |
return "-"+s.readNumber(); | |
else | |
return s.readNumber(); | |
} | |
public static function parseType(s:Stream):Type | |
{ | |
if(s.readIdentifier("void")!=null) | |
return Primitive("Void"); | |
var long=0; | |
var type=null; | |
var signed=true; | |
while(true) | |
{ | |
if(s.readIdentifier("unsigned")!=null) | |
{ | |
signed=false; | |
type="int"; | |
} | |
else if(s.readIdentifier("long")!=null) | |
{ | |
long++; | |
type="int"; | |
} | |
else if(s.readIdentifier("short")!=null) | |
{ | |
long--; | |
type="int"; | |
} | |
else | |
{ | |
break; | |
} | |
} | |
if(s.readIdentifier("float")!=null) | |
return Primitive("Float"); | |
else if(s.readIdentifier("double")!=null) | |
return Primitive("Float"); | |
else if(s.readIdentifier("boolean")!=null) | |
return Primitive("Bool"); | |
else if(s.readIdentifier("DOMString")!=null) | |
return Primitive("String"); | |
else if(s.readIdentifier("any")!=null) | |
return Primitive("Dynamic"); | |
if(s.readIdentifier("int")!=null || type=="int") | |
return Primitive("Int"); | |
else | |
{ | |
var t=s.readIdentifier(); | |
if(t==null) | |
return null; | |
else | |
return Primitive(t); | |
} | |
} | |
public static function parseNamedType(s:Stream):NamedType | |
{ | |
var type=parseType(s); | |
if(type==null) | |
return null; | |
while(s.read("*")!=null) | |
type=Pointer(type); | |
var name=s.readIdentifier(); | |
if(name==null) | |
s.panic("expected type name"); | |
return new NamedType(name,type); | |
} | |
public static function parseDeclaration(s:Stream):Declaration | |
{ | |
var declaration:Declaration=new Declaration(); | |
if(s.readIdentifier("readonly")!=null) | |
{ | |
declaration.readonly=true; | |
} | |
if(s.readIdentifier("attribute")!=null) | |
{ | |
declaration.type=parseType(s); | |
declaration.name=s.readIdentifier(); | |
} | |
else if(s.readIdentifier("const")!=null) | |
{ | |
declaration.constant=true; | |
declaration.type=parseType(s); | |
declaration.name=s.readIdentifier(); | |
s.expect("="); | |
declaration.value=parseConstant(s); | |
} | |
else | |
{ | |
var ret=parseType(s); | |
if(ret==null) | |
return null; | |
declaration.name=s.readIdentifier(); | |
s.expect("("); | |
var args=new Array<NamedType>(); | |
while(true) | |
{ | |
var optional=false; | |
s.readIdentifier("in");s.readIdentifier("out"); | |
if(s.readIdentifier("optional")!=null) | |
optional=true; | |
s.readIdentifier("in");s.readIdentifier("out"); | |
var dec=parseNamedType(s); | |
if(dec==null) | |
break; | |
dec.optional=optional; | |
args.push(dec); | |
if(s.read(",")==null) | |
break; | |
} | |
s.expect(")"); | |
declaration.type=Function(args,ret); | |
} | |
do | |
{ | |
s.readIdentifier("setter"); | |
s.readIdentifier("getter"); | |
if(s.readIdentifier("raises")!=null) | |
{ | |
s.expect("("); | |
s.readIdentifier(); | |
while(s.read(",")!=null) | |
s.readIdentifier(); | |
s.expect(")"); | |
} | |
}while(s.read(",")!=null); | |
return declaration; | |
} | |
public static function parseModule(s:Stream,name:String):Array<Module> | |
{ | |
var m=new Module(name); | |
var ms=[m]; | |
while(!s.isEof()) | |
{ | |
if(s.readIdentifier("module")!=null) | |
{ | |
var module_name=s.readIdentifier(); | |
if(module_name==null) | |
s.panic("expected module name"); | |
s.expect("{"); | |
var modules=parseModule(s,module_name); | |
s.expect("}"); | |
s.read(";"); | |
ms=ms.concat(modules); | |
} | |
else if(s.readIdentifier("interface")!=null) | |
{ | |
var i=new Interface(s.readIdentifier()); | |
if(i.name==null) | |
s.panic("expected interface name"); | |
if(s.read(":")!=null) | |
{ | |
var base_class=s.readIdentifier(); | |
if(base_class==null) | |
s.panic("expected base class name"); | |
i.base=[base_class]; | |
while(s.read(",")!=null) | |
i.base.push(s.readIdentifier()); | |
} | |
if(s.read("{")!=null) | |
{ | |
while(true) | |
{ | |
var d=parseDeclaration(s); | |
if(d==null) | |
break; | |
i.declarations.push(d); | |
s.expect(";"); | |
} | |
s.expect("}"); | |
} | |
s.read(";"); | |
m.interfaces.push(i); | |
} | |
else if(s.readIdentifier("cpp_quote")!=null) | |
{ | |
s.expect("("); | |
s.readQuotedString(); | |
s.expect(")"); | |
} | |
else if(s.readIdentifier("typedef")!=null) | |
{ | |
s.panic("typedefs not supported"); | |
} | |
else if(s.readIdentifier("import")!=null) | |
{ | |
s.readQuotedString(); | |
s.expect(";"); | |
} | |
else | |
{ | |
return ms; | |
} | |
} | |
return ms; | |
} | |
public static function createPath(path:String) | |
{ | |
var f=""; | |
for(p in path.split("/")) | |
{ | |
f+=p; | |
if( !neko.FileSystem.exists(f) ) | |
neko.FileSystem.createDirectory(f); | |
f+="/"; | |
} | |
} | |
public static function parseFile(filename:String) | |
{ | |
var s=new Stream(filename); | |
var modules=parseModule(s,null); | |
s.readWhiteSpace(); | |
if(!s.isEof()) | |
s.panic("unexpected: '"+s.data.substr(s.position,12)+"'"); | |
for(module in modules) | |
{ | |
for(i in module.interfaces) | |
{ | |
var pkg:String; | |
if(module.name!=null) | |
pkg="js."+module.name; | |
else | |
pkg="js"; | |
var path=StringTools.replace(pkg,".","/")+"/"; | |
createPath(path); | |
var fout = neko.io.File.write(path+i.name+".hx", false); | |
fout.writeString("\npackage "+pkg+";\n\n"); | |
fout.writeString(i.toHaxeString()); | |
fout.close(); | |
} | |
} | |
} | |
public static function main() | |
{ | |
for(file in neko.Sys.args()) | |
{ | |
try | |
{ | |
parseFile(file); | |
} | |
catch(e:String) | |
{ | |
neko.Lib.println(e); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment