jingoro2112 / wrench

practical embedded script interpreter
MIT License
99 stars 10 forks source link

WRValue strings, arrays & hash maps access from native code #32

Closed ohordiichuk closed 1 month ago

ohordiichuk commented 1 month ago

Hello,

I'm in the middle of porting wrench to JS (emscripten) and faced with a problem that I CAN NOT:

1) Check if the WRValue is a string (no isString() method). As a workaround I'm currently checking it like this:

bool isString = value->array(&len, SV_CHAR) != NULL

2) Check if the WRValue is array or hash table. All methods - isRawArray, isWrenchArray & isHashTable always return false for arrays and hash tables. P.S. for strings isWrenchArray returns true - not sure if it's expected behavior.

3) Access array values & get its size (number of elements). I tried to overcome it with indexArray check for null hack, but it doesn't work. The code below always returns NULL for value->indexArray

        int i;
        for (i = 0;; i++) {
            wrArrVal = value->indexArray(c, i, false); // currently it's always NULL!!!
            if (wrArrVal == nullptr) {
                break;
            }
             // do something with wrArrVal
        }

        int size = i;

4) Access keys of the hash map. I checked the code of WRValue and see it stores keys as hash values. However key values should exist in WRContext as far as I understand. It would be great if in Native code it would be possible to implement foreach as in wrench. Not sure about possible API, but just an explanation of what I want to achieve:

if (value->isHashMap()) {
     int size = value->hashMapSize();

     for (int i = 0; i < size; ++i) {
         WRValue key, value;
         value->getKeyValue(i, &key, &value);
    }
}
jingoro2112 commented 1 month ago

On Tue, Jun 11, 2024 at 4:45 AM ohordiichuk @.***> wrote:

Hello,

I'm in the middle of porting wrench to JS (emscripten) and faced with a problem that I CAN NOT:

  1. Check if the WRValue is a string (no isString() method). As a workaround I'm currently checking it like this:

bool isString = value->array(&len, SV_CHAR) != NULL

I can come up with something cleaner, look for 5.0.3

2.

Check if the WRValue is array or hash table. All methods - isRawArray, isWrenchArray & isHashTable always return false for arrays and hash tables. P.S. for strings isWrenchArray returns true - not sure if it's expected behavior.

well a string is really a special case of array, I'll visit these and make sure they return correctly.

2. 3.

Access array values & get its size (number of elements). I tried to overcome it with indexArray check for null hack, but it doesn't work. The code below always returns NULL for value->indexArray

    int i;
    for (i = 0;; i++) {
        wrArrVal = value->indexArray(c, i, false); // currently it's always NULL!!!
        if (wrArrVal == nullptr) {
            break;
        }
         // do something with wrArrVal
    }

    int size = I;

Should be an easy fix, sorry for the bugs.

  1. Access keys of the hash map. I checked the code of WRValue and see it stores keys as hash values. However key values should exist in WRContext as far as I understand. It would be great if in Native code it would be possible to implement foreach as in wrench. Not sure about possible API, but just an explanation of what I want to achieve:

if (value->isHashMap()) { int size = value->hashMapSize();

 for (int I = 0; I < size; ++i) {
     WRValue key, value;
     value->getKeyValue(I, &key, &value);
}

}

5.0.2 already does this, the keys were always supposed to be available in a hash table but for some reason were removed in a previous patch.

-Curt

Message ID: @.***>

jingoro2112 commented 1 month ago

sorry, 5.0.1 has the key/value fixed, but I'll make sure the c method reflects that in my next push, which will be some time today

On Tue, Jun 11, 2024 at 8:39 AM Curt Hartung @.***> wrote:

On Tue, Jun 11, 2024 at 4:45 AM ohordiichuk @.***> wrote:

Hello,

I'm in the middle of porting wrench to JS (emscripten) and faced with a problem that I CAN NOT:

  1. Check if the WRValue is a string (no isString() method). As a workaround I'm currently checking it like this:

bool isString = value->array(&len, SV_CHAR) != NULL

I can come up with something cleaner, look for 5.0.3

2.

Check if the WRValue is array or hash table. All methods - isRawArray, isWrenchArray & isHashTable always return false for arrays and hash tables. P.S. for strings isWrenchArray returns true - not sure if it's expected behavior.

well a string is really a special case of array, I'll visit these and make sure they return correctly.

2. 3.

Access array values & get its size (number of elements). I tried to overcome it with indexArray check for null hack, but it doesn't work. The code below always returns NULL for value->indexArray

    int i;
    for (i = 0;; i++) {
        wrArrVal = value->indexArray(c, i, false); // currently it's always NULL!!!
        if (wrArrVal == nullptr) {
            break;
        }
         // do something with wrArrVal
    }

    int size = I;

Should be an easy fix, sorry for the bugs.

  1. Access keys of the hash map. I checked the code of WRValue and see it stores keys as hash values. However key values should exist in WRContext as far as I understand. It would be great if in Native code it would be possible to implement foreach as in wrench. Not sure about possible API, but just an explanation of what I want to achieve:

if (value->isHashMap()) { int size = value->hashMapSize();

 for (int I = 0; I < size; ++i) {
     WRValue key, value;
     value->getKeyValue(I, &key, &value);
}

}

5.0.2 already does this, the keys were always supposed to be available in a hash table but for some reason were removed in a previous patch.

-Curt

Message ID: @.***>

jingoro2112 commented 1 month ago

5.0.2 has all these issues fixed, including some new functionality that should make getting array sizes easier:

bool isString( int* len =0 ) const;
bool isWrenchArray( int* len =0 ) const;
bool isRawArray( int* len =0 ) const;

void* array( unsigned int* len =0, char arrayType =SV_CHAR ) const;
int arrayLen() const; // returns length of the array or -1 if this value is not an array
jingoro2112 commented 1 month ago

oops I didn't see the "foreach" part I'll work on that

On Tue, Jun 11, 2024 at 8:42 AM Curt Hartung @.***> wrote:

sorry, 5.0.1 has the key/value fixed, but I'll make sure the c method reflects that in my next push, which will be some time today

On Tue, Jun 11, 2024 at 8:39 AM Curt Hartung @.***> wrote:

On Tue, Jun 11, 2024 at 4:45 AM ohordiichuk @.***> wrote:

Hello,

I'm in the middle of porting wrench to JS (emscripten) and faced with a problem that I CAN NOT:

  1. Check if the WRValue is a string (no isString() method). As a workaround I'm currently checking it like this:

bool isString = value->array(&len, SV_CHAR) != NULL

I can come up with something cleaner, look for 5.0.3

2.

Check if the WRValue is array or hash table. All methods - isRawArray, isWrenchArray & isHashTable always return false for arrays and hash tables. P.S. for strings isWrenchArray returns true - not sure if it's expected behavior.

well a string is really a special case of array, I'll visit these and make sure they return correctly.

2. 3.

Access array values & get its size (number of elements). I tried to overcome it with indexArray check for null hack, but it doesn't work. The code below always returns NULL for value->indexArray

    int i;
    for (i = 0;; i++) {
        wrArrVal = value->indexArray(c, i, false); // currently it's always NULL!!!
        if (wrArrVal == nullptr) {
            break;
        }
         // do something with wrArrVal
    }

    int size = I;

Should be an easy fix, sorry for the bugs.

  1. Access keys of the hash map. I checked the code of WRValue and see it stores keys as hash values. However key values should exist in WRContext as far as I understand. It would be great if in Native code it would be possible to implement foreach as in wrench. Not sure about possible API, but just an explanation of what I want to achieve:

if (value->isHashMap()) { int size = value->hashMapSize();

 for (int I = 0; I < size; ++i) {
     WRValue key, value;
     value->getKeyValue(I, &key, &value);
}

}

5.0.2 already does this, the keys were always supposed to be available in a hash table but for some reason were removed in a previous patch.

-Curt

Message ID: @.***>

ohordiichuk commented 1 month ago

Hello, thank you I've checked it's working. So I'll be waiting for "foreach" part.

Apart from it, another question related to this has arised:

How can I create Wrench Array inside stack, so I can pass it for example as function arguments, e.g.

WRValue *array = wr_makeArray(c, size); // ???
....

wr_callFunction(c, "myfunc", array, 1); // please note I'm not passing "array" as C array here, but as wrench array.

P.S. how do you want us to release JS port (emscripten)? I mean:

a) I can make a pull-request so it will live in a separate directory of this repository, so anyone downloading wrench library could also get "wrench-js" b) "wrench-js" should be a separate project that has this library as a submodule (this is the way it's now)

It's almost complete right now. I'm just waiting for "foreach" and wr_makeArray (or workaround) and it will have full (almost) JS object <-> WR object binding between languages. So, for example, it's possible to run wrench from browser. We plan to use it as a simulator of scripts for our hardware in our project.

jingoro2112 commented 1 month ago

foreach() functionality is complete in 6.0.0 I added iteration for arrays and hashes:

struct WRIteratorEntry { int type; // SV_VALUE, SV_CHAR or SV_HASH_TABLE

const WRValue* key; // if this is a hash table, the key value
const WRValue* value; // for SV_HASH_TABLE and SV_VALUE (character will

be null)

int index; // array entry for non-hash tables
char character; // for SV_CHAR

};

// requires c++11 and above, which supports foreach() syntax: for( WRIteratorEntry const& member : *argv ) { // member provided here, as defined above }

// for c++98: for ( WRValue::Iterator it = argv->begin(); it != argv->end(); ++it ) { WRIteratorEntry const& member = *it;

// member is always valid here

}

wow a JS port? I would love to add that to the project! I think it would be easiest to link to it in the README and website. Probably easier that way you can maintain the archive yourself and not need my help for updates.

I have a couple of other outstanding bug regarding an odd case of string concatenation with global/local variables. All this activity is finding some of the obscure bugs in the corners. I am trying to get them resolved as quickly as possible though, please let me know if you have any blockers.

-Curt

On Tue, Jun 11, 2024 at 12:31 PM ohordiichuk @.***> wrote:

Hello, thank you I've checked it's working. So I'll be waiting for "foreach" part.

Apart from it, another question related to this has arised:

How can I create Wrench Array inside stack, so I can pass it for example as function arguments, e.g.

WRValue *array = wr_makeArray(c, size); // ??? .... wr_callFunction(c, "myfunc", array, 1); // please note I'm not passing "array" as C array here, but as wrench array.

P.S. how do you want us to release JS port (emscripten)? I mean:

a) I can make a pull-request so it will live in a separate directory of this repository, so anyone downloading wrench library could also get "wrench-js" b) "wrench-js" should be a separate project that has this library as a submodule (this is the way it's now)

It's almost complete right now. I'm just waiting for "foreach" and wr_makeArray (or workaround) and it will have full (almost) JS object <-> WR object binding between languages. So, for example, it's possible to run wrench from browser. We plan to use it as a simulator of scripts for our hardware in our project.

— Reply to this email directly, view it on GitHub https://github.com/jingoro2112/wrench/issues/32#issuecomment-2161175989, or unsubscribe https://github.com/notifications/unsubscribe-auth/AALIKAZZMY5CWJU72TDVIIDZG4Q6ZAVCNFSM6AAAAABJD3KY56VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDCNRRGE3TKOJYHE . You are receiving this because you modified the open/close state.Message ID: @.***>

jingoro2112 commented 1 month ago

foreach() / iterator functionality part of 6.0.0 update

jingoro2112 commented 1 month ago

this already exists, you can create a value on the stack and then use these methods (context required because of memory management) to pass it in. I don't THINK this causes problems with GC as long as you don't touch the memory again after you pass it in.

class WRValue
{

    // if this value is an array, return 'index'-th element
    // if create is true and this value is NOT an array, it will be converted into one
    WRValue* indexArray( WRContext* context, const uint32_t index, const bool create );

    // if this value is a hash table, return [or create] the 'index' hash item
    // if create is true and this value is NOT a hash, it will be converted into one
    WRValue* indexHash( WRContext* context, const uint32_t hash, const bool create );

};

ie:

WRValue argument;
argument.indexArray( c, 0, true )->setInt( 10 ); // element 0 is 10
argument.indexArray( c, 1, true )->setInt( 20 ); // element 1 is 20
argument.indexArray( c, 2, true )->setFloat( 10.05f ); // element 3 is 10.05
ohordiichuk commented 1 month ago

Thank you Curt for all the efforts and replies! I'll try all new features soon, but it seems it covers everything I need at the moment. I'll do a separate repo for wrench-js and once it's ready for releasing I'll let you know!