On Sunday, 21 February 2016 at 15:18:44 UTC, ZombineDev wrote:
On Sunday, 21 February 2016 at 12:52:33 UTC, Nicholas Wilson
wrote:
So I was going through the vulcan spec to try to create a
better D bindings for it. (pointer /len pairs to arrays
adhering to D naming conventions and prettying up the *Create
functions functions like vkResult *Create( arg ,&arg, retptr)
to a fancy throw on misuse struct with constructors and that
kind of stuff.)
the structure of the registry is as follows.
struct KHR_registry
{
<snip>
}
I'm glad to see more people looking to create a D binding from
vk.xml!
I was also working on this
(http://forum.dlang.org/post/ygylvtuwwiwyqtcnl...@forum.dlang.org), unfortunately I won't be able to continue my work until early March, so I hope you'll do a good job in the mean time ;)
Unfortunately the spec is written in a very C specific way so
we need to have a bunch of special cases for parsing the C code
that's interleaved with the XML tags.
I haven't used std.xml yet, but I hope I may be able to help
you.
where "string content" is the string between the tags (does
this have a name?) and thing with @Tag are always in the tag
(although it is also inconsistent). I tried looking at the
docs for std.xml but they were not very helpful.
May be you can use
http://dlang.org/phobos/std_xml#.Element.text in combination
with
http://dlang.org/phobos/std_xml#.Element.elements
To further complicate things not all of the fields are present
in all cases also I need to keep track of the number of '*'
(see fields named indirections) after a tag has closed for
pointer type declarations in function signatures and structs.
You mean things like:
<param><type>void</type>** <name>ppData</name></param>
(from the vkMapMemory command) ?
Yeah this is extremely ugly.
One way to do it is to go to the "param" XML element
and check if the size of it's elements matches it's text size.
In the case above everything between <param> and </param> is
39 characters and the two tags are
17 and 19 character respectively (total 36) which leaves 3
chars for the indirections. If we make a pass to remove all
whitespace between tags (between ">" and "<") we should get
exactly 2 characters which is the number of indirection we're
looking for.
Another way is to just strip the tags and leave only their
internal text. In the above example the result should be:
<param>void** ppData</param>
which is valid D code and we can leave it that way for now.
Also is there a way to do sane cross referencing without
having internal pointers everywhere.
I am no XML expert (so there may be an easier way), but I think
it would be easier if we use associative arrays, instead of
plain arrays. That way we can check for example:
if ("VkBuffer" in types && "VkDevice" in types)
types["VkBuffer"].parent = types["VkDevice"];
I'm sure there should be a very elegant way to do this by
recursing down through the sub-structs
The simplest solution, IMO, is to insert all the types,
commands, enums, etc. in associative arrays first and then do
cross-referencing.
E.g.
Type[string] types;
struct Type
{
// ... other members
// Set to string first, and later change
// to the proper Type, after the whole `types` table has
been read.
Algebraic(string, Type*) parent;
// ... more members
}
Otherwise the only sane way to do forward reference resolution
is to use D Fibers. I have used fibers in some of my projects,
but the extra complications are not worth it for this task as
the xml is only 5100 lines and the performance benefits
probably would not be very large.
Many thanks
Nic
BTW if you run into some other issues with std.xml, you can
also check
http://arsdnet.net/web.d/dom.html and
https://github.com/burner/std.xml2
Also don't forget to use unittests extensively!
Thanks for the tips. I used AA and just got it to compile! :) :|
:( but fails to link.
Undefined symbols for architecture x86_64:
"_D28TypeInfo_xC4arsd3dom7Element6__initZ", referenced from:
_D29TypeInfo_AxC4arsd3dom7Element6__initZ in app.o
"_D4arsd3dom12__ModuleInfoZ", referenced from:
_D3app12__ModuleInfoZ in app.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to
see invocation)
--- errorlevel 1
any Ideas?
structure is
app.d
app.o
arsd/
dom.d
dom.o
-------------------
CODE
-------------------
struct KHR_registry
{
string comment; // Not as xml comment but a <comment> ...
</comment>
struct vendorid
{
string name;
string id;
string comment;
}
vendorid[] vendorids;
struct tag
{
string name;
string author;
string contact;
}
tag[] tags;
struct validity_type // does not appear at this level but
is common to commands and types
{
string[] str;
}
struct type
{
string name; // inconsistanly in tag and
content
string requires;
string parent; // these are different things
string parentName; // ditto
string content; // <type ... > content </type>
string category;
bool returnedonly;
validity_type validity;
struct member
{
string typename; //name of the
members type variable tag type
string name; //name of the
member variable
string len; //companion
member length variable for array pointers
bool optional; //should generate
as a default value
bool noautovalidity;
string enum_;
}
member[string] members;
}
type[string] types;
struct enums
{
string name,comment, type, expand; // expand is the
C enum's Prefix e.g. VK_QUERY_TYPE
struct enum_
{
string value, name, comment;
}
enum_[string] values;
}
enums[string] enumerations;
struct command
{
string successcodes;
string errorcodes;
string returnType;
string name;
string content;
struct param
{
string content;
bool optional;
string externsync;
string len;
size_t inderections;
immutable invalidLen = "null-terminated"; // if len
is this then it is not an array
//
although this is inconsistant. sometimes as a len
// others
in the validity
}
param[] params;
validity_type validity;
string queues, renderpass, cmdbufferlevel;
}
command[string] commands;
struct feature
{
string api, name, number;
/*
<require comment="API version">
<type name="VK_API_VERSION"/>
<enum name="VK_VERSION_MAJOR"/>
<command name="VK_VERSION_MINOR"/>
<type name="VK_VERSION_PATCH"/>
</require>*/
struct require
{
string comment;
string metaType; // type enum command see above
variable tag type
string name;
}
require requires;
}
struct extension
{
string name, number, supported;
struct require
{
struct elem
{
string name, content;
string metaType;
string offset, extends;
}
}
}
extension[] extensions;
}
KHR_registry regFromArsdDocument(Document doc)
{
KHR_registry reg;
size_t i;
reg.comment =
doc.getElementsByTagName("comment")[0].innerText(); // only one
comment
reg.vendorids = doc.getElementsByTagName("vendorids")[0]
.getElementsByTagName("vendorid")
.map!(elem =>
{
KHR_registry.vendorid vid;
vid.name =
elem.getAttribute("name");
vid.id =
elem.getAttribute("id");
vid.comment =
elem.getAttribute("comment");
return vid;
})()
.array[].map!(a => a()).array;
reg.tags = doc.getElementsByTagName("tags")[0]
.getElementsByTagName("tag")
.map!(elem =>
{
KHR_registry.tag t;
t.name =
elem.getAttribute("name");
t.author =
elem.getAttribute("author");
t.contact =
elem.getAttribute("contact");
return t;
})
.array.map!(a => a()).array[];
/*reg.types =*/
doc.getElementsByTagName("types")[0] //associatve array
assignment: can we do this as a range?
.getElementsByTagName("type")
.map!(elem =>
{
KHR_registry.type t;
t.name = elem.getAttribute("name"); // for root types only
if (t.name == null)
{
t.name =
elem.getElementsByTagName("name")[0].innerText;
t.parentName =
elem.getElementsByTagName("type")[0].innerText;
}
t.requires = elem.getAttribute("requires");
t.parent = elem.getAttribute("parent");
t.content = elem.innerText;
t.category = elem.getAttribute("catagory");
t.returnedonly = elem.getAttribute("returnedonly") !=
null; // if it is present it is true
if (elem.getElementsByTagName("validity"))
{
t.validity.str =
elem.getElementsByTagName("validity")[0]
.getElementsByTagName("usage")
.map!( elem2 =>
elem2.innerText).array[];
}
if (t.category == "funcpointer")
{
t.parentName = null; // first <type> ... </type> is
not actuall the parent type
// we dont use the types in the signature because we
use the inner text.
}
if (t.category == "struct")
{ // TODO: preserve the XML comments
/*t.members = */ elem.getElementsByTagName("name")
.map!(elem2 =>
{
KHR_registry.type.member
m;
string name =
elem2.getElementsByTagName("name")[0].innerText;
m.name = name;
m.typename =
elem2.getElementsByTagName("type")[0].innerText;
m.len =
elem2.getAttribute("len");
m.enum_ =
elem2.getAttribute("enum"); // for c-style array
decls
// dont store text its the
//wrong way round for D fix it
//later
m.optional =
elem2.getAttribute("optional") != null; // if it is present it
is true
m.noautovalidity =
elem2.getAttribute("noautovalidity") != null;
t.members[name] = m;
});
}
reg.types[t.name] = t;
});
foreach(elem; doc.getElementsByTagName("enums")) // there are
multiple <enums> elements
{
KHR_registry.enums e;
e.name = elem.getAttribute("name");
e.comment = elem.getAttribute("comment");
e.expand = elem.getAttribute("expand");
foreach(elem2; elem.getElementsByTagName("enum"))
{
KHR_registry.enums.enum_ e2;
e2.name = elem.getAttribute("name");
e2.comment = elem.getAttribute("comment");
e2.value = elem.getAttribute("value");
e.values[e2.name] = e2;
}
reg.enumerations[e.name] = e;
}
doc.getElementsByTagName("commands")[0] //associatve array
assignment: can we do this as a range?
.getElementsByTagName("command")
.map!(elem =>
{
KHR_registry.command c;
c.successcodes = elem.getAttribute("successcodes");
c.errorcodes = elem.getAttribute("errorcodes");
c.queues = elem.getAttribute("queues");
c.renderpass = elem.getAttribute("renderpass");
c.cmdbufferlevel = elem.getAttribute("cmdbufferlevel");
c.content = elem.innerText;
c.name =
elem.getElementsByTagName("proto")[0].getElementsByTagName("name")[0].innerText;
c.returnType =
elem.getElementsByTagName("proto")[0].getElementsByTagName("type")[0].innerText;
if (elem.getElementsByTagName("validity"))
{
c.validity.str =
elem.getElementsByTagName("validity")[0]
.getElementsByTagName("usage")
.map!( elem2 =>
elem2.innerText).array[];
}
foreach(elem2; elem.getElementsByTagName("param"))
{
KHR_registry.command.param p;
p.optional = elem2.getAttribute("optional") != null;
// present = true
p.externsync = elem2.getAttribute("externsync");
p.len = elem2.getAttribute("len");
c.params ~= p;
}
});
return reg;
}