root/src/bifs/script_enc.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. EncScriptFields
  2. SFE_GetNumber
  3. SFE_NextToken
  4. SFE_CheckToken
  5. SFE_PutIdentifier
  6. SFE_Arguments
  7. SFE_PutInteger
  8. SFE_LoadExpression
  9. SFE_ScanExpression
  10. SFE_CompoundExpression
  11. SFE_OptionalExpression
  12. SFE_IfStatement
  13. SFE_ForStatement
  14. SFE_WhileStatement
  15. SFE_ReturnStatement
  16. SFE_CaseBlock
  17. SFE_PutCaseInteger
  18. SFE_SwitchStatement
  19. SFE_Statement
  20. SFE_Statements
  21. SFE_StatementBlock
  22. SFE_Function
  23. SFScript_Encode
  24. SFE_PutReal
  25. SFE_PutNumber
  26. SFE_PutBoolean
  27. TOK_To_ET
  28. MoveToToken
  29. SFE_Expression
  30. SFE_FunctionCall
  31. SFE_ObjectMemberAccess
  32. SFE_ObjectMethodCall
  33. SFE_ArrayDereference
  34. SFE_ObjectConstruct
  35. SFE_ConditionTest
  36. SFE_Params

/*
 *                      GPAC - Multimedia Framework C SDK
 *
 *                      Authors: Jean Le Feuvre
 *                      Copyright (c) Telecom ParisTech 2000-2012
 *                                      All rights reserved
 *
 *  This file is part of GPAC / BIFS codec sub-project
 *
 *  GPAC is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *
 *  GPAC is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

/*
                script encoder is IM1 version
*/

#include "script.h"

#include <gpac/internal/scenegraph_dev.h>

#if !defined(GPAC_DISABLE_BIFS) && !defined(GPAC_DISABLE_BIFS_ENC) && defined(GPAC_HAS_SPIDERMONKEY)

typedef struct
{
        GF_Node *script;
        GF_BifsEncoder *codec;
        GF_BitStream *bs;
        GF_List *identifiers;
        GF_Err err;

        char *cur_buf;
        char token[500];
        u32 token_code;

        u32 cur_line;

        Bool emul;

        char expr_toks[500];
        u32 expr_toks_len;
        GF_List *id_buf;
} ScriptEnc;


#define SFE_WRITE_INT(sc_enc, val, nbBits, str1, str2)  \
                if (!sc_enc->emul) GF_BIFS_WRITE_INT(sc_enc->codec, sc_enc->bs, val, nbBits, str1, str2);       \
 

static GF_Err EncScriptFields(ScriptEnc *sc_enc)
{
        u32 nbFields, nbBits, eType, nbBitsProto, i;
        Bool use_list;
        GF_Err e;
        GF_FieldInfo info;

        nbFields = gf_node_get_num_fields_in_mode(sc_enc->script, GF_SG_FIELD_CODING_ALL) - 3;
        use_list = GF_TRUE;
        nbBits = gf_get_bit_size(nbFields);
        if (nbFields+1 > 4 + gf_get_bit_size(nbFields)) use_list = GF_FALSE;
        if (!nbFields) {
                GF_BIFS_WRITE_INT(sc_enc->codec, sc_enc->bs, 1, 1, "Script::isList", NULL);
                GF_BIFS_WRITE_INT(sc_enc->codec, sc_enc->bs, 1, 1, "end", NULL);
                return GF_OK;
        }
        GF_BIFS_WRITE_INT(sc_enc->codec, sc_enc->bs, use_list ? 1 : 0, 1, "Script::isList", NULL);
        if (!use_list) {
                GF_BIFS_WRITE_INT(sc_enc->codec, sc_enc->bs, nbBits, 4, "nbBits", NULL);
                GF_BIFS_WRITE_INT(sc_enc->codec, sc_enc->bs, nbFields, nbBits, "count", NULL);
        }

        nbBitsProto = 0;
        if (sc_enc->codec->encoding_proto) nbBitsProto = gf_get_bit_size(gf_sg_proto_get_field_count(sc_enc->codec->encoding_proto) - 1);

        for (i=0; i<nbFields; i++) {
                if (use_list) GF_BIFS_WRITE_INT(sc_enc->codec, sc_enc->bs, 0, 1, "end", NULL);

                gf_node_get_field(sc_enc->script, i+3, &info);
                switch (info.eventType) {
                case GF_SG_EVENT_IN:
                        eType = GF_SG_SCRIPT_TYPE_EVENT_IN;
                        break;
                case GF_SG_EVENT_OUT:
                        eType = GF_SG_SCRIPT_TYPE_EVENT_OUT;
                        break;
                default:
                        eType = GF_SG_SCRIPT_TYPE_FIELD;
                        break;
                }
                GF_BIFS_WRITE_INT(sc_enc->codec, sc_enc->bs, eType, 2, "eventType", NULL);
                GF_BIFS_WRITE_INT(sc_enc->codec, sc_enc->bs, info.fieldType, 6, "fieldType", NULL);
                gf_bifs_enc_name(sc_enc->codec, sc_enc->bs, (char *) info.name);
                /*this is an identifier for script*/
                gf_list_add(sc_enc->identifiers, gf_strdup(info.name));

                if (sc_enc->codec->encoding_proto) {
                        GF_Route *isedField = gf_bifs_enc_is_field_ised(sc_enc->codec, sc_enc->script, i+3);
                        if (isedField) {
                                GF_BIFS_WRITE_INT(sc_enc->codec, sc_enc->bs, 1, 1, "isedField", NULL);

                                if (isedField->ToNode == sc_enc->script) {
                                        GF_BIFS_WRITE_INT(sc_enc->codec, sc_enc->bs, isedField->FromField.fieldIndex, nbBitsProto, "protoField", NULL);
                                } else {
                                        GF_BIFS_WRITE_INT(sc_enc->codec, sc_enc->bs, isedField->ToField.fieldIndex, nbBitsProto, "protoField", NULL);
                                }
                                continue;
                        }
                        GF_BIFS_WRITE_INT(sc_enc->codec, sc_enc->bs, 0, 1, "isedField", NULL);
                }
                /*default value*/
                if (eType == GF_SG_SCRIPT_TYPE_FIELD) {
                        GF_BIFS_WRITE_INT(sc_enc->codec, sc_enc->bs, (info.far_ptr) ? 1 : 0, 1, "hasInitialValue", NULL);
                        if (info.far_ptr) {
                                e = gf_bifs_enc_field(sc_enc->codec, sc_enc->bs, sc_enc->script, &info);
                                if (e) return e;
                        }
                }
        }
        if (use_list) GF_BIFS_WRITE_INT(sc_enc->codec, sc_enc->bs, 1, 1, "end", NULL);
        return GF_OK;
}



enum
{
        TOK_FUNCTION,
        TOK_IF,
        TOK_ELSE,
        TOK_FOR,
        TOK_WHILE,
        TOK_RETURN,
        TOK_BREAK,
        TOK_CONTINUE,
        TOK_NEW,
        TOK_SWITCH,
        TOK_CASE,
        TOK_DEFAULT,
        TOK_VAR,
        NUMBER_OF_KEYWORD,
        TOK_LEFT_BRACE = NUMBER_OF_KEYWORD,
        TOK_RIGHT_BRACE,
        TOK_LEFT_CURVE,
        TOK_RIGHT_CURVE,
        TOK_LEFT_BRACKET,
        TOK_RIGHT_BRACKET,
        TOK_PERIOD,
        TOK_NOT,
        TOK_ONESCOMP,
        TOK_NEGATIVE,
        TOK_INCREMENT,
        TOK_DECREMENT,
        TOK_MULTIPLY,
        TOK_DIVIDE,
        TOK_MOD,
        TOK_PLUS,
        TOK_MINUS,
        TOK_LSHIFT,
        TOK_RSHIFT,
        TOK_RSHIFTFILL,
        TOK_LT,
        TOK_LE,
        TOK_GT,
        TOK_GE,
        TOK_EQ,
        TOK_NE,
        TOK_AND,
        TOK_XOR,
        TOK_OR,
        TOK_LAND,
        TOK_LOR,
        TOK_CONDTEST,
        TOK_ASSIGN,
        TOK_PLUSEQ,
        TOK_MINUSEQ,
        TOK_MULTIPLYEQ,
        TOK_DIVIDEEQ,
        TOK_MODEQ,
        TOK_LSHIFTEQ,
        TOK_RSHIFTEQ,
        TOK_RSHIFTFILLEQ,
        TOK_ANDEQ,
        TOK_XOREQ,
        TOK_OREQ,
        TOK_COMMA,
        TOK_SEMICOLON,
        TOK_CONDSEP,
        TOK_IDENTIFIER,
        TOK_STRING,
        TOK_NUMBER,
        TOK_EOF,
        TOK_BOOLEAN
};

const char *tok_names[] =
{
        "function",
        "if",
        "else",
        "for",
        "while",
        "return",
        "break",
        "continue",
        "new",
        "switch",
        "case",
        "default",
        "var",
        "{",
        "}",
        "(",
        ")",
        "[",
        "]",
        ".",
        "!",
        "~",
        "-",
        "++",
        "--",
        "*",
        "/",
        "%",
        "+",
        "-",
        "<<",
        ">>",
        ">>>",
        "<",
        "<=",
        ">",
        ">=",
        "==",
        "!=",
        "&",
        "^",
        "|",
        "&&",
        "||",
        "?",
        "=",
        "+=",
        "-=",
        "*=",
        "/=",
        "%=",
        "<<=",
        ">>=",
        ">>>=",
        "&=",
        "^=",
        "|=",
        ",",
        ";",
        ":",
        "identifier",
        "string",
        "number",
        "boolean",
        "end of script"
};

const char* sc_keywords [] =
{
        "function",
        "if",
        "else",
        "for",
        "while",
        "return",
        "break",
        "continue",
        "new",
        "switch",
        "case",
        "default",
        "var"
};

Bool SFE_GetNumber(ScriptEnc *sc_enc)
{
        u32 i = 0;
        Bool exp = GF_FALSE;
        while ( isdigit(sc_enc->cur_buf[i])
                || (toupper(sc_enc->cur_buf[i])=='X')
                || ((toupper(sc_enc->cur_buf[i]) >='A') && (toupper(sc_enc->cur_buf[i])<='F'))
                || (sc_enc->cur_buf[i]=='.')
                || (tolower(sc_enc->cur_buf[i])=='e')
                || (exp && (sc_enc->cur_buf[i] == '-'))
              ) {
                sc_enc->token[i] = sc_enc->cur_buf[i];
                if (tolower(sc_enc->cur_buf[i])=='e') exp = GF_TRUE;
                i++;
                if (!sc_enc->cur_buf[i]) {
                        GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[bifs] Script encoding: Invalid number syntax (%s)\n", sc_enc->cur_buf));
                        sc_enc->err = GF_BAD_PARAM;
                        return GF_FALSE;
                }
        }
        sc_enc->token[i] = 0;
        sc_enc->cur_buf += i;
        sc_enc->token_code = TOK_NUMBER;
        return GF_TRUE;
}

Bool SFE_NextToken(ScriptEnc *sc_enc)
{
        u32 i;
        if (sc_enc->err) return GF_FALSE;
        while (strchr(" \t\r\n", sc_enc->cur_buf[0])) {
                if (sc_enc->cur_buf[0]=='\n') sc_enc->cur_line ++;
                sc_enc->cur_buf++;
        }
        if ((sc_enc->cur_buf[0] == '/') && (sc_enc->cur_buf[1] == '*')) {
                sc_enc->cur_buf += 2;
                while ((sc_enc->cur_buf[0] != '*') || (sc_enc->cur_buf[1] != '/')) {
                        sc_enc->cur_buf++;
                        if (!sc_enc->cur_buf[0] || !sc_enc->cur_buf[1]) {
                                GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[bifs] Script encoding: cannot find closing comment */\n"));
                                sc_enc->err = GF_BAD_PARAM;
                                return GF_FALSE;
                        }
                }
                sc_enc->cur_buf+=2;
                return SFE_NextToken(sc_enc);
        }
        i = 0;
        /*get a name*/
        if (isalpha(sc_enc->cur_buf[i]) || (sc_enc->cur_buf[i]=='_')) {
                while (isalnum(sc_enc->cur_buf[i]) || (sc_enc->cur_buf[i]=='_')) {
                        sc_enc->token[i] = sc_enc->cur_buf[i];
                        i++;
                }
                sc_enc->token[i] = 0;
                sc_enc->cur_buf += i;
                sc_enc->token_code = TOK_IDENTIFIER;
                /*check keyword*/
                for (i=0; i<NUMBER_OF_KEYWORD; i++) {
                        if (!strcmp(sc_enc->token, sc_keywords[i])) {
                                sc_enc->token_code = i;
                                return GF_TRUE;
                        }
                }
                if (!stricmp(sc_enc->token, "TRUE") || !stricmp(sc_enc->token, "FALSE") ) {
                        sc_enc->token_code = TOK_BOOLEAN;
                }
                return GF_TRUE;
        }
        /*get a number*/
        if (isdigit(sc_enc->cur_buf[i])) return SFE_GetNumber(sc_enc);
        /*get a string*/
        if ((sc_enc->cur_buf[i]=='\'') || (sc_enc->cur_buf[i]=='\"')
                || ((sc_enc->cur_buf[i]=='\\') && (sc_enc->cur_buf[i+1]=='\"'))
           ) {
                char end;
                Bool skip_last = GF_FALSE;
                end = sc_enc->cur_buf[i];
                if (sc_enc->cur_buf[i]=='\\') {
                        skip_last = GF_TRUE;
                        sc_enc->cur_buf++;
                }
                while (sc_enc->cur_buf[i+1] != end) {
                        sc_enc->token[i] = sc_enc->cur_buf[i+1];
                        i++;
                }
                sc_enc->token[i] = 0;
                sc_enc->cur_buf += i+2;
                if (skip_last) sc_enc->cur_buf++;
                sc_enc->token_code = TOK_STRING;
                return GF_TRUE;
        }
        /*all other codes*/
        switch (sc_enc->cur_buf[i]) {
        case '.':
                if (isdigit(sc_enc->cur_buf[i+1])) {
                        SFE_GetNumber(sc_enc);
                        return GF_TRUE;
                } else {
                        sc_enc->token_code = TOK_PERIOD;
                }
                break;
        case '!':
                if (sc_enc->cur_buf[i+1] == '=') {
                        sc_enc->token_code = TOK_NE;
                        sc_enc->cur_buf ++;
                } else {
                        sc_enc->token_code = TOK_NOT;
                }
                break;
        case '=':
                if (sc_enc->cur_buf[i+1]=='=') {
                        if (sc_enc->cur_buf[i+2]=='=') {
                                GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[BIFSEnc] JavaScript token '===' not supported by standard\n"));
                                sc_enc->err = GF_NOT_SUPPORTED;
                                return GF_FALSE;
                        }
                        sc_enc->token_code = TOK_EQ;
                        sc_enc->cur_buf ++;
                } else {
                        sc_enc->token_code = TOK_ASSIGN;
                }
                break;
        case '+':
                if(sc_enc->cur_buf[i+1]=='=') {
                        sc_enc->token_code = TOK_PLUSEQ;
                        sc_enc->cur_buf ++;
                } else if(sc_enc->cur_buf[i+1]=='+') {
                        sc_enc->token_code = TOK_INCREMENT;
                        sc_enc->cur_buf++;
                } else {
                        sc_enc->token_code = TOK_PLUS;
                }
                break;
        case '-':
                if(sc_enc->cur_buf[i+1]=='=') {
                        sc_enc->token_code = TOK_MINUSEQ;
                        sc_enc->cur_buf++;
                } else if(sc_enc->cur_buf[i+1] == '-') {
                        sc_enc->token_code = TOK_DECREMENT;
                        sc_enc->cur_buf++;
                } else {
                        sc_enc->token_code = TOK_MINUS;
                }
                break;
        case '*':
                if(sc_enc->cur_buf[i+1]=='=') {
                        sc_enc->token_code = TOK_MULTIPLYEQ;
                        sc_enc->cur_buf++;
                } else {
                        sc_enc->token_code = TOK_MULTIPLY;
                }
                break;
        case '/':
                if(sc_enc->cur_buf[i+1]=='=') {
                        sc_enc->token_code = TOK_DIVIDEEQ;
                        sc_enc->cur_buf++;
                } else {
                        sc_enc->token_code = TOK_DIVIDE;
                }
                break;
        case '%':
                if(sc_enc->cur_buf[i+1]=='=') {
                        sc_enc->token_code = TOK_MODEQ;
                        sc_enc->cur_buf++;
                } else {
                        sc_enc->token_code = TOK_MOD;
                }
                break;
        case '&':
                if(sc_enc->cur_buf[i+1]=='=') {
                        sc_enc->token_code = TOK_ANDEQ;
                        sc_enc->cur_buf++;
                } else if(sc_enc->cur_buf[i+1]=='&') {
                        sc_enc->token_code = TOK_LAND;
                        sc_enc->cur_buf++;
                } else {
                        sc_enc->token_code = TOK_AND;
                }
                break;
        case '|':
                if(sc_enc->cur_buf[i+1]=='=') {
                        sc_enc->token_code = TOK_OREQ;
                        sc_enc->cur_buf++;
                } else if(sc_enc->cur_buf[i+1]=='|') {
                        sc_enc->token_code = TOK_LOR;
                        sc_enc->cur_buf++;
                } else {
                        sc_enc->token_code = TOK_OR;
                }
                break;
        case '^':
                if(sc_enc->cur_buf[i+1]=='=') {
                        sc_enc->token_code = TOK_XOREQ;
                        sc_enc->cur_buf++;
                } else {
                        sc_enc->token_code = TOK_XOR;
                }
                break;
        case '<':
                if (sc_enc->cur_buf[i+1]=='<') {
                        if(sc_enc->cur_buf[i+2]=='=') {
                                sc_enc->token_code = TOK_LSHIFTEQ;
                                sc_enc->cur_buf += 1;
                        } else {
                                sc_enc->token_code = TOK_LSHIFT;
                        }
                        sc_enc->cur_buf += 1;
                } else if(sc_enc->cur_buf[i+1]=='=') {
                        sc_enc->token_code = TOK_LE;
                        sc_enc->cur_buf ++;
                } else {
                        sc_enc->token_code = TOK_LT;
                }
                break;
        case '>':
                if (sc_enc->cur_buf[i+1]=='>') {
                        if (sc_enc->cur_buf[i+2]=='=') {
                                sc_enc->token_code = TOK_RSHIFTEQ;
                                sc_enc->cur_buf ++;
                        } else if(sc_enc->cur_buf[i+2]=='>') {
                                if(sc_enc->cur_buf[i+3]=='=') {
                                        sc_enc->token_code = TOK_RSHIFTFILLEQ;
                                        sc_enc->cur_buf ++;
                                } else {
                                        sc_enc->token_code = TOK_RSHIFTFILL;
                                }
                                sc_enc->cur_buf ++;
                        } else {
                                sc_enc->token_code = TOK_RSHIFT;
                        }
                        sc_enc->cur_buf ++;
                } else if (sc_enc->cur_buf[i+1]=='=') {
                        sc_enc->token_code = TOK_GE;
                        sc_enc->cur_buf ++;
                } else {
                        sc_enc->token_code = TOK_GT;
                }
                break;
        case '?':
                sc_enc->token_code = TOK_CONDTEST;
                break;
        case ':':
                sc_enc->token_code = TOK_CONDSEP;
                break;
        case '~':
                sc_enc->token_code = TOK_ONESCOMP;
                break;
        case ',':
                sc_enc->token_code = TOK_COMMA;
                break;
        case ';':
                sc_enc->token_code = TOK_SEMICOLON;
                break;
        case '{':
                sc_enc->token_code = TOK_LEFT_BRACE;
                break;
        case '}':
                sc_enc->token_code = TOK_RIGHT_BRACE;
                break;
        case '(':
                sc_enc->token_code = TOK_LEFT_CURVE;
                break;
        case ')':
                sc_enc->token_code = TOK_RIGHT_CURVE;
                break;
        case '[':
                sc_enc->token_code = TOK_LEFT_BRACKET;
                break;
        case ']':
                sc_enc->token_code = TOK_RIGHT_BRACKET;
                break;
        default:
                GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[bifs] Script encoding: Unrecognized symbol %c\n", sc_enc->cur_buf[i]));
                sc_enc->err = GF_BAD_PARAM;
                return GF_FALSE;
        }
        sc_enc->cur_buf ++;
        return GF_TRUE;
}

Bool SFE_CheckToken(ScriptEnc *sc_enc, u32 token)
{
        if (sc_enc->token_code != token) {
                GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[bifs] Script encoding: Bad token (expecting \"%s\" got \"%s\")\n", tok_names[token] , tok_names[sc_enc->token_code]));
                return GF_FALSE;
        }
        return GF_TRUE;
}

void SFE_PutIdentifier(ScriptEnc *sc_enc, char *id)
{
        u32 i;
        u32 nbBits, length;
        char *str;

        if (sc_enc->emul) return;

        i=0;
        while ((str = (char *)gf_list_enum(sc_enc->identifiers, &i))) {
                if (strcmp(str, id)) continue;

                nbBits = 0;
                length = gf_list_count(sc_enc->identifiers) - 1;
                while (length > 0) {
                        length >>= 1;
                        nbBits ++;
                }
                GF_BIFS_WRITE_INT(sc_enc->codec, sc_enc->bs, 1, 1, "received", str);
                GF_BIFS_WRITE_INT(sc_enc->codec, sc_enc->bs, i-1, nbBits, "identifierCode", str);
                return;
        }
        GF_BIFS_WRITE_INT(sc_enc->codec, sc_enc->bs, 0, 1, "received", id);
        gf_list_add(sc_enc->identifiers, gf_strdup(id));
        gf_bifs_enc_name(sc_enc->codec, sc_enc->bs, id);
}


void SFE_Arguments(ScriptEnc *sc_enc)
{
        while (1) {
                if (!SFE_NextToken(sc_enc)) return;
                if (sc_enc->token_code == TOK_RIGHT_CURVE) break;
                else if (sc_enc->token_code == TOK_COMMA) continue;
                GF_BIFS_WRITE_INT(sc_enc->codec, sc_enc->bs, 1, 1, "hasArgument", NULL);
                SFE_PutIdentifier(sc_enc, sc_enc->token);
        }
        GF_BIFS_WRITE_INT(sc_enc->codec, sc_enc->bs, 0, 1, "hasArgument", NULL);
}

void SFE_StatementBlock(ScriptEnc *sc_enc);
void SFE_Statement(ScriptEnc *sc_enc);

void SFE_PutInteger(ScriptEnc *sc_enc, char *str)
{
        u32 nbBits, val = 0;
        if (sc_enc->emul) return;
        if ((str[0]=='0') && (str[1]=='x' || str[1]=='X')) {
                val = (u32) strtoul(sc_enc->token, (char **) NULL, 16);
        } else if (str[0]=='0' && isdigit(str[1])) {
                val = (u32) strtoul(str, (char **) NULL, 8);
        } else if (isdigit(str[0])) {
                val = (u32) strtoul(str, (char **) NULL, 10);
        } else {
                GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[bifs] Script encoding: %s is not an integer\n", str));
                sc_enc->err = GF_BAD_PARAM;
                return;
        }
        nbBits = gf_get_bit_size(val);
        GF_BIFS_WRITE_INT(sc_enc->codec, sc_enc->bs, nbBits, 5, "nbBitsInteger", NULL);
        GF_BIFS_WRITE_INT(sc_enc->codec, sc_enc->bs, val, nbBits, "value", sc_enc->token);
}

u32 SFE_LoadExpression(ScriptEnc *sc_enc, u32 *expr_sep)
{
        Bool is_var = GF_FALSE;
        u32 close_code, open_code;
        u32 count = 0;
        u32 nbExpr = 1;
        u32 nbIndir = 0;
        expr_sep[0] = 0;

        sc_enc->expr_toks_len = 0;

        while ( (sc_enc->token_code != TOK_SEMICOLON) && (sc_enc->token_code != TOK_RIGHT_CURVE) ) {
                switch (sc_enc->token_code) {
                case TOK_CONDTEST:
                        nbIndir ++;
                        break;
                case TOK_CONDSEP:
                        if (nbIndir > 0) nbIndir--;
                        /*'case'*/
                        else {
                                goto break_loop;
                        }
                        break;
                case TOK_IDENTIFIER:
                case TOK_NUMBER:
                case TOK_STRING:
                case TOK_BOOLEAN:
                        gf_list_add(sc_enc->id_buf, gf_strdup(sc_enc->token));
                        break;
                case TOK_FUNCTION:
                        goto break_loop;
                }

                if (sc_enc->token_code==TOK_VAR) is_var = GF_TRUE;
                if (!is_var || (sc_enc->token_code!=TOK_COMMA)) {
                        sc_enc->expr_toks[sc_enc->expr_toks_len] = sc_enc->token_code;
                        sc_enc->expr_toks_len++;
                }

                open_code = sc_enc->token_code;
                close_code = 0;
                if (sc_enc->token_code == TOK_LEFT_CURVE) close_code = TOK_RIGHT_CURVE;
                else if (sc_enc->token_code == TOK_LEFT_BRACKET) close_code = TOK_RIGHT_BRACKET;
                else if (sc_enc->token_code == TOK_LEFT_BRACE) close_code = TOK_RIGHT_BRACE;

                /*other expr*/
                if ((sc_enc->token_code == TOK_COMMA) && (sc_enc->expr_toks[0] != TOK_VAR) ) {
                        expr_sep[nbExpr++] = sc_enc->expr_toks_len - 1;
                }
                /*sub-expr*/
                else if (close_code) {
                        count++;
                        do {
                                SFE_NextToken(sc_enc);
                                if ((sc_enc->token_code == TOK_IDENTIFIER) || (sc_enc->token_code == TOK_NUMBER)
                                        || (sc_enc->token_code == TOK_STRING) || (sc_enc->token_code == TOK_BOOLEAN) ) {
                                        gf_list_add(sc_enc->id_buf, gf_strdup(sc_enc->token));
                                }
                                sc_enc->expr_toks[sc_enc->expr_toks_len] = sc_enc->token_code;
                                sc_enc->expr_toks_len++;
                                if (sc_enc->token_code == open_code) count++;
                                else if (sc_enc->token_code == close_code) count--;
                        } while ( (sc_enc->token_code != close_code) || count);
                }
                SFE_NextToken(sc_enc);

                if (sc_enc->err) break;
        }

break_loop:
        if (sc_enc->err) {
                GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[bifs] Script encoding: end of compoundExpression not found\n"));
                return 0;
        }
        expr_sep[nbExpr] = sc_enc->expr_toks_len;
        if ((sc_enc->token_code == TOK_IDENTIFIER) || (sc_enc->token_code == TOK_NUMBER)
                || (sc_enc->token_code == TOK_STRING) || (sc_enc->token_code == TOK_BOOLEAN) ) {
                gf_list_add(sc_enc->id_buf, gf_strdup(sc_enc->token));
        }

        if ((sc_enc->token_code != TOK_CONDSEP) && (sc_enc->token_code != TOK_RIGHT_BRACE) && (sc_enc->expr_toks[0] != TOK_VAR)) {
                sc_enc->expr_toks[sc_enc->expr_toks_len] = sc_enc->token_code;
                sc_enc->expr_toks_len++;
        }
        return nbExpr;
}

u32 MoveToToken(ScriptEnc *sc_enc, u32 endTok, u32 cur, u32 end);

u32 SFE_ScanExpression(ScriptEnc *sc_enc, u32 start, u32 end, u32 *expr_sep)
{
        u32 curTok;
        u32 n = start;
        u32 nbExpr = 1;

        expr_sep[0] = start;
        while (n<end) {
                curTok = sc_enc->expr_toks[n++];
                if (curTok == TOK_LEFT_CURVE) {
                        n = MoveToToken(sc_enc, TOK_RIGHT_CURVE, n-1, end);
                        n++;
                } else if (curTok == TOK_LEFT_BRACKET) {
                        n = MoveToToken(sc_enc, TOK_RIGHT_BRACKET, n-1, end);
                        n++;
                } else if (curTok == TOK_COMMA) {
                        expr_sep[nbExpr++] = n-1;
                }
        }
        expr_sep[nbExpr] = end;
        return nbExpr;
}

u32 SFE_Expression(ScriptEnc *sc_enc, u32 start, u32 end, Bool memberAccess);

void SFE_CompoundExpression(ScriptEnc *sc_enc, u32 start, u32 end, u32 isPar)
{
        u32 nbExp, i;
        /*filled by indexes of ',' expressions in the expr_tok buffer*/
        u32 expr_sep[MAX_NUM_EXPR];

        if (sc_enc->err) return;

        if (end==0) {
                /*load expressions , eg "a ? ((a>b) ? 1 : 0) : 0" */
                nbExp = SFE_LoadExpression(sc_enc, expr_sep);
        } else {
                /*load sub-expression from loaded expression set*/
                nbExp = SFE_ScanExpression(sc_enc, start, end, expr_sep);
        }

        SFE_Expression(sc_enc, expr_sep[0], expr_sep[1], GF_FALSE);
        for (i=1; i<nbExp; i++) {
                SFE_WRITE_INT(sc_enc, 1, 1, isPar ? "hasParam" : "hasExpression", NULL);
                SFE_Expression(sc_enc, expr_sep[i]+1, expr_sep[i+1], GF_FALSE);
        }
        SFE_WRITE_INT(sc_enc, 0, 1, isPar ? "hasParam" : "hasExpression", NULL);
}

void SFE_OptionalExpression(ScriptEnc *sc_enc)
{
        if (sc_enc->token_code != TOK_SEMICOLON) {
                SFE_WRITE_INT(sc_enc, 1, 1, "hasCompoundExpression", NULL);
                SFE_CompoundExpression(sc_enc, 0, 0, 0);
        } else {
                SFE_WRITE_INT(sc_enc, 0, 1, "hasCompoundExpression", NULL);
        }
}

void SFE_IfStatement(ScriptEnc *sc_enc)
{
        char *buf_bck;
        u32 tok_bck;
        SFE_NextToken(sc_enc);
        SFE_CheckToken(sc_enc, TOK_LEFT_CURVE);
        SFE_NextToken(sc_enc);
        SFE_CompoundExpression(sc_enc, 0, 0, 0);
        SFE_CheckToken(sc_enc, TOK_RIGHT_CURVE);
        SFE_StatementBlock(sc_enc);

        buf_bck = sc_enc->cur_buf;
        tok_bck = sc_enc->token_code;
        SFE_NextToken(sc_enc);
        if (sc_enc->token_code == TOK_ELSE) {
                SFE_WRITE_INT(sc_enc, 1, 1, "hasELSEStatement", NULL);
                SFE_StatementBlock(sc_enc);
        } else {
                SFE_WRITE_INT(sc_enc, 0, 1, "hasELSEStatement", NULL);
                sc_enc->cur_buf = buf_bck;
                sc_enc->token_code = tok_bck;
        }
}

void SFE_ForStatement(ScriptEnc *sc_enc)
{
        SFE_NextToken(sc_enc);
        SFE_CheckToken(sc_enc, TOK_LEFT_CURVE);

        SFE_NextToken(sc_enc);
        SFE_OptionalExpression(sc_enc);
        SFE_CheckToken(sc_enc, TOK_SEMICOLON);

        SFE_NextToken(sc_enc);
        SFE_OptionalExpression(sc_enc);
        SFE_CheckToken(sc_enc, TOK_SEMICOLON);

        SFE_NextToken(sc_enc);
        SFE_OptionalExpression(sc_enc);
        SFE_CheckToken(sc_enc, TOK_RIGHT_CURVE);

        SFE_StatementBlock(sc_enc);
}

void SFE_WhileStatement(ScriptEnc *sc_enc)
{
        SFE_NextToken(sc_enc);
        SFE_CheckToken(sc_enc, TOK_LEFT_CURVE);
        SFE_NextToken(sc_enc);
        SFE_CompoundExpression(sc_enc, 0, 0, 0);
        SFE_CheckToken(sc_enc, TOK_RIGHT_CURVE);

        SFE_StatementBlock(sc_enc);
}

void SFE_ReturnStatement(ScriptEnc *sc_enc)
{
        SFE_NextToken(sc_enc);
        if (sc_enc->token_code != TOK_SEMICOLON) {
                SFE_WRITE_INT(sc_enc, 1, 1, "returnValue", NULL);
                SFE_CompoundExpression(sc_enc, 0, 0, 0);
        } else {
                SFE_WRITE_INT(sc_enc, 0, 1, "returnValue", NULL);
        }
}

void SFE_CaseBlock(ScriptEnc *sc_enc)
{
        SFE_WRITE_INT(sc_enc, 1, 1, "isCompoundStatement", NULL);
        SFE_NextToken(sc_enc);
        if (sc_enc->token_code == TOK_LEFT_BRACE) {
                SFE_NextToken(sc_enc);
                while (sc_enc->token_code != TOK_RIGHT_BRACE) {
                        SFE_WRITE_INT(sc_enc, 1, 1, "hasStatement", NULL);
                        SFE_Statement(sc_enc);
                        SFE_NextToken(sc_enc);
                }
                SFE_NextToken(sc_enc);
        }
        while ((sc_enc->token_code != TOK_CASE) && (sc_enc->token_code != TOK_DEFAULT) && (sc_enc->token_code != TOK_RIGHT_BRACE)) {
                SFE_WRITE_INT(sc_enc, 1, 1, "hasStatement", NULL);
                SFE_Statement(sc_enc);
                SFE_NextToken(sc_enc);
        }
        SFE_WRITE_INT(sc_enc, 0, 1, "hasStatement", NULL);
}

u32 SFE_PutCaseInteger(ScriptEnc *sc_enc, char *str, u32 nbBits)
{
        u32 val = 0;
        if ((str[0]=='0') && (str[1]=='x' || str[1]=='X')) {
                val = (u32) strtoul(sc_enc->token, (char **) NULL, 16);
        } else if (str[0]=='0' && isdigit(str[1])) {
                val = (u32) strtoul(str, (char **) NULL, 8);
        } else if (isdigit(str[0])) {
                val = (u32) strtoul(str, (char **) NULL, 10);
        } else {
                GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[bifs] Script encoding: %s is not an integer\n", str));
                sc_enc->err = GF_BAD_PARAM;
                return 0;
        }
        if (!sc_enc->emul) {
                GF_BIFS_WRITE_INT(sc_enc->codec, sc_enc->bs, val, nbBits, "value", sc_enc->token);
        } else {
                nbBits = gf_get_bit_size(val);
        }
        return nbBits;
}

void SFE_SwitchStatement(ScriptEnc *sc_enc)
{
        u32 nbBits, maxBits = 0;
        Bool prev_emu;
        char *buf_bck;
        u32 tok_bck;

        SFE_NextToken(sc_enc);
        SFE_CheckToken(sc_enc, TOK_LEFT_CURVE);

        SFE_NextToken(sc_enc);
        SFE_CompoundExpression(sc_enc, 0, 0, 0);
        SFE_CheckToken(sc_enc, TOK_RIGHT_CURVE);

        SFE_NextToken(sc_enc);
        SFE_CheckToken(sc_enc, TOK_LEFT_BRACE);

        /*first pass in emul*/
        buf_bck = sc_enc->cur_buf;
        tok_bck = sc_enc->token_code;
        prev_emu = sc_enc->emul;
        sc_enc->emul = GF_TRUE;

        SFE_NextToken(sc_enc);
        while (sc_enc->token_code == TOK_CASE) {
                SFE_NextToken(sc_enc);
                SFE_CheckToken(sc_enc, TOK_NUMBER);
                nbBits = SFE_PutCaseInteger(sc_enc, sc_enc->token, 0);
                if (maxBits<nbBits) maxBits = nbBits;

                SFE_NextToken(sc_enc);
                SFE_CheckToken(sc_enc, TOK_CONDSEP);

                SFE_CaseBlock(sc_enc);
                SFE_WRITE_INT(sc_enc, (sc_enc->token_code == TOK_CASE) ? 1 : 0, 1, "hasMoreCases", NULL);
        }

        /*second pass in parent mode*/
        sc_enc->cur_buf = buf_bck;
        sc_enc->token_code = tok_bck;
        sc_enc->emul = prev_emu;
        maxBits ++;
        SFE_WRITE_INT(sc_enc, maxBits, 5, "caseNbBits", NULL);

        SFE_NextToken(sc_enc);
        while (sc_enc->token_code == TOK_CASE) {
                SFE_NextToken(sc_enc);
                SFE_CheckToken(sc_enc, TOK_NUMBER);
                SFE_PutCaseInteger(sc_enc, sc_enc->token, maxBits);

                SFE_NextToken(sc_enc);
                SFE_CheckToken(sc_enc, TOK_CONDSEP);

                SFE_CaseBlock(sc_enc);
                SFE_WRITE_INT(sc_enc, (sc_enc->token_code == TOK_CASE) ? 1 : 0, 1, "hasMoreCases", NULL);
        }

        if (sc_enc->token_code == TOK_DEFAULT) {
                SFE_WRITE_INT(sc_enc, 1, 1, "hasDefault", NULL);
                SFE_NextToken(sc_enc);
                SFE_CheckToken(sc_enc, TOK_CONDSEP);
                SFE_CaseBlock(sc_enc);
        } else {
                SFE_WRITE_INT(sc_enc, 0, 1, "hasDefault", NULL);
        }
        SFE_CheckToken(sc_enc, TOK_RIGHT_BRACE);
}

void SFE_Statement(ScriptEnc *sc_enc)
{
        switch (sc_enc->token_code) {
        case TOK_IF:
                SFE_WRITE_INT(sc_enc, ST_IF, NUMBITS_STATEMENT, "statementType", "if");
                SFE_IfStatement(sc_enc);
                break;
        case TOK_FOR:
                SFE_WRITE_INT(sc_enc, ST_FOR, NUMBITS_STATEMENT, "statementType", "for");
                SFE_ForStatement(sc_enc);
                break;
        case TOK_WHILE:
                SFE_WRITE_INT(sc_enc, ST_WHILE, NUMBITS_STATEMENT, "statementType", "while");
                SFE_WhileStatement(sc_enc);
                break;
        case TOK_RETURN:
                SFE_WRITE_INT(sc_enc, ST_RETURN, NUMBITS_STATEMENT, "statementType", "return");
                SFE_ReturnStatement(sc_enc);
                break;
        case TOK_BREAK:
                SFE_WRITE_INT(sc_enc, ST_BREAK, NUMBITS_STATEMENT, "statementType", "break");
                SFE_NextToken(sc_enc);
                break;
        case TOK_CONTINUE:
                SFE_WRITE_INT(sc_enc, ST_CONTINUE, NUMBITS_STATEMENT, "statementType", "continue");
                SFE_NextToken(sc_enc);
                break;
        case TOK_SWITCH:
                SFE_WRITE_INT(sc_enc, ST_SWITCH, NUMBITS_STATEMENT, "statementType", "while");
                SFE_SwitchStatement(sc_enc);
                break;
        default:
                SFE_WRITE_INT(sc_enc, ST_COMPOUND_EXPR, NUMBITS_STATEMENT, "statementType", "compoundExpr");
                SFE_CompoundExpression(sc_enc, 0, 0, 0);
                break;
        }
}

void SFE_Statements(ScriptEnc *sc_enc)
{
        while (1) {
                if (!SFE_NextToken(sc_enc)) return;
                if (sc_enc->token_code == TOK_RIGHT_BRACE) break;
                SFE_WRITE_INT(sc_enc, 1, 1, "hasStatement", NULL);
                SFE_Statement(sc_enc);
        }
        SFE_WRITE_INT(sc_enc, 0, 1, "hasStatement", NULL);
}

void SFE_StatementBlock(ScriptEnc *sc_enc)
{
        if (!SFE_NextToken(sc_enc)) return;
        if (sc_enc->token_code == TOK_LEFT_BRACE) {
                SFE_WRITE_INT(sc_enc, 1, 1, "isCompoundStatement", NULL);
                SFE_Statements(sc_enc);
        } else {
                SFE_WRITE_INT(sc_enc, 0, 1, "isCompoundStatement", NULL);
                SFE_Statement(sc_enc);
        }
}
void SFE_Function(ScriptEnc *sc_enc)
{
        char szName[1000];

        SFE_NextToken(sc_enc);
        SFE_CheckToken(sc_enc, TOK_FUNCTION);

        SFE_NextToken(sc_enc);
        SFE_CheckToken(sc_enc, TOK_IDENTIFIER);
        strcpy(szName, sc_enc->token);
        SFE_PutIdentifier(sc_enc, sc_enc->token);

        SFE_NextToken(sc_enc);
        SFE_CheckToken(sc_enc, TOK_LEFT_CURVE);

        SFE_Arguments(sc_enc);
        SFE_StatementBlock(sc_enc);

        if (sc_enc->err) GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[bifs] Script encoding: Error while parsing function %s\n", szName));
}


GF_Err SFScript_Encode(GF_BifsEncoder *codec, SFScript *script_field, GF_BitStream *bs, GF_Node *n)
{
        char *ptr;
        ScriptEnc sc_enc;
        if (gf_node_get_tag(n) != TAG_MPEG4_Script) return GF_NON_COMPLIANT_BITSTREAM;

        memset(&sc_enc, 0, sizeof(ScriptEnc));
        sc_enc.codec = codec;
        sc_enc.script = n;
        sc_enc.bs = bs;
        sc_enc.identifiers = gf_list_new();
        sc_enc.id_buf = gf_list_new();
        sc_enc.err = GF_OK;

        if (codec->is_encoding_command) {
                GF_BIFS_WRITE_INT(codec, bs, 1, 1, "Script::isList", NULL);
                GF_BIFS_WRITE_INT(codec, bs, 1, 1, "end", NULL);
        } else {
                EncScriptFields(&sc_enc);
        }
        /*reserevd*/
        GF_BIFS_WRITE_INT(codec, bs, 1, 1, "reserved", NULL);

        if (script_field) {
                sc_enc.cur_buf = (char *)script_field->script_text;
        } else if (((M_Script*)n)->url.count) {
                sc_enc.cur_buf = (char *)((M_Script*)n)->url.vals[0].script_text;
        }
        if (sc_enc.cur_buf) {
                if (!strnicmp(sc_enc.cur_buf, "javascript:", 11)
                        || !strnicmp(sc_enc.cur_buf, "vrmlscript:", 11)
                        || !strnicmp(sc_enc.cur_buf, "ECMAScript:", 11)
                   ) {
                        sc_enc.cur_buf += 11;
                } else if (!strnicmp(sc_enc.cur_buf, "mpeg4script:", 12) ) {
                        sc_enc.cur_buf += 12;
                }
        }

        /*encode functions*/
        while (sc_enc.cur_buf && sc_enc.cur_buf[0] && (sc_enc.cur_buf[0]!='}')) {
                if (strchr("\r\n\t ", sc_enc.cur_buf[0]) ) {
                        sc_enc.cur_buf++;
                        continue;
                }
                GF_BIFS_WRITE_INT(codec, bs, 1, 1, "hasFunction", NULL);
                SFE_Function(&sc_enc);
                if (sc_enc.err) break;
        }
        GF_BIFS_WRITE_INT(codec, bs, 0, 1, "hasFunction", NULL);

        //clean up
        while (gf_list_count(sc_enc.identifiers)) {
                ptr = (char *)gf_list_get(sc_enc.identifiers, 0);
                gf_list_rem(sc_enc.identifiers, 0);
                gf_free(ptr);
        }
        gf_list_del(sc_enc.identifiers);
        /*in case of error this is needed*/
        while (gf_list_count(sc_enc.id_buf)) {
                ptr = (char *)gf_list_get(sc_enc.id_buf, 0);
                gf_list_rem(sc_enc.id_buf, 0);
                gf_free(ptr);
        }
        gf_list_del(sc_enc.id_buf);

        return sc_enc.err;
}


void SFE_PutReal(ScriptEnc *sc_enc, char *str)
{
        u32 i;
        size_t length = strlen(str);
        for (i=0; i<length; i++) {
                s32 c = str[i];
                if (c >= '0' && c <= '9')
                {
                        SFE_WRITE_INT(sc_enc, c-'0', 4, "floatChar", "Digital");
                }
                else if (c == '.')
                {
                        SFE_WRITE_INT(sc_enc, 10, 4, "floatChar", "Decimal Point");
                }
                else if (c == 'e' || c == 'E')
                {
                        SFE_WRITE_INT(sc_enc, 11, 4, "floatChar", "Exponential");
                }
                else if (c == '-')
                {
                        SFE_WRITE_INT(sc_enc, 12, 4, "floatChar", "Sign");
                }
                else {
                        GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[bifs] Script encoding: %s is not a real number\n", str));
                        sc_enc->err = GF_BAD_PARAM;
                        return;
                }
        }
        SFE_WRITE_INT(sc_enc, 15, 4, "floatChar", "End Symbol");
}

void SFE_PutNumber(ScriptEnc *sc_enc, char *str)
{
        if (strpbrk(str,".eE-") == 0) {
                SFE_WRITE_INT(sc_enc, 1, 1, "isInteger", "integer");
                SFE_PutInteger(sc_enc, str);
        } else {
                SFE_WRITE_INT(sc_enc, 0, 1, "isInteger", "real");
                SFE_PutReal(sc_enc, str);
        }
}

void SFE_PutBoolean(ScriptEnc *sc_enc, char *str)
{
        u32 v = 1;
        if (!stricmp(str, "false") || !strcmp(str, "0")) v = 0;
        SFE_WRITE_INT(sc_enc, v, 1, "value", "bolean");
}


#define CHECK_TOK(x) \
        if (curTok != x) { \
                GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[bifs] Script encoding: Token %s read, %s expected\n", tok_names[curTok], tok_names[x])); \
                sc_enc->err = GF_BAD_PARAM;     \
        }       \
 

u32 TOK_To_ET(u32 tok)
{
        switch(tok) {
        case TOK_INCREMENT:
                return ET_INCREMENT;
        case TOK_DECREMENT:
                return ET_DECREMENT;
        case TOK_NOT:
                return ET_NOT;
        case TOK_ONESCOMP:
                return ET_ONESCOMP;
        case TOK_MULTIPLY :
                return ET_MULTIPLY;
        case TOK_DIVIDE :
                return ET_DIVIDE;
        case TOK_MOD :
                return ET_MOD;
        case TOK_PLUS :
                return ET_PLUS;
        case TOK_MINUS :
                return ET_MINUS;
        case TOK_LSHIFT :
                return ET_LSHIFT;
        case TOK_RSHIFT :
                return ET_RSHIFT;
        case TOK_RSHIFTFILL :
                return ET_RSHIFTFILL;
        case TOK_LT :
                return ET_LT;
        case TOK_LE :
                return ET_LE;
        case TOK_GT :
                return ET_GT;
        case TOK_GE :
                return ET_GE;
        case TOK_EQ :
                return ET_EQ;
        case TOK_NE :
                return ET_NE;
        case TOK_AND :
                return ET_AND;
        case TOK_XOR :
                return ET_XOR;
        case TOK_OR :
                return ET_OR;
        case TOK_LAND :
                return ET_LAND;
        case TOK_LOR :
                return ET_LOR;
        case TOK_CONDTEST :
                return ET_CONDTEST;
        case TOK_ASSIGN :
                return ET_ASSIGN;
        case TOK_PLUSEQ :
                return ET_PLUSEQ;
        case TOK_MINUSEQ :
                return ET_MINUSEQ;
        case TOK_MULTIPLYEQ :
                return ET_MULTIPLYEQ;
        case TOK_DIVIDEEQ :
                return ET_DIVIDEEQ;
        case TOK_MODEQ :
                return ET_MODEQ;
        case TOK_LSHIFTEQ :
                return ET_LSHIFTEQ;
        case TOK_RSHIFTEQ :
                return ET_RSHIFTEQ;
        case TOK_RSHIFTFILLEQ :
                return ET_RSHIFTFILLEQ;
        case TOK_ANDEQ :
                return ET_ANDEQ;
        case TOK_XOREQ :
                return ET_XOREQ;
        case TOK_OREQ :
                return ET_OREQ;
        case TOK_FUNCTION:
                return ET_FUNCTION_CALL;
        case TOK_VAR:
                return ET_VAR;
        default:
                assert(0);
                return (u32) -1;
        }
}

static const char *expr_name[] = {
        "ET_CURVED_EXPR",
        "ET_NEGATIVE",
        "ET_NOT",
        "ET_ONESCOMP",
        "ET_INCREMENT",
        "ET_DECREMENT",
        "ET_POST_INCREMENT",
        "ET_POST_DECREMENT",
        "ET_CONDTEST",
        "ET_STRING",
        "ET_NUMBER",
        "ET_IDENTIFIER",
        "ET_FUNCTION_CALL",
        "ET_NEW",
        "ET_OBJECT_MEMBER_ACCESS",
        "ET_OBJECT_METHOD_CALL",
        "ET_ARRAY_DEREFERENCE",
        "ET_ASSIGN",
        "ET_PLUSEQ",
        "ET_MINUSEQ",
        "ET_MULTIPLYEQ",
        "ET_DIVIDEEQ",
        "ET_MODEQ",
        "ET_ANDEQ",
        "ET_OREQ",
        "ET_XOREQ",
        "ET_LSHIFTEQ",
        "ET_RSHIFTEQ",
        "ET_RSHIFTFILLEQ",
        "ET_EQ",
        "ET_NE",
        "ET_LT",
        "ET_LE",
        "ET_GT",
        "ET_GE",
        "ET_PLUS",
        "ET_MINUS",
        "ET_MULTIPLY",
        "ET_DIVIDE",
        "ET_MOD",
        "ET_LAND",
        "ET_LOR",
        "ET_AND",
        "ET_OR",
        "ET_XOR",
        "ET_LSHIFT",
        "ET_RSHIFT",
        "ET_RSHIFTFILL",
        "ET_BOOLEAN",
        "ET_VAR",
        "NUMBER_OF_EXPR_TYPE"
};

#define NUMBER_OF_RANK  15
static s32 ET_Rank[NUMBER_OF_EXPR_TYPE] =
{
        1,// ET_CURVED_EXPR
        2,// ET_NEGATIVE
        2,// ET_NOT
        2,// ET_ONESCOMP
        2,// ET_INCREMENT
        2,// ET_DECREMENT
        2,// ET_POST_INCREMENT
        2,// ET_POST_DECREMENT
        14,// ET_CONDTEST
        0,// ET_STRING
        0,// ET_NUMBER
        0,// ET_IDENTIFIER
        1,// ET_FUNCTION_CALL
        2,// ET_NEW
        1,// ET_OBJECT_MEMBER_ACCESS
        1,// ET_OBJECT_METHOD_CALL
        1,// ET_ARRAY_DEREFERENCE
        14,// ET_ASSIGN
        14,// ET_PLUSEQ
        14,// ET_MINUSEQ
        14,// ET_MULTIPLYEQ
        14,// ET_DIVIDEEQ
        14,// ET_MODEQ
        14,// ET_ANDEQ
        14,// ET_OREQ
        14,// ET_XOREQ
        14,// ET_LSHIFTEQ
        14,// ET_RSHIFTEQ
        14,// ET_RSHIFTFILLEQ
        8,// ET_EQ
        9,// ET_NE
        6,// ET_LT
        6,// ET_LE
        7,// ET_GT
        7,// ET_GE
        5,// ET_PLUS
        5,// ET_MINUS
        3,// ET_MULTIPLY
        3,// ET_DIVIDE
        3,// ET_MOD
        13,// ET_LAND
        14,// ET_LOR
        10,// ET_AND
        12,// ET_OR
        11,// ET_XOR
        5,// ET_LSHIFT
        6,// ET_RSHIFT
        6,// ET_RSHIFTFILL
        0, // ET_BOOLEAN
        14 // ET_VAR

        /*
                0, 0, 0,                        // variable, number, string
                1, 1, 1, 1, 1,                  // curved expr, call, member, array
                2, 2, 2, 2, 2, 2, 2, 2,         // unary operator
                3, 3, 3,                        // multiply, divide, mod
                4, 4,                           // add, subtract
                5, 5, 5,                        // bitwise shift
                6, 6, 6, 6,                     // relational
                7, 7,                           // equality
                8,                              // bitwise and
                9,                              // bitwise xor
                10,                             // bitwise or
                11,                             // logical and
                12,                             // logical or
                13,                             // conditional
                14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14  // assignment
                */
};
static s32 ET_leftAssoc[NUMBER_OF_RANK] = {1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0};

u32 MoveToToken(ScriptEnc *sc_enc, u32 endTok, u32 cur, u32 end)
{
        u32 cnt = 0;
        u32 startTok = TOK_EOF, curTok;

        if (endTok == TOK_RIGHT_CURVE) startTok = TOK_LEFT_CURVE;
        else if (endTok == TOK_RIGHT_BRACKET) startTok = TOK_LEFT_BRACKET;
        else if (endTok == TOK_RIGHT_BRACE) startTok = TOK_LEFT_BRACE;
        else if (endTok == TOK_CONDSEP) startTok = TOK_CONDTEST;
        else {
                GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[bifs] Script encoding: illegal MoveToToken %s\n", tok_names[endTok]));
                sc_enc->err = GF_BAD_PARAM;
                return (u32) -1;
        }
        do {
                curTok = sc_enc->expr_toks[cur++];
                if (curTok == startTok) cnt++;
                else if (curTok == endTok) cnt--;
        } while ( (curTok != endTok || cnt) && cur < end);
        if (curTok==endTok && cnt==0) return cur-1;
        return (u32) -1;
}


void SFE_FunctionCall(ScriptEnc *sc_enc, u32 start, u32 end);
void SFE_Params(ScriptEnc *sc_enc, u32 start, u32 end);
void SFE_ConditionTest(ScriptEnc *sc_enc, u32 start, u32 op, u32 end);
void SFE_ObjectConstruct(ScriptEnc *sc_enc, u32 start, u32 op, u32 end);
void SFE_ArrayDereference(ScriptEnc *sc_enc, u32 start, u32 op, u32 end);
void SFE_ObjectMethodCall(ScriptEnc *sc_enc, u32 start, u32 op, u32 end);
void SFE_ObjectMemberAccess(ScriptEnc *sc_enc, u32 start, u32 op, u32 end);

u32 SFE_Expression(ScriptEnc *sc_enc, u32 start, u32 end, Bool memberAccess)
{
        char *str;
        u32 n = start;
        u32 curPos = 0, finalPos = 0;
        u32 curTok, prevTok;
        u32 curExpr = 0, expr = 0;
        u32 curRank, maxRank=0;

        if (sc_enc->err) return 0;

        curTok = prevTok = sc_enc->expr_toks[n];
        do {
                curPos = n;
                switch (curTok) {
                case TOK_IDENTIFIER:
                        curExpr = ET_IDENTIFIER;
                        break;
                case TOK_NUMBER:
                        curExpr = ET_NUMBER;
                        break;
                case TOK_BOOLEAN:
                        curExpr = ET_BOOLEAN;
                        break;
                case TOK_STRING:
                        curExpr = ET_STRING;
                        break;
                case TOK_LEFT_CURVE:
                        if (prevTok == TOK_IDENTIFIER) curExpr = ET_FUNCTION_CALL;
                        else curExpr = ET_CURVED_EXPR;
                        n = MoveToToken(sc_enc, TOK_RIGHT_CURVE, n, end);
                        curTok = TOK_RIGHT_CURVE;
                        break;
                case TOK_LEFT_BRACKET:
                        curExpr = ET_ARRAY_DEREFERENCE;
                        n = MoveToToken(sc_enc, TOK_RIGHT_BRACKET, n, end);
                        curTok = TOK_RIGHT_BRACKET;
                        break;
                case TOK_PERIOD:
                        curTok = sc_enc->expr_toks[++n];
                        CHECK_TOK(TOK_IDENTIFIER);
                        if (sc_enc->expr_toks[n+1] == TOK_LEFT_CURVE) {
                                curExpr = ET_OBJECT_METHOD_CALL;
                                n = MoveToToken(sc_enc, TOK_RIGHT_CURVE, n+1, end);
                                curTok = TOK_RIGHT_CURVE;
                        } else {
                                curExpr = ET_OBJECT_MEMBER_ACCESS;
                        }
                        break;
                case TOK_NEW:
                        curExpr = ET_NEW;
                        curTok = sc_enc->expr_toks[++n];
                        CHECK_TOK(TOK_IDENTIFIER);
                        curTok = sc_enc->expr_toks[++n];
                        CHECK_TOK(TOK_LEFT_CURVE);
                        n = MoveToToken(sc_enc, TOK_RIGHT_CURVE, n, end);
                        curTok = TOK_RIGHT_CURVE;
                        break;
                case TOK_FUNCTION:
                        curExpr = ET_FUNCTION_ASSIGN;
                        break;
                case TOK_PLUS:
                        if (
                            prevTok==TOK_RIGHT_CURVE || prevTok==TOK_RIGHT_BRACKET ||
                            prevTok==TOK_IDENTIFIER || prevTok==TOK_NUMBER ||
                            prevTok==TOK_STRING || prevTok==TOK_INCREMENT ||
                            prevTok==TOK_DECREMENT
                        ) {
                                curExpr = ET_PLUS;
                        } else {
                                goto skip_token;
                        }
                        break;
                case TOK_MINUS:
                        if (
                            prevTok==TOK_RIGHT_CURVE || prevTok==TOK_RIGHT_BRACKET ||
                            prevTok==TOK_IDENTIFIER || prevTok==TOK_NUMBER ||
                            prevTok==TOK_STRING || prevTok==TOK_INCREMENT ||
                            prevTok==TOK_DECREMENT
                        ) {
                                curExpr = ET_MINUS;
                        } else {
                                curExpr = ET_NEGATIVE;
                                curTok = TOK_NEGATIVE;
                        }
                        break;
                case TOK_INCREMENT:
                        curExpr = ET_INCREMENT;
                        break;
                case TOK_DECREMENT:
                        curExpr = ET_DECREMENT;
                        break;
                case TOK_NOT:
                        curExpr = ET_NOT;
                        break;
                case TOK_ONESCOMP:
                        curExpr = ET_ONESCOMP;
                        break;
                case TOK_CONDTEST:
                        curExpr = ET_CONDTEST;
                        break;
                case TOK_CONDSEP:
                        break;
                case TOK_LEFT_BRACE:
                        curExpr = ET_CURVED_EXPR;
                        n = MoveToToken(sc_enc, TOK_RIGHT_BRACE, n, end);
                        curTok = TOK_RIGHT_BRACE;
                        break;
                        break;
                default:

                        if (curTok && (curTok != TOK_VAR)
                                && (curTok < TOK_MULTIPLY || curTok > TOK_OREQ)) {
                                GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[bifs] Script encoding: illegal token %s read\n", tok_names[curTok]));
                                sc_enc->err = GF_BAD_PARAM;
                                return 0;
                        }
                        curExpr = TOK_To_ET(curTok);
                }

                if (curTok == TOK_CONDSEP) {
                        prevTok = curTok;
                        curTok = sc_enc->expr_toks[++n];
                        continue;
                }
                curRank = ET_Rank[curExpr];
                if (curRank > maxRank) {
                        maxRank = curRank;
                        expr = curExpr;
                        finalPos = curPos;
                } else if (curRank == maxRank && ET_leftAssoc[curRank]) {
                        expr = curExpr;
                        finalPos = curPos;
                }
                prevTok = curTok;
skip_token:
                curTok = sc_enc->expr_toks[++n];
        } while (n<end);

        if (expr == ET_INCREMENT) {
                if (finalPos==start) {}
                else if (finalPos==end-1) expr = ET_POST_INCREMENT;
                else {
                        GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[bifs] Script encoding: illegal Increment expression\n"));
                        sc_enc->err = GF_BAD_PARAM;
                        return expr;
                }
        } else if (expr == ET_DECREMENT) {
                if (finalPos==start) {}
                else if (finalPos==end-1) expr = ET_POST_DECREMENT;
                else {
                        GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[bifs] Script encoding: illegal Decrement expression\n"));
                        sc_enc->err = GF_BAD_PARAM;
                        return expr;
                }
        }

        SFE_WRITE_INT(sc_enc, expr, NUMBITS_EXPR_TYPE, "expressionType", (char *) expr_name[expr]);

        switch (expr) {
        case ET_MULTIPLY:
        case ET_DIVIDE:
        case ET_MOD:
        case ET_PLUS:
        case ET_MINUS:
        case ET_LSHIFT:
        case ET_RSHIFT:
        case ET_RSHIFTFILL:
        case ET_LT:
        case ET_LE:
        case ET_GT:
        case ET_GE:
        case ET_EQ:
        case ET_NE:
        case ET_AND:
        case ET_XOR:
        case ET_OR:
        case ET_LAND:
        case ET_LOR:
                SFE_Expression(sc_enc, start, finalPos, GF_FALSE);
                SFE_Expression(sc_enc, finalPos+1, end, GF_FALSE);
                break;
        case ET_ASSIGN:
        case ET_PLUSEQ:
        case ET_MINUSEQ:
        case ET_MULTIPLYEQ:
        case ET_DIVIDEEQ:
        case ET_MODEQ:
        case ET_LSHIFTEQ:
        case ET_RSHIFTEQ:
        case ET_RSHIFTFILLEQ:
        case ET_ANDEQ:
        case ET_XOREQ:
        case ET_OREQ:
        {
                u32 ret = SFE_Expression(sc_enc, start, finalPos, GF_FALSE);
                if ( ret != ET_IDENTIFIER && ret != ET_OBJECT_MEMBER_ACCESS && ret != ET_ARRAY_DEREFERENCE ) {
                        GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[bifs] Script encoding: LeftVariable expected, %s returned\n", expr_name[ret]));
                        sc_enc->err = GF_BAD_PARAM;
                        return expr;
                }
                SFE_Expression(sc_enc, finalPos+1, end, GF_FALSE);
        }
        break;

        case ET_IDENTIFIER:
                str = (char *)gf_list_get(sc_enc->id_buf, 0);
                gf_list_rem(sc_enc->id_buf, 0);
                /*TODO: when accessing member, we should try to translate proto fields into _fieldALL when not
                using USENAMEs...*/
                if (memberAccess) {
                }
                SFE_PutIdentifier(sc_enc, str);
                gf_free(str);
                break;
        case ET_NUMBER:
                str = (char *)gf_list_get(sc_enc->id_buf, 0);
                gf_list_rem(sc_enc->id_buf, 0);
                SFE_PutNumber(sc_enc, str);
                gf_free(str);
                break;
        case ET_BOOLEAN:
                str = (char *)gf_list_get(sc_enc->id_buf, 0);
                gf_list_rem(sc_enc->id_buf, 0);
                SFE_PutBoolean(sc_enc, str);
                gf_free(str);
                break;
        case ET_VAR:
                while (1) {
                        str = (char *)gf_list_get(sc_enc->id_buf, 0);
                        if (!str) break;
                        gf_list_rem(sc_enc->id_buf, 0);
                        GF_BIFS_WRITE_INT(sc_enc->codec, sc_enc->bs, 1, 1, "hasArgument", NULL);
                        SFE_PutIdentifier(sc_enc, str);
                        gf_free(str);
                }
                GF_BIFS_WRITE_INT(sc_enc->codec, sc_enc->bs, 0, 1, "hasArgument", NULL);
                break;
        case ET_STRING:
                str = (char *)gf_list_get(sc_enc->id_buf, 0);
                gf_list_rem(sc_enc->id_buf, 0);
                if (!sc_enc->emul) gf_bifs_enc_name(sc_enc->codec, sc_enc->bs, str);
                gf_free(str);
                break;
        case ET_NEGATIVE:
        case ET_INCREMENT:
        case ET_DECREMENT:
        case ET_NOT:
        case ET_ONESCOMP:
                SFE_Expression(sc_enc, finalPos+1, end, GF_FALSE);
                break;
        case ET_CURVED_EXPR:
                SFE_CompoundExpression(sc_enc, finalPos+1, end-1, 0);
                break;
        case ET_POST_INCREMENT :
        case ET_POST_DECREMENT :
                SFE_Expression(sc_enc, start, finalPos, GF_FALSE);
                break;
        case ET_FUNCTION_CALL:
                SFE_FunctionCall(sc_enc, start, end);
                break;
        case ET_OBJECT_MEMBER_ACCESS :
                SFE_ObjectMemberAccess(sc_enc, start, finalPos, end);
                break;
        case ET_OBJECT_METHOD_CALL:
                SFE_ObjectMethodCall(sc_enc, start, finalPos, end);
                break;
        case ET_ARRAY_DEREFERENCE:
                SFE_ArrayDereference(sc_enc, start, finalPos, end);
                break;
        case ET_NEW:
                SFE_ObjectConstruct(sc_enc, start, finalPos, end);
                break;
        case ET_CONDTEST:
                SFE_ConditionTest(sc_enc, start, finalPos, end);
                break;
        case ET_FUNCTION_ASSIGN:
                SFE_NextToken(sc_enc);
                SFE_CheckToken(sc_enc, TOK_LEFT_CURVE);

                SFE_Arguments(sc_enc);
                SFE_StatementBlock(sc_enc);
                SFE_NextToken(sc_enc);
                SFE_CheckToken(sc_enc, TOK_SEMICOLON);
                break;
        default:
                GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[bifs] Script encoding: illegal expression type %s\n", expr_name[expr]));
                sc_enc->err = GF_BAD_PARAM;
                break;
        }
        return expr;
}


void SFE_FunctionCall(ScriptEnc *sc_enc, u32 start, u32 end)
{
        u32 curTok;
        char *str;
        curTok = sc_enc->expr_toks[start++];
        CHECK_TOK(TOK_IDENTIFIER);
        str = (char *)gf_list_get(sc_enc->id_buf, 0);
        gf_list_rem(sc_enc->id_buf, 0);
        SFE_PutIdentifier(sc_enc, str);
        gf_free(str);
        curTok = sc_enc->expr_toks[start++];
        CHECK_TOK(TOK_LEFT_CURVE);
        SFE_Params(sc_enc, start, end-1);
        curTok = sc_enc->expr_toks[end-1];
        CHECK_TOK(TOK_RIGHT_CURVE);
}

void SFE_ObjectMemberAccess(ScriptEnc *sc_enc, u32 start, u32 op, u32 end)
{
        u32 curTok;
        char *str;

        SFE_Expression(sc_enc, start, op, GF_TRUE);
        curTok = sc_enc->expr_toks[op];
        CHECK_TOK(TOK_PERIOD);
        curTok = sc_enc->expr_toks[end-1];
        CHECK_TOK(TOK_IDENTIFIER);
        str = (char *)gf_list_get(sc_enc->id_buf, 0);
        gf_list_rem(sc_enc->id_buf, 0);
        SFE_PutIdentifier(sc_enc, str);
        gf_free(str);
}

void SFE_ObjectMethodCall(ScriptEnc *sc_enc, u32 start, u32 op, u32 end)
{
        u32 curTok;
        char *str;

        SFE_Expression(sc_enc, start, op, GF_FALSE);
        curTok = sc_enc->expr_toks[op];
        CHECK_TOK(TOK_PERIOD);
        curTok = sc_enc->expr_toks[op+1];
        CHECK_TOK(TOK_IDENTIFIER);
        str = (char *)gf_list_get(sc_enc->id_buf, 0);
        gf_list_rem(sc_enc->id_buf, 0);
        SFE_PutIdentifier(sc_enc, str);
        gf_free(str);
        curTok = sc_enc->expr_toks[op+2];
        CHECK_TOK(TOK_LEFT_CURVE);
        SFE_Params(sc_enc, op+3, end-1);
        curTok = sc_enc->expr_toks[end-1];
        CHECK_TOK(TOK_RIGHT_CURVE);
}

void SFE_ArrayDereference(ScriptEnc *sc_enc, u32 start, u32 op, u32 end)
{
        u32 curTok;

        SFE_Expression(sc_enc, start, op, GF_FALSE);
        curTok = sc_enc->expr_toks[op];
        CHECK_TOK(TOK_LEFT_BRACKET);
        SFE_CompoundExpression(sc_enc, op+1, end-1, 0);
        curTok = sc_enc->expr_toks[end-1];
        CHECK_TOK(TOK_RIGHT_BRACKET);
}

void SFE_ObjectConstruct(ScriptEnc *sc_enc, u32 start, u32 op, u32 end)
{
        u32 curTok;
        char *str;

        curTok = sc_enc->expr_toks[start++];
        CHECK_TOK(TOK_NEW);
        curTok = sc_enc->expr_toks[start++];
        CHECK_TOK(TOK_IDENTIFIER);
        str = (char *)gf_list_get(sc_enc->id_buf, 0);
        gf_list_rem(sc_enc->id_buf, 0);
        SFE_PutIdentifier(sc_enc, str);
        gf_free(str);
        curTok = sc_enc->expr_toks[start++];
        CHECK_TOK(TOK_LEFT_CURVE);
        SFE_Params(sc_enc, start, end-1);
        curTok = sc_enc->expr_toks[end-1];
        CHECK_TOK(TOK_RIGHT_CURVE);
}

void SFE_ConditionTest(ScriptEnc *sc_enc, u32 start, u32 op, u32 end)
{
        u32 curTok;

        SFE_Expression(sc_enc, start, op, GF_FALSE);
        curTok = sc_enc->expr_toks[op];
        CHECK_TOK(TOK_CONDTEST);
        start = op+1;
        op = MoveToToken(sc_enc, TOK_CONDSEP, op, end-1);
        SFE_Expression(sc_enc, start, op, GF_FALSE);
        curTok = sc_enc->expr_toks[op];
        CHECK_TOK(TOK_CONDSEP);
        SFE_Expression(sc_enc, op+1, end, GF_FALSE);
}

void SFE_Params(ScriptEnc *sc_enc, u32 start, u32 end)
{
        u32 curTok;

        curTok = sc_enc->expr_toks[start];
        if (curTok != TOK_RIGHT_CURVE) {
                SFE_WRITE_INT(sc_enc, 1, 1, "hasParam", NULL);
                SFE_CompoundExpression(sc_enc, start, end, 1);
        } else {
                SFE_WRITE_INT(sc_enc, 0, 1, "hasParam", NULL);
        }
}

#endif  /* !defined(GPAC_DISABLE_BIFS) && !defined(GPAC_DISABLE_BIFS_ENC) && defined(GPAC_HAS_SPIDERMONKEY) */

/* [<][>][^][v][top][bottom][index][help] */