since the original project got way too little attention in my opinion, I
would like to give an update on selecthx(ml). The author of selecthx
updated his project today:
http://code.google.com/p/selecthx/
Make sure to check out the awesome amount of selector types that is
supported now:
http://code.google.com/p/selecthx/wiki/SelectorSupport
You can do all kinds of crazy stuff like selecting all odd numbered img
elements through "img:nth-of-type(odd)" and whatnot.
And here's my clone that works on haxe Xml:
http://code.google.com/p/selecthxml/
I've successfully ran unit tests of all the basic and some of the fancy
stuff on flash9, js, neko, cpp and php targets. If we fully polish these
libs, it might be our reply to the lack of E4X support in haxe. What do
you think?
Simon
Simon
--
To post to this group haxe...@googlegroups.com
http://groups.google.com/group/haxelang?hl=en
I'm pretty excited about this... Looking forward to getting home and playing with it.
Not on my laptop at the moment, so I can't test but does this use macros at all? Could it be run with selectors given at runtime?
...
Sent from my phone, so apologies for any typos...
The original selecthx uses a macro to generate a typed function which is
executed at runtime. It actually parses the selector string and inspects
which type you are selecting, which will give you fully typed completion
(e.g. if you select "input[type=text]", it will be treated as
js.Dom.Text). The same process is then dynamically repeated at runtime,
where again the selector is parsed and then executed.
It might seem a little odd that the selectors are parsed at both compile
time and runtime, but it provides the most flexibility and passing
values from macro runtime to actual runtime isn't trivial anyway.
For my Xml version, the whole macro pass is currently only used to
determine if you select a concrete element (with #id) or an array of
elements. I plan to allow custom inspectors later, which will then
enable typed working on known Xml formats.
Simon
Actually, it will attempt to use querySelectorAll, which has decent
support: https://developer.mozilla.org/en/DOM/Document.querySelectorAll#Browser_compatibility
I would speculate, that this will perform better than anything you
could spin yourself, even if you were to translate the selector to a
matching function at macro time already.
> For my Xml version, the whole macro pass is currently only used to determine
> if you select a concrete element (with #id) or an array of elements. I plan
> to allow custom inspectors later, which will then enable typed working on
> known Xml formats.
Intriguing idea. Might make Xml parsing a joy ;)
>> For my Xml version, the whole macro pass is currently only used to determine
>> if you select a concrete element (with #id) or an array of elements. I plan
>> to allow custom inspectors later, which will then enable typed working on
>> known Xml formats.
> Intriguing idea. Might make Xml parsing a joy ;)
I just can't make up my mind how to integrate the inspectors.
Integrating a custom inspector function is easy, but I don't want to
force people to write
xml.select("#menu ul > li a", myInspectorFunc);
There's the option of making it state based, i.e.
SelectDom.inspectorFunc = myInspectorFunc;
but this could be tricky when working with different XML formats.
Ideally, the inspector functions would be directly linked to each Xml
instance (identifier), but I'm not sure if that could be set up reliably
during compile time.
Any thoughts on this?
Simon
> I just can't make up my mind how to integrate the inspectors. Integrating a
> custom inspector function is easy, but I don't want to force people to write
> xml.select("#menu ul > li a", myInspectorFunc);
> There's the option of making it state based, i.e.
> SelectDom.inspectorFunc = myInspectorFunc;
> but this could be tricky when working with different XML formats.
>
> Ideally, the inspector functions would be directly linked to each Xml
> instance (identifier), but I'm not sure if that could be set up reliably
> during compile time.
>
> Any thoughts on this?
Sure :)
Well, the type is actually associated with the document, not with the
general API nor with just the call.
Therefore I think this makes sense:
typedef Rules = {
li: ListItem,
a: Anchor,
ul: UnorderedList,
document: Document,
}
typedef TypedXml<T> = Xml;
var xml:TypedXml<Rules> = Xml.parse("<document><el id='test'>Hello</el><ul>
<li><a>1</a></li>
<li><a>2</a></li>
</ul></document>");
The problem here is, that you can lose that type rather quickly, so
this might be a better alternative:
@:native("Xml") extern class TypedXml<T> {
public inline function x():Xml
return untyped this
static public inline function parse<A>(source:String):TypedXml<A>
return untyped Xml.parse(source)
}
Here however the problem is, that the type is not Xml for the
compiler, so the ExprRequire<Xml> will not match. But that's a minor
hurdle ;)
Regards,
Juraj
Thanks, that looks very promising. I was thinking in terms of functions
because I don't think things like the HTML input types where the type
depends on an attribute can be properly handled through typedefs. This
should be fine for all other cases though.
Simon
typedef Rules = {
@:pseudo(type) input: {
text:TextInput,
button:Button,
}
a: Anchor,
ul: UnorderedList,
document: Document,
}
Regards,
Juraj
That looks funny, I like it.
I managed to implement the basic version, but I'm unsure as to what
actually return during runtime. The whole thing works so well for the
Dom objects because they all inherit from the MetaDom structure which
has the basic modification operations. The equivalent in regards to Xml
would be something like
typedef Anchor = {
> Xml,
href: String
};
This can be declared, but I couldn't find any obvious way of actually
creating these objects from a given Xml node. I guess this still needs
some thought.
Simon
Yes, it does look funny. On second thought this might actually be better:
typedef Rules = {
@:pseudo(input.type) text:TextInput,
@:pseudo(input.type) button:Button,
a: Anchor,
ul: UnorderedList,
document: Document,
}
This has duplication, but it's flat. I guess it's a matter of taste.
Just wanted to add the option :)
> I managed to implement the basic version, but I'm unsure as to what actually
> return during runtime. The whole thing works so well for the Dom objects
> because they all inherit from the MetaDom structure which has the basic
> modification operations. The equivalent in regards to Xml would be something
> like
>
> typedef Anchor = {
>> Xml,
> href: String
> };
>
> This can be declared, but I couldn't find any obvious way of actually
> creating these objects from a given Xml node. I guess this still needs some
> thought.
Just as a thought: Basically, you also don't have to define the types
explicitly but just have one structure and declare the rest through
Context.define to get what you actually need (the return values must
actually be objects created from the result nodes, otherwise field
access won't do what you'd want it to).
class MyRules implements Rules {
@:name(TextInput) @:pseudo(input.type) text:{ text:String },
@:pseudo(input.type) button:{ caption:String },
@:name(Anchor) a: { href: String },
@:name(UnorderedList) ul: {},
document: {},
}
From this you can generate any type you need and the code that will
transform a result node into it.
Regards,
Juraj
I'm not sure if it's even necessary to define the types explicitely. It
should be sufficient to return
Context.parse("function():{href:String} { ... }()", ...);
from the macro. I still need the specific Rules struct, but the
@:name(Anchor) could be omitted, no?
The above is easy to do, but I don't want to lose the real Xml node
because people might want to operate on that on (otherwise typed) Xml
objects as well. I could obviously just return a {href:String, xml:Xml}
object, but that might lead to name collision issues if there's ever an
attribute named xml. I also don't want to end up with a {
attrs:{href:String}, xml:Xml} object as that would partially defeat the
purpose of having easier Xml access. Since I would detect name
collisions in the macro, I think it's okay to go that way and somehow
react to an occuring collision... somehow.
Simon
Technically, it could be, but if you have named types for everything,
they can be explicitly referenced by other code. I find that to be
comforting. I regularly succeed in having type inference give up on me
:D
> The above is easy to do, but I don't want to lose the real Xml node because
> people might want to operate on that on (otherwise typed) Xml objects as
> well. I could obviously just return a {href:String, xml:Xml} object, but
> that might lead to name collision issues if there's ever an attribute named
> xml. I also don't want to end up with a { attrs:{href:String}, xml:Xml}
> object as that would partially defeat the purpose of having easier Xml
> access. Since I would detect name collisions in the macro, I think it's okay
> to go that way and somehow react to an occuring collision... somehow.
Hide it in a "runtime only" field, that's not a valid attribute name
and access it through a helper function. For example, you could add
this to SelectDom:
static public inline function getXML(result:SelectDom):Xml {
return Reflect.field(result, " can't touch this!!! ");
}
And then go:
Context.parse("function():{>SelectDom, href:String} { ... }()", ...);
Where the field is actually created. As a matter of fact, I don't know
how PHP would respond to you using arbitrary field names for anonymous
objects, but I think it works.
This way, you can call SelectDom.getXML to get the xml, even if there
is a getXML attribute on the element. Otherwise you can call it on the
result itself through using. Of course declaring the result to be a
subtype of SelectDom is just an arbitrary choice. Anything
distinguishable will do.
Regards,
Juraj
I had this idea too, but I thought it would only shift the issue to
another field name (getXml). I'm not sure how that behaves with using,
will have to check that out. The rest seems to be working, I'll clean up
my code and update it tomorrow.
By the way, tink's Printer.hx line 77:
if (a.length > 1)
Shouldn't this be > 0?
Simon
Teaser image for upcoming update:
http://simn.de/haxe/selecthxml_nmml.png
Note how I gracefully removed the if attribute from the window tag to
hide a problem case. On the bright side, note how it auto completes to a
single element of type window. Working with Xml has never been typesafer!
Simon
No, the getXml is not a member of the result, that's the difference.
The name of the field is something that just can't collide, containing
spaces for example. The getXml through using and *if* there is a
getXml attribute, one can still get to the Xml by an explicit call.
> By the way, tink's Printer.hx line 77:
> if (a.length > 1)
> Shouldn't this be > 0?
That is correct, yes :)
That's the convincing argument.
I've integrated a first version which now has tink_macros as a
dependency. Usage can be seen here:
http://code.google.com/p/selecthxml/source/browse/trunk/test/TestTyped.hx
I think this is as typesafe as Xml can get. Comments and suggestions
appreciated!
Simon
There seems to be a small problem with the whole extension approach.
Currently when doing:
var s = xml.select("body#test");
s = xml.select("body#test");
The compiler complains with
selecthxml.+TypedResult should be selecthxml.+TypedResult
I kind of understand this because I was recreating the TExtend for each
macro call, so I added a caching mechanism using Context.signature of
the class field array:
fields = t.getFields().data();
if (fields.length > 0)
{
var signature = Context.signature(fields);
if (!extensionCache.exists(signature))
extensionCache.set(signature, createExtension( { name:
"TypedResult", pack:["selecthxml"], params:[], sub:null }, fields));
ctype = extensionCache.get(signature);
}
ctype is then used as return type for the function. However, the problem
remains. Might be a general limitation of extensions? I will see if I
can find a way to fix it.
Simon
I have fixed all known problems and updated the documentation:
http://code.google.com/p/selecthxml/wiki/TypedXml
You now get proxy objects to the xml attributes when selecting something
in typed mode. For example, given a proper typedef for the nmml format:
typedef Nmml = {
window: { width:Int, height: Int, fps: Int, orientation:String,
resizable:String }
}
The following would completely type-safely update the width and height
attributes of a window element:
var nmml:TypedXml<Nmml> = Xml.parse(...);
var wnd = nmml.select("window")[0];
wnd.width = 400;
wnd.height = 300;
The type for window will be defined as
selecthxml.types.nmml.Window
I wonder if it makes sense to provide a set of definitions for commonly
used Xml formats?
Simon
> I wonder if it makes sense to provide a set of definitions for commonly used
> Xml formats?
Nah, not really. At least not as a part of the lib. Such definitions
should be bundled with the lib that uses them or simply be provided as
a standalone, with dependency to selecthxml.
Either way, very nice work thus far! :)
Regards,
Juraj
Thank you! I think I'm pretty much finished for now and will release it
to haxelib if nobody has any objections or suggestions.
One more thing I'm not sure about: I need a way to define types for
elements whose names that are keywords, such as <class>. I found that
when comparing strings case insensetively, this works:
typedef Rules = {
Class: { someValue: Float }
}
Do you think this is sufficient, or can you think of a scenario where
this fails?
Simon
Never mind, it seems much cleaner to just allow @value on element names too:
typedef Rules = {
@value('class') cl: { someValue: Float }
}
Simon
I added it to haxelib as selecthxml.
Simon
Simon
Do you have a particular Xml format you're working with? I want to make
a separate lib with a collection of typedefs for commonly used Xml
formats so this is even easier to use. I just don't know what's commonly
used by haxe devs.
Simon
Simon
> Do you have a particular Xml format you're working with? I want to make a separate lib with a collection of typedefs for commonly used Xml formats so this is even easier to use. I just don't know what's commonly used by haxe devs.
How about two haxe specific formats: the format used for RTTI, and Haxedoc's XML format.
Well the TypedXml is all about attributes. I don't plan to implement
anything related to child nodes as that is properly reflected by the
selection engine itself. You can select a certain node such as
var formNode = myXml.select("form#form1");
and then select its children input nodes:
for (inputNode in formNode.getXml().select('input'))
trace(inputNode.value);
You can also obviously select them directly:
var inputNodes = myXml.select("form#form1 input");
> How about two haxe specific formats: the format used for RTTI, and
> Haxedoc's XML format.
Are these actually used in haxe applications?
Simon
Hello,since the original project got way too little attention in my opinion, I
would like to give an update on selecthx(ml). The author of selecthx
updated his project toda
y:
http://code.google.com/p/selecthx/Make sure to check out the awesome amount of selector types that is
supported now:
http://code.google.com/p/selecthx/wiki/SelectorSupport
You can do all kinds of crazy stuff like selecting all odd numbered img
elements through "img:nth-of-type(odd)" and whatnot.And here's my clone that works on haxe Xml:
http://code.google.com/p/selecthxml/I've successfully ran unit tests of all the basic and some of the fancy
stuff on flash9, js, neko, cpp and php targets. If we fully polish these
libs, it might be our reply to the lack of E4X support in haxe. What do
you think?Simon
Hello,since the original project got way too little attention in my opinion, I
would like to give an update on selecthx(ml). The author of selecthx
updated his project today:
http://code.google.com/p/selecthx/Make sure to check out the awesome amount of selector types that is
supported now:
http://code.google.com/p/selecthx/wiki/SelectorSupport
You can do all kinds of crazy stuff like selecting all odd numbered img
elements through "img:nth-of-type(odd)" and whatnot.And here's my clone that works on haxe Xml:
http://code.google.com/p/selecthxml/I've successfully ran unit tests of all the basic and some of the fancy
stuff on flash9, js, neko, cpp and php targets. If we fully polish these
libs, it might be our reply to the lack of E4X support in haxe. What do
you think?Simon
Hello,since the original project got way too little attention in my opinion, I
would like to give an update on selecthx(ml). The author of selecthx
updated his project today:
http://code.google.com/p/selecthx/Make sure to check out the awesome amount of selector types that is
supported now:
http://code.google.com/p/selecthx/wiki/SelectorSupport
You can do all kinds of crazy stuff like selecting all odd numbered img
elements through "img:nth-of-type(odd)" and whatnot.And here's my clone that works on haxe Xml:
http://code.google.com/p/selecthxml/I've successfully ran unit tests of all the basic and some of the fancy
stuff on flash9, js, neko, cpp and php targets. If we fully polish these
libs, it might be our reply to the lack of E4X support in haxe. What do
you think?Simon
I'm not the author, so I can't add it. Your best chances are probably to
file it as an issue:
http://code.google.com/p/selecthx/issues/list
> Also currently in my experimental divtastic ( see the one in haxelib
> not on googlecode ) when I create a div ( HtmlDom ) I store it and the
> style on the class. Now James seemed to think this was very heavy,
> but would it be lighter to name every div ( 'div'+Math.random() ) and
> then only store it's string name and use Selectorhx to get the div
> when I need to modify it?
I don't really get what you're trying to do. The style is stored in the
Dom anyway, so why would you have a separate class?
Simon
I don't really get what you're trying to do. The style is stored in the
Dom anyway, so why would you have a separate class?
Obviously it makes sense to store a reference to objects that you
frequently access, e.g. each frame. Likewise, it makes little sense to
roll out your own accessing structure when the Dom and libs like
selecthx already provide one.
I guess I still don't really get what you're asking.
Simon