Skip to content

Instantly share code, notes, and snippets.

@banthar
Created May 7, 2011 15:17
Show Gist options
  • Save banthar/960575 to your computer and use it in GitHub Desktop.
Save banthar/960575 to your computer and use it in GitHub Desktop.
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