Geoffrey Broadwell a écrit :
On Mon, 2008-07-21 at 09:34 +0200, François Perrad wrote:
Geoffrey Broadwell a écrit :
fperrad: How do these bindings actually work?
There'll work with runtime/parrot/library/OpenGL.pir.
OK ... so what could be improved about runtime/parrot/library/OpenGL.pir
so that you didn't have to write any bindings at all, or so that your
bindings could be greatly simplified? So far, I'm seeing the following:
Ok, talking about libraries :
Lua compiler & Lua Standard Libraries are complete (as far as the
current Parrot supports it).
So, since April 2008, I wrote some extension libraries for Lua :
- lua/src/lib/base64.pir : wrapper over library/MIME/Base64.pir
- lua/src/lib/bc.pir : big number library, currently over the PMC
BigInt (waiting for BigFloat)
- lua/src/lib/bitlib.pir : bitwise operation library, pure PIR
- lua/src/lib/lfs.pir : Lua File System library, over the PMC OS (still
incomplete)
- lua/src/lib/md5.pir & sha1.pir : wrapper over PMC MD5 & SHA1 (I wrote
them on the outside of Lua)
- lua/src/lib/random.pir : wrapper over
library/Math/Random/mt19937ar.pir (I wrote it on the outside of Lua)
- lua/src/lib/uuid.pir : wrapper over library/uuid.pir (I wrote it on
the outside of Lua)
Since mid-June 2008, I tried to write extension libraries for Pipp (PHP
supplies more than 2500 functions !!!, huge work, but I believe that PHP
could be the killer application for Parrot) :
- pipp/src/common/php_API.pir & php_MACRO.pir : some helpers (argument
checking)
- pipp/src/common/php_base64.pir : wrapper over library/MIME/Base64.pir
(but incomplete because PHP API has more options than MIME/Base64 Perl API)
- pipp/src/common/php_ctype.pir : pure PIR
- pipp/src/common/php_gmp.pir : started over PMC BigInt, but needs a
full NCI binding over gmp
- pipp/src/common/php_math.pir : pure PIR
- pipp/src/common/php_md5.pir & sha1.pir : wrapper over PMC MD5 & SHA1
- pipp/src/common/php_pcre.pir : wrapper over library/pcre.pir
(incomplete because no PhpArray support, and the NCI PCRE is incomplete
and seems very old)
- pipp/src/common/php_rand.pir : wrapper over PMC Random &
library/Math/Random/mt19937ar.pir
- pipp/src/common/php_type.pir : pure PIR
As I need to write a NCI wrapper over gmp, I begin my study of NCI by
using the OpenGL one (with Lua).
If I try to summarize my experiment with libraries :
Parrot will supply 3 kinds of common libraries for HLL
- written in Perl6, but currently not available (I don't know if NQP is
suitable for writing library)
- written in PIR, but only for bootstrap, because as Bernhard
Schmalhofer tell "PIR is not a decent language"
+ good for libraries not common (in fact equivalent to builtins)
- lua/src/lib/bitlib.pir
- lua/src/lib/lfs.pir
- pipp/src/common/php_ctype.pir
- pipp/src/common/php_math.pir
- pipp/src/common/php_type.pir
+ good when no native library available (but a full test suite is
needed)
- library/Math/Random/mt19937ar.pir (Mersenne Twisted)
- binding over native (C/C++) shared libraries
+ with native PMC (C compile/link)
- sometime, for security reason, a static linkage is mandatory
(libssl is shared lib, but its subset libcrypto is static lib)
- other advantage, PMC allows direct OO interface
+ with NCI : the best way (no C compile/link)
- but only procedural interface (no direct OO)
2 designs choices :
- For long term maintenance, I write PIR close to original C. For
example, I start Lua on Parrot aligned with version 5.0.2 and now it's
5.1.3. And in most of case, the original C is the only valid (updated)
user & requirements documentation.
- I try to emit the same (as possible) error or warning messages than
the original implementation and to have the same interface. Rule of
Least Surprise for the end user. And I could run the test suite against
the original implementation.
Now, talking about Lua specifics :
- Lua Tables are the main (and the only) structured type in Lua, they
supply array & hash (like PHP array).
They are used to supply the namespace support and the OO mecanism.
There are no keyword for namespace.
Standard Libraries & others live in their table (ie namespace).
So, in the init function of a library, I wrote (or generate) some boring
code like :
.const .Sub _mod_funct = 'func'
_mod_func.'setfenv'(_lua__GLOBAL)
set $P1, 'func'
_mod[$P1] = _mod_func
I wait for a IMCC improvement (hi kjs) in order to support :
.macro register(tname, fname)
.const .Sub $fname = .fname
$fname.'setfenv'(_lua__GLOBAL)
set $P1, .fname
.tname[$P1] = $fname
.endm
- Currently, the LuaTable implementation don't support the Parrot
namespaces API. But I think it's doable.
- Since my first implementation, libraries & generated code use the 2
same patterns for function preamble :
.sub 'func_with_vararg' : anon
.param pmc arg1 :optional
.param pmc argn :optional
.param pmc vararg :slurpy
...
.sub 'func_without_vararg' :anon
.param pmc arg1 :optional
.param pmc argn :optional
.param pmc extra :slurpy # ignore silently extra parameters
...
After parameters are checked by helpers that could perform some type
conversion (to_number, to_string, ...
) and eventually a default value support.
I could do the job with :optional, and without any :opt_flag.
It's a old design choice, perhaps now :opt_flag is better.
All that I do in a binding for Lua, I already did it in the whole Lua.
Now, I expect two things of Parrot :
1) for language interoperability :
an automatic conversion between LuaString & PhpString (if they are
defined with "does string maps String").
problems come with LuaTable (with array & hash part) and Perl6Hash
2) thin NCI binding for all native shared libraries (gmp, GD, OpenGL,
MySQL, ...)
I think this layer doesn't add functionality or behavior.
With generated code from .h
Specific OSes path search hidden
A way to import all methods in my namespace, without get_global or
get_hll_global for each symbol.
If NCI binding is thin, it could be common & reusable for each HLL.
But in other hand, each HLL needs a specific layer with its specific
argument checking.
When you study wrappers in C interpreters, there are thin. It's just
glue (generated or not) for building a bridge between languages. And
this glue depends more on target interpreter than on library wrapped.
SWIG is a well-known tool, but I wrote a obscure Perl module
CORBA::Python that generates C/Python API from IDL file.
For example, wxPerl & wxPython include both a thin wrapper over
wxWidget. Now, wxPython package is better than wxPerl, because the
wxPython community is larger and adds functionalities in pure Python
modules.
In early version, wxPython allows only calling convention close to C++
one, now current versions are more pythonic (named parameter, ...). It's
done in Python.
In the same way, when you write a Perl 5 extension, the XS part is
minimum, the perlish interface or OO interface is written in a .pm file.
I think argument type checking & conversion are HLL specific. This layer
could be written in PIR or in HLL (when checking subroutines are
callable from HLL).
Currently, in Pipp, PIR is the only way because the compiler is in early
stage (but in progress). But this way is only valid for binding over
shared libraries. PHP has many specific extension written in pure C, I
think these extension must be rewrite in PHP (not a job for PIR).
In Lua, PIR is available since the beginning, so with the time I become
fluent in PIR.
Finally, improvement in runtime/parrot/library/OpenGL.pir :
at this time, just split the function '_export_all_functions' into
'_export_GL_functions' & '_export_GLUT_functions'.
François.
1. HLL access to the GL constants. At the very least, you should
already be able to define your constant table using the generated
constants in runtime/parrot/include/opengl_defines.pasm, rather than
having to hardcode them all. Even better, this should all be wrapped
up in an HLL-friendly way for you, but I've been looking for
suggestions on how best to do that in a cross-HLL manner.
2. Namespace unflattening (glFoo -> gl.Foo, glutBar -> glut.Bar). That
should be easy for runtime/parrot/library/OpenGL.pir to do, but may
not be valuable to you if you already have to do everything else
below.
3. All subs are marked :anon, and then manually added into a global
LuaTable which appears to be reimplementing a namespace. Why? And
if all Lua namespaces are created this way, does LuaTable implement
enough of the Parrot namespace API that other HLLs will be able to
work with Lua-implemented modules?
4. Ignore extra args to each function (which I'm just guessing is the
purpose of the '.param pmc extra :slurpy' on every sub). Why do
you want to do this?
5. All params are marked :optional (but don't have matching :opt_flag
params) and seem required by the code. Again, why do this?
6. Argument type checking and conversion. This appears to be the "real"
problem, though this seems like the exact kind of problem that Parrot
was supposed to make easier for us. If you have to manually wrap
every function in a cross-language library in every HLL because
Parrot won't Do The Right Thing, that seems like a design flaw.
7. Simplified wrappers around some common functions. I've been thinking
about creating some of these (most OpenGL wrappers for scripting
languages seem to do this, to a greater or lesser degree). Whether
it is worth it to try to do this in runtime/parrot/library/OpenGL.pir
depends on how many HLL implementors are trying to get exact ports
of existing bindings in the original (non-parrot) implementation of
their language, and how many would be willing to share a common
simplified binding.
Anything else I'm missing?
-'f