Browse Source

[cpp] Use more efficient NativeXml externs

Hugh 9 years ago
parent
commit
d64cfcfbda
2 changed files with 831 additions and 399 deletions
  1. 466 399
      std/cpp/NativeXml.hx
  2. 365 0
      std/cpp/NativeXmlImport.cpp

+ 466 - 399
std/cpp/NativeXml.hx

@@ -22,412 +22,479 @@
 package cpp;
 
 @:enum abstract XmlType(Int) {
-	/**
-		Represents an XML element type.
-	**/
-	var Element = 0;
-	/**
-		Represents XML parsed character data type.
-	**/
-	var PCData = 1;
-	/**
-		Represents XML character data type.
-	**/
-	var CData = 2;
-	/**
-		Represents an XML comment type.
-	**/
-	var Comment = 3;
-	/**
-		Represents an XML doctype element type.
-	**/
-	var DocType = 4;
-	/**
-	 	Represents an XML processing instruction type.
-	**/
-	var ProcessingInstruction = 5;
-	/**
-		Represents an XML document type.
-	**/
-	var Document = 6;
+   /**
+      Represents an XML element type.
+   **/
+   var Element = 0;
+   /**
+      Represents XML parsed character data type.
+   **/
+   var PCData = 1;
+   /**
+      Represents XML character data type.
+   **/
+   var CData = 2;
+   /**
+      Represents an XML comment type.
+   **/
+   var Comment = 3;
+   /**
+      Represents an XML doctype element type.
+   **/
+   var DocType = 4;
+   /**
+       Represents an XML processing instruction type.
+   **/
+   var ProcessingInstruction = 5;
+   /**
+      Represents an XML document type.
+   **/
+   var Document = 6;
 }
 
+class NativeXmlState
+{
+   var cur : Xml;
+
+   public function new(x:Xml)
+   {
+      x._children = new Array<Xml>();
+      cur = x;
+   }
+
+
+   @:keep
+   public function xml(name:String, att:Dynamic<String>)
+   {
+      var x = new Xml();
+      x._parent = cur;
+      x.nodeType = Xml.Element;
+      x._nodeName = name;
+      x._attributes = att;
+      x._children = new Array<Xml>();
+      cur.addChild(x);
+      cur = x;
+   }
+
+   @:keep
+   public function cdata(text:String)
+   {
+      var x = new Xml();
+      x._parent = cur;
+      x.nodeType = Xml.CData;
+      x._nodeValue = text;
+      cur.addChild(x);
+   }
+
+   @:keep
+   public function pcdata(text:String)
+   {
+      var x = new Xml();
+      x._parent = cur;
+      x.nodeType = Xml.PCData;
+      x._nodeValue = text;
+      cur.addChild(x);
+   }
+
+   @:keep
+   public function comment(text:String)
+   {
+      var x = new Xml();
+      x._parent = cur;
+      if( text.length>1 && StringTools.fastCodeAt(text,0) == 63 )
+      {
+         x.nodeType = Xml.ProcessingInstruction;
+         text = text.substr(1, text.length - 2);
+      }
+      else
+      {
+         x.nodeType = Xml.Comment;
+      }
+      x._nodeValue = text;
+      cur.addChild(x);
+   }
+
+   @:keep
+   public function doctype(text:String)
+   {
+      var x = new Xml();
+      x._parent = cur;
+      x.nodeType = Xml.DocType;
+      x._nodeValue = text.substr(1);
+      cur.addChild(x);
+   }
+   
+   @:keep
+   public function done()
+   {
+     cur = cur._parent;
+   }
+}
+
+private class NativeXmlIterator
+{
+   var cur = 0;
+   var children:Array<Xml>;
+
+   public function new(inChildren:Array<Xml>)
+   {
+      children = inChildren;
+      cur = 0;
+   }
+
+   public function hasNext() : Bool
+   {
+      var k = cur;
+      var l = children.length;
+      while( k < l )
+      {
+         if (children[k].nodeType == Xml.Element)
+            break;
+         k += 1;
+      }
+      cur = k;
+      return k < l;
+   }
+
+   public function next():Xml
+   {
+      var k = cur;
+      var l = children.length;
+      while( k < l )
+      {
+         var n = children[k];
+         k += 1;
+         if( n.nodeType == Xml.Element )
+         {
+            cur = k;
+            return n;
+         }
+      }
+      return null;
+   }
+}
+
+private class NativeXmlNamedIterator
+{
+   var cur = 0;
+   var children:Array<Xml>;
+   var name:String;
+
+   public function new(inChildren:Array<Xml>, inName:String)
+   {
+      children = inChildren;
+      name = inName;
+      cur = 0;
+   }
+
+
+   public function hasNext() : Bool
+   {
+      var k = cur;
+      var l = children.length;
+      while( k < l )
+      {
+         var n = children[k];
+         if( n.nodeType == Xml.Element && n._nodeName == name )
+            break;
+         k++;
+     }
+     cur = k;
+     return k < l;
+   }
+
+   public function next():Xml
+   {
+      var k = cur;
+      var l = children.length;
+      while( k < l )
+      {
+         var n = children[k];
+         k++;
+         if( n.nodeType == Xml.Element && n._nodeName == name ) {
+            cur = k;
+            return n;
+         }
+      }
+      return null;
+   }
+}
+
+
+
+
+@:cppInclude("./NativeXmlImport.cpp")
+@:allow(cpp.NativeXmlState) @:allow(cpp.NativeXmlIterator) @:allow(cpp.NativeXmlNamedIterator)
 class Xml {
-	static inline var Element = XmlType.Element;
-	static inline var PCData = XmlType.PCData;
-	static inline var CData = XmlType.CData;
-	static inline var Comment = XmlType.Comment;
-	static inline var DocType = XmlType.DocType;
-	static inline var ProcessingInstruction = XmlType.ProcessingInstruction;
-	static inline var Document = XmlType.Document;
-
-
-	private var _nodeName : String;
-	private var _nodeValue : String;
-	private var _attributes : Dynamic<String>;
-	private var _children : Array<Xml>;
-	private var _parent : Xml;
-
-	function new() : Void {
-	}
-
-	private static var _parse = cpp.Lib.load("std","parse_xml",2);
-
-	@:analyzer(ignore) public static function parse( str : String ) : Xml {
-		var x = new Xml();
-		x._children = new Array();
-		var parser = {
-			cur : x,
-			xml : function(name,att) {
-				var x = new Xml();
-				x._parent = untyped __this__.cur;
-				x.nodeType = Xml.Element;
-				x._nodeName = new String(name);
-				x._attributes = att;
-				x._children = new Array();
-				untyped {
-					var i = 0;
-					__this__.cur.addChild(x);
-					__this__.cur = x;
-				}
-			},
-			cdata : function(text) {
-				var x = new Xml();
-				x._parent = untyped __this__.cur;
-				x.nodeType = Xml.CData;
-				x._nodeValue = new String(text);
-				untyped __this__.cur.addChild(x);
-			},
-			pcdata : function(text) {
-				var x = new Xml();
-				x._parent = untyped __this__.cur;
-				x.nodeType = Xml.PCData;
-				x._nodeValue = new String(text);
-				untyped __this__.cur.addChild(x);
-			},
-			comment : function(text:String) {
-				var x = new Xml();
-				x._parent = untyped __this__.cur;
-				if( untyped text.cca(0) == 63 ) {
-					x.nodeType = Xml.ProcessingInstruction;
-					text = new String(text);
-					text = text.substr(1, text.length - 2);
-				} else {
-					x.nodeType = Xml.Comment;
-					text = new String(text);
-				}
-				x._nodeValue = text;
-				untyped __this__.cur.addChild(x);
-			},
-			doctype : function(text) {
-				var x = new Xml();
-				x._parent = untyped __this__.cur;
-				x.nodeType = Xml.DocType;
-				x._nodeValue = (new String(text)).substr(1);
-				var p : Xml = untyped __this__.cur;
-				p.addChild(x);
-			},
-			done : function() {
-				untyped __this__.cur = __this__.cur._parent;
-			}
-		};
-		untyped _parse(str,parser);
-		x.nodeType = Xml.Document;
-		return x;
-	}
-
-
-	public static function createElement( name : String ) : Xml {
-		var r = new Xml();
-		r.nodeType = Xml.Element;
-		r._nodeName = name;
-		r._attributes = null;
-		r._children = new Array();
-		return r;
-	}
-
-	public static function createPCData( data : String ) : Xml {
-		var r = new Xml();
-		r.nodeType = Xml.PCData;
-		r._nodeValue = data;
-		return r;
-	}
-
-	public static function createCData( data : String ) : Xml {
-		var r = new Xml();
-		r.nodeType = Xml.CData;
-		r._nodeValue = data;
-		return r;
-	}
-
-	public static function createComment( data : String ) : Xml {
-		var r = new Xml();
-		r.nodeType = Xml.Comment;
-		r._nodeValue = data;
-		return r;
-	}
-
-	public static function createDocType( data : String ) : Xml {
-		var r = new Xml();
-		r.nodeType = Xml.DocType;
-		r._nodeValue = data;
-		return r;
-	}
-
-	public static function createProcessingInstruction( data : String ) : Xml {
-		var r = new Xml();
-		r.nodeType = Xml.ProcessingInstruction;
-		r._nodeValue = data;
-		return r;
-	}
-
-	public static function createDocument() : Xml {
-		var r = new Xml();
-		r.nodeType = Xml.Document;
-		r._children = new Array();
-		return r;
-	}
-
-	public var nodeType(default,null) : XmlType;
-
-	public var nodeName(get,set) : String;
-
-	public var nodeValue(get,set) : String;
-
-
-	private function get_nodeName() : String {
-		if( nodeType != Xml.Element )
-			throw "bad nodeType";
-		return _nodeName;
-	}
-
-	private function set_nodeName( n : String ) : String {
-		if( nodeType != Xml.Element )
-			throw "bad nodeType";
-		return _nodeName = n;
-	}
-
-	private function get_nodeValue() : String {
-		if( nodeType == Xml.Element || nodeType == Xml.Document )
-			throw "bad nodeType";
-		return _nodeValue;
-	}
-
-	private function set_nodeValue( v : String ) : String {
-		if( nodeType == Xml.Element || nodeType == Xml.Document )
-			throw "bad nodeType";
-		return _nodeValue = v;
-	}
-
-	public var parent(get,null) : Xml;
-	private function get_parent() : Xml {
-		return _parent;
-	}
-
-	public function get( att : String ) : String {
-		if( nodeType != Xml.Element )
-			throw "bad nodeType";
-		return Reflect.field( _attributes, att );
-	}
-
-	public function set( att : String, value : String ) : Void {
-		if( nodeType != Xml.Element )
-			throw "bad nodeType";
-		if (_attributes==null)
-			_attributes = {};
-		Reflect.setField (_attributes, att, value );
-		return null;
-	}
-
-	public function remove( att : String ) : Void{
-		if( nodeType != Xml.Element )
-			throw "bad nodeType";
-		Reflect.deleteField( _attributes, att );
-		return null;
-	}
-
-	public function exists( att : String ) : Bool {
-		if( nodeType != Xml.Element )
-			throw "bad nodeType";
-		return Reflect.hasField( _attributes, att );
-	}
-
-	public function attributes() : Iterator<String> {
-		if( nodeType != Xml.Element )
-			throw "bad nodeType";
-		return Reflect.fields( _attributes ).iterator();
-	}
-
-	public function iterator() : Iterator<Xml> {
-		if( _children == null )
-			throw "bad nodetype";
+   static inline var Element = XmlType.Element;
+   static inline var PCData = XmlType.PCData;
+   static inline var CData = XmlType.CData;
+   static inline var Comment = XmlType.Comment;
+   static inline var DocType = XmlType.DocType;
+   static inline var ProcessingInstruction = XmlType.ProcessingInstruction;
+   static inline var Document = XmlType.Document;
+
+
+   private var _nodeName : String;
+   private var _nodeValue : String;
+   private var _attributes : Dynamic<String>;
+   private var _children : Array<Xml>;
+   private var _parent : Xml;
+
+   function new() : Void {
+   }
+
+   @:extern @:native("parse_xml")
+   static function parse_xml(str:String, state:NativeXmlState) { }
+
+   public static function parse( str : String ) : Xml
+   {
+      var x = new Xml();
+      var state = new NativeXmlState(x);
+      parse_xml(str,state);
+      x.nodeType = Xml.Document;
+      return x;
+   }
+
+
+   public static function createElement( name : String ) : Xml {
+      var r = new Xml();
+      r.nodeType = Xml.Element;
+      r._nodeName = name;
+      r._attributes = null;
+      r._children = new Array();
+      return r;
+   }
+
+   public static function createPCData( data : String ) : Xml {
+      var r = new Xml();
+      r.nodeType = Xml.PCData;
+      r._nodeValue = data;
+      return r;
+   }
+
+   public static function createCData( data : String ) : Xml {
+      var r = new Xml();
+      r.nodeType = Xml.CData;
+      r._nodeValue = data;
+      return r;
+   }
+
+   public static function createComment( data : String ) : Xml {
+      var r = new Xml();
+      r.nodeType = Xml.Comment;
+      r._nodeValue = data;
+      return r;
+   }
+
+   public static function createDocType( data : String ) : Xml {
+      var r = new Xml();
+      r.nodeType = Xml.DocType;
+      r._nodeValue = data;
+      return r;
+   }
+
+   public static function createProcessingInstruction( data : String ) : Xml {
+      var r = new Xml();
+      r.nodeType = Xml.ProcessingInstruction;
+      r._nodeValue = data;
+      return r;
+   }
+
+   public static function createDocument() : Xml {
+      var r = new Xml();
+      r.nodeType = Xml.Document;
+      r._children = new Array();
+      return r;
+   }
+
+   public var nodeType(default,null) : XmlType;
+
+   public var nodeName(get,set) : String;
+
+   public var nodeValue(get,set) : String;
+
+
+   private function get_nodeName() : String {
+      if( nodeType != Xml.Element )
+         throw "bad nodeType";
+      return _nodeName;
+   }
+
+   private function set_nodeName( n : String ) : String {
+      if( nodeType != Xml.Element )
+         throw "bad nodeType";
+      return _nodeName = n;
+   }
+
+   private function get_nodeValue() : String {
+      if( nodeType == Xml.Element || nodeType == Xml.Document )
+         throw "bad nodeType";
+      return _nodeValue;
+   }
+
+   private function set_nodeValue( v : String ) : String {
+      if( nodeType == Xml.Element || nodeType == Xml.Document )
+         throw "bad nodeType";
+      return _nodeValue = v;
+   }
+
+   public var parent(get,null) : Xml;
+   private function get_parent() : Xml {
+      return _parent;
+   }
+
+   public function get( att : String ) : String {
+      if( nodeType != Xml.Element )
+         throw "bad nodeType";
+      return Reflect.field( _attributes, att );
+   }
+
+   public function set( att : String, value : String ) : Void {
+      if( nodeType != Xml.Element )
+         throw "bad nodeType";
+      if (_attributes==null)
+         _attributes = {};
+      Reflect.setField (_attributes, att, value );
+      return null;
+   }
+
+   public function remove( att : String ) : Void{
+      if( nodeType != Xml.Element )
+         throw "bad nodeType";
+      Reflect.deleteField( _attributes, att );
+      return null;
+   }
+
+   public function exists( att : String ) : Bool {
+      if( nodeType != Xml.Element )
+         throw "bad nodeType";
+      return Reflect.hasField( _attributes, att );
+   }
+
+   public function attributes() : Iterator<String> {
+      if( nodeType != Xml.Element )
+         throw "bad nodeType";
+      return Reflect.fields( _attributes ).iterator();
+   }
+
+   public function iterator() : Iterator<Xml> {
+      if( _children == null )
+         throw "bad nodetype";
       return untyped _children.iterator();
-	}
-
-
-	@:analyzer(ignore) public function elements(): Iterator<Xml> {
-		if( _children == null )
-			throw "bad nodetype";
-      var children = _children;
-		return untyped {
-			cur: 0,
-			hasNext : function() {
-				var k:Int = __this__.cur;
-				var l = children.length;
-				while( k < l ) {
-					if( children[k].nodeType == Xml.Element )
-						break;
-					k += 1;
-				}
-				__this__.cur = k;
-				return k < l;
-			},
-			next : function() {
-				var k = __this__.cur;
-				var l = children.length;
-				while( k < l ) {
-					var n = children[k];
-					k += 1;
-					if( n.nodeType == Xml.Element ) {
-						__this__.cur = k;
-						return n;
-					}
-				}
-				return null;
-			}
-		}
-	}
-
-	@:analyzer(ignore) public function elementsNamed( name : String ) : Iterator<Xml> {
-		if( _children == null )
-			throw "bad nodetype";
-      var children = _children;
-		return untyped {
-			cur: 0,
-			hasNext : function() {
-				var k = __this__.cur;
-				var l = children.length;
-				while( k < l ) {
-					var n = children[k];
-					if( n.nodeType == Xml.Element && n._nodeName == name )
-						break;
-					k++;
-				}
-				__this__.cur = k;
-				return k < l;
-			},
-			next : function() {
-				var k = __this__.cur;
-				var l = children.length;
-				while( k < l ) {
-					var n = children[k];
-					k++;
-					if( n.nodeType == Xml.Element && n._nodeName == name ) {
-						__this__.cur = k;
-						return n;
-					}
-				}
-				return null;
-			}
-		}
-	}
-
-	public function firstChild() : Xml {
-		if( _children == null )
-			throw "bad nodetype";
-		return _children[0];
-	}
-
-	public function firstElement() : Xml {
-		if( _children == null )
-			throw "bad nodetype";
-		for( cur in 0..._children.length ) {
-			var n:Xml = _children[cur];
-			if( n.nodeType == Xml.Element )
-				return n;
-		}
-		return null;
-	}
+   }
+
+
+   public function elements(): Iterator<Xml>
+   {
+      if( _children == null )
+         throw "bad nodetype";
+      return new NativeXmlIterator(_children);
+   }
+
+   public function elementsNamed( name : String ) : Iterator<Xml>
+   {
+      if( _children == null )
+         throw "bad nodetype";
+      return new NativeXmlNamedIterator(_children,name);
+   }
+
+   public function firstChild() : Xml {
+      if( _children == null )
+         throw "bad nodetype";
+      return _children[0];
+   }
+
+   public function firstElement() : Xml {
+      if( _children == null )
+         throw "bad nodetype";
+      for( cur in 0..._children.length ) {
+         var n:Xml = _children[cur];
+         if( n.nodeType == Xml.Element )
+            return n;
+      }
+      return null;
+   }
 
    public function addChild( x : Xml ) : Void {
-		if( _children == null )
-			throw "bad nodetype";
-		if( x._parent != null ) x._parent._children.remove(x);
-		x._parent = this;
-		_children.push( x );
-		return null;
-	}
+      if( _children == null )
+         throw "bad nodetype";
+      if( x._parent != null ) x._parent._children.remove(x);
+      x._parent = this;
+      _children.push( x );
+      return null;
+   }
 
    public function removeChild( x : Xml ) : Bool {
-		if( _children == null )
-			throw "bad nodetype";
-		var b = _children.remove( x );
-		if( b ) x._parent = null;
-		return b;
-	}
-
-	public function insertChild( x : Xml, pos : Int ) : Void {
-		if( _children == null )
-			throw "bad nodetype";
-		if( x._parent != null ) x._parent._children.remove(x);
-		x._parent = this;
-		_children.insert( pos, x );
-		return null;
-	}
-
-	public function toString() : String {
-		var s = new StringBuf();
-		toStringRec(s);
-		return s.toString();
-	}
-
-	private function toStringRec(s: StringBuf) : Void {
-		switch( nodeType ) {
-		case Xml.Document:
-			for( x in _children )
-				x.toStringRec(s);
-		case Xml.Element:
-			s.addChar("<".code);
-			s.add(_nodeName);
-			for( k in Reflect.fields(_attributes) ) {
-				s.addChar(" ".code);
-				s.add(k);
-				s.addChar("=".code);
-				s.addChar("\"".code);
-				s.add(Reflect.field(_attributes,k));
-				s.addChar("\"".code);
-			}
-			if( _children.length == 0 ) {
-				s.addChar("/".code);
-				s.addChar(">".code);
-				return;
-			}
-			s.addChar(">".code);
-			for( x in _children )
-				x.toStringRec(s);
-			s.addChar("<".code);
-			s.addChar("/".code);
-			s.add(_nodeName);
-			s.addChar(">".code);
-		case Xml.PCData:
-			s.add(StringTools.htmlEscape(_nodeValue));
-		case Xml.CData:
-			s.add("<![CDATA[");
-			s.add(_nodeValue);
-			s.add("]]>");
-		case Xml.Comment:
-			s.add("<!--");
-			s.add(_nodeValue);
-			s.add("-->");
-		case Xml.DocType:
-			s.add("<!DOCTYPE ");
-			s.add(_nodeValue);
-			s.add(">");
-		case Xml.ProcessingInstruction:
-			s.add("<?");
-			s.add(_nodeValue);
-			s.add("?>");
-		}
-	}
+      if( _children == null )
+         throw "bad nodetype";
+      var b = _children.remove( x );
+      if( b ) x._parent = null;
+      return b;
+   }
+
+   public function insertChild( x : Xml, pos : Int ) : Void {
+      if( _children == null )
+         throw "bad nodetype";
+      if( x._parent != null ) x._parent._children.remove(x);
+      x._parent = this;
+      _children.insert( pos, x );
+      return null;
+   }
+
+   public function toString() : String {
+      var s = new StringBuf();
+      toStringRec(s);
+      return s.toString();
+   }
+
+   private function toStringRec(s: StringBuf) : Void {
+      switch( nodeType ) {
+      case Xml.Document:
+         for( x in _children )
+            x.toStringRec(s);
+      case Xml.Element:
+         s.addChar("<".code);
+         s.add(_nodeName);
+         for( k in Reflect.fields(_attributes) ) {
+            s.addChar(" ".code);
+            s.add(k);
+            s.addChar("=".code);
+            s.addChar("\"".code);
+            s.add(Reflect.field(_attributes,k));
+            s.addChar("\"".code);
+         }
+         if( _children.length == 0 ) {
+            s.addChar("/".code);
+            s.addChar(">".code);
+            return;
+         }
+         s.addChar(">".code);
+         for( x in _children )
+            x.toStringRec(s);
+         s.addChar("<".code);
+         s.addChar("/".code);
+         s.add(_nodeName);
+         s.addChar(">".code);
+      case Xml.PCData:
+         s.add(StringTools.htmlEscape(_nodeValue));
+      case Xml.CData:
+         s.add("<![CDATA[");
+         s.add(_nodeValue);
+         s.add("]]>");
+      case Xml.Comment:
+         s.add("<!--");
+         s.add(_nodeValue);
+         s.add("-->");
+      case Xml.DocType:
+         s.add("<!DOCTYPE ");
+         s.add(_nodeValue);
+         s.add(">");
+      case Xml.ProcessingInstruction:
+         s.add("<?");
+         s.add(_nodeValue);
+         s.add("?>");
+      }
+   }
 
 }

+ 365 - 0
std/cpp/NativeXmlImport.cpp

@@ -0,0 +1,365 @@
+
+#ifdef EPPC
+#include <memory>
+#else
+#include <memory.h>
+#endif
+
+
+#ifndef HX_WINDOWS
+#  include <strings.h>
+#  undef strcmpi
+#  define strcmpi(a,b) strcasecmp(a,b)
+#else
+#   include <string.h>
+#endif
+
+
+// -------------- parsing --------------------------
+
+
+enum STATE {
+   IGNORE_SPACES,
+   BEGIN,
+   BEGIN_NODE,
+   TAG_NAME,
+   BODY,
+   ATTRIB_NAME,
+   EQUALS,
+   ATTVAL_BEGIN,
+   ATTRIB_VAL,
+   CHILDS,
+   CLOSE,
+   WAIT_END,
+   WAIT_END_RET,
+   PCDATA,
+   HEADER,
+   COMMENT,
+   DOCTYPE,
+   CDATA,
+};
+
+static void xml_error( const char *xml, const char *inWhere, int *line, String msg ) {
+   String b = HX_CSTRING("Xml parse error : ") + msg + HX_CSTRING(" at line ") + String(*line) + HX_CSTRING(" : ");
+   String where(inWhere);
+
+   int l = where.length;
+   int nchars = 30;
+   if( inWhere != xml )
+      b += HX_CSTRING("...");
+
+   if (where.length==0)
+      b+= HX_CSTRING("<eof>");
+   else if (where.length<nchars)
+      b+= where;
+   else
+      b+= where.substr(0,nchars) + HX_CSTRING("...");
+
+   hx::Throw(b);
+}
+
+#define ERRORSTR(msg)   xml_error(xml,p,line,msg);
+#define ERROR(msg)   xml_error(xml,p,line,HX_CSTRING(msg));
+
+static bool is_valid_char( int c ) {
+   return ( c >= 'a' && c <= 'z' ) || ( c >= 'A' && c <= 'Z' ) || ( c >= '0' && c <= '9' ) || c == ':' || c == '.' || c == '_' || c == '-';
+}
+
+static void do_parse_xml( const char *xml, const char **lp, int *line, cpp::NativeXmlState callb, String parentname )
+{
+   STATE state = BEGIN;
+   STATE next = BEGIN;
+   String aname;
+   hx::Anon attribs;
+   String nodename;
+
+   const char *start = NULL;
+   const char *p = *lp;
+   char c = *p;
+   int nsubs = 0, nbrackets = 0;
+   while( c ) {
+      switch( state ) {
+      case IGNORE_SPACES:
+         switch( c ) {
+         case '\n':
+         case '\r':
+         case '\t':
+         case ' ':
+            break;
+         default:
+            state = next;
+            continue;
+         }
+         break;
+      case BEGIN:
+         switch( c ) {
+         case '<':
+            state = IGNORE_SPACES;
+            next = BEGIN_NODE;
+            break;
+         default:
+            start = p;
+            state = PCDATA;
+            continue;
+         }
+         break;
+      case PCDATA:
+         if( c == '<' ) {
+            callb->pcdata(String(start,p-start).dup());
+            nsubs++;
+            state = IGNORE_SPACES;
+            next = BEGIN_NODE;
+         }
+         break;
+      case CDATA:
+         if( c == ']' && p[1] == ']' && p[2] == '>' ) {
+            callb->cdata(String(start,p-start).dup());
+            nsubs++;
+            p += 2;
+            state = BEGIN;
+         }
+         break;
+      case BEGIN_NODE:
+         switch( c ) {
+         case '!':
+            if( p[1] == '[' ) {
+               p += 2;
+               if( (p[0] != 'C' && p[0] != 'c') ||
+                  (p[1] != 'D' && p[1] != 'd') ||
+                  (p[2] != 'A' && p[2] != 'a') ||
+                  (p[3] != 'T' && p[3] != 't') ||
+                  (p[4] != 'A' && p[4] != 'a') ||
+                  (p[5] != '[') )
+                  ERROR("Expected <![CDATA[");
+               p += 5;
+               state = CDATA;
+               start = p + 1;
+               break;
+            }
+            if( p[1] == 'D' || p[1] == 'd' ) {
+               if( (p[2] != 'O' && p[2] != 'o') ||
+                  (p[3] != 'C' && p[3] != 'c') ||
+                  (p[4] != 'T' && p[4] != 't') ||
+                  (p[5] != 'Y' && p[5] != 'y') ||
+                  (p[6] != 'P' && p[6] != 'p') ||
+                  (p[7] != 'E' && p[7] != 'e') )
+                  ERROR("Expected <!DOCTYPE");
+               p += 7;
+               state = DOCTYPE;
+               start = p + 1;
+               break;
+            }
+            if( p[1] != '-' || p[2] != '-' )
+               ERROR("Expected <!--");
+            p += 2;
+            state = COMMENT;
+            start = p + 1;
+            break;
+         case '?':
+            state = HEADER;
+            start = p;
+            break;
+         case '/':
+            if( parentname.length==0 )
+               ERROR("Expected node name");
+            start = p + 1;
+            state = IGNORE_SPACES;
+            next = CLOSE;
+            break;
+         default:
+            state = TAG_NAME;
+            start = p;
+            continue;
+         }
+         break;
+      case TAG_NAME:
+         if( !is_valid_char(c) ) {
+            if( p == start )
+               ERROR("Expected node name");
+            nodename = String(start,p-start).dup();
+            attribs = hx::Anon_obj::Create();
+            state = IGNORE_SPACES;
+            next = BODY;
+            continue;
+         }
+         break;
+      case BODY:
+         switch( c ) {
+         case '/':
+            state = WAIT_END;
+            nsubs++;
+            callb->xml(nodename,attribs);
+            break;
+         case '>':
+            state = CHILDS;
+            nsubs++;
+            callb->xml(nodename,attribs);
+            break;
+         default:
+            state = ATTRIB_NAME;
+            start = p;
+            continue;
+         }
+         break;
+      case ATTRIB_NAME:
+         if( !is_valid_char(c) ) {
+            if( start == p )
+               ERROR("Expected attribute name");
+            aname = String(start,p-start).dup();
+            if( attribs->__Field(aname,hx::paccDynamic) != null() )
+               ERROR("Duplicate attribute");
+            state = IGNORE_SPACES;
+            next = EQUALS;
+            continue;
+         }
+         break;
+      case EQUALS:
+         switch( c ) {
+         case '=':
+            state = IGNORE_SPACES;
+            next = ATTVAL_BEGIN;
+            break;
+         default:
+            ERROR("Expected =");
+         }
+         break;
+      case ATTVAL_BEGIN:
+         switch( c ) {
+         case '"':
+         case '\'':
+            state = ATTRIB_VAL;
+            start = p;
+            break;
+         default:
+            ERROR("Expected \"");
+         }
+         break;
+      case ATTRIB_VAL:
+         if( c == *start ) {
+            attribs->Add( aname, String(start+1,p-start-1).dup() );
+            state = IGNORE_SPACES;
+            next = BODY;
+         }
+         break;
+      case CHILDS:
+         *lp = p;
+         do_parse_xml(xml,lp,line,callb,nodename);
+         p = *lp;
+         start = p;
+         state = BEGIN;
+         break;
+      case WAIT_END:
+         switch( c ) {
+         case '>':
+            callb->done();
+            state = BEGIN;
+            break;
+         default :
+            ERROR("Expected >");
+         }
+         break;
+      case WAIT_END_RET:
+         switch( c ) {
+         case '>':
+            if( nsubs == 0 )
+               callb->pcdata(HX_CSTRING(""));
+            *lp = p;
+            return;
+         default :
+            ERROR("Expected >");
+         }
+         break;
+      case CLOSE:
+         if( !is_valid_char(c) ) {
+            if( start == p )
+               ERROR("Expected node name");
+            {
+               String v = String(start,p - start).dup();
+               if( strcmpi(parentname.__s,v.__s) != 0 ) {
+                  ERRORSTR(HX_CSTRING("Expected </") + parentname + HX_CSTRING(">"));
+               }
+            }
+            state = IGNORE_SPACES;
+            next = WAIT_END_RET;
+            continue;
+         }
+         break;
+      case COMMENT:
+         if( c == '-' && p[1] == '-' && p[2] == '>' ) {
+            callb->comment(String(start,p-start).dup());
+            p += 2;
+            state = BEGIN;
+         }
+         break;
+      case DOCTYPE:
+         if( c == '[' )
+            nbrackets++;
+         else if( c == ']' )
+            nbrackets--;
+         else if( c == '>' && nbrackets == 0 ) {
+            callb->doctype(String(start,p-start).dup());
+            state = BEGIN;
+         }
+         break;
+      case HEADER:
+         if( c == '?' && p[1] == '>' ) {
+            p++;
+            callb->comment(String(start,p-start).dup());
+            state = BEGIN;
+         }
+         break;
+      }
+      c = *++p;
+      if( c == '\n' )
+         (*line)++;
+   }
+   if( state == BEGIN ) {
+      start = p;
+      state = PCDATA;
+   }
+   if( parentname.__s == 0 && state == PCDATA ) {
+      if( p != start || nsubs == 0 )
+         callb->pcdata(String(start,p-start).dup());
+      return;
+   }
+   ERROR("Unexpected end");
+}
+
+// ----------------------------------------------
+
+/**
+   <doc>
+   <h1>Xml</h1>
+   <p>
+   The standard event-driven XML parser.
+   </p>
+   </doc>
+**/
+
+/**
+   parse_xml : xml:string -> events:object -> void
+   <doc>
+   The [parse_xml] parse a string and for each parsed element call the
+   corresponding object method in [events] :
+   <ul>
+   <li>[void xml( name : string, attribs : object)] when an XML node is found</li>
+   <li>[void done()] when an XML node is closed</li>
+   <li>[void pcdata(string)] when PCData chars found</li>
+   <li>[void cdata(string)] when a CData session is found</li>
+   <li>[void comment(string)] when some comment or special header is found</li>
+   </ul>
+   You can then implement the events so they build the appropriate XML data
+   structure needed by your language.
+   </doc>
+**/
+static void parse_xml( String str, cpp::NativeXmlState state )
+{
+   int line = 0;
+   const char *p = str.__s;
+   // skip BOM
+   if( p[0] == (char)0xEF && p[1] == (char)0xBB && p[2] == (char)0xBF )
+      p += 3;
+   do_parse_xml(p,&p,&line,state,String());
+}
+
+