/*
 * Copyright (C) 2013 JPEXS
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.jpexs.decompiler.flash.action.parser.script;

import com.jpexs.decompiler.flash.action.Action;
import com.jpexs.decompiler.flash.action.model.AsciiToCharActionItem;
import com.jpexs.decompiler.flash.action.model.CallActionItem;
import com.jpexs.decompiler.flash.action.model.CallFunctionActionItem;
import com.jpexs.decompiler.flash.action.model.CallMethodActionItem;
import com.jpexs.decompiler.flash.action.model.CharToAsciiActionItem;
import com.jpexs.decompiler.flash.action.model.CloneSpriteActionItem;
import com.jpexs.decompiler.flash.action.model.ConstantPool;
import com.jpexs.decompiler.flash.action.model.DefineLocalActionItem;
import com.jpexs.decompiler.flash.action.model.DefineRegisterActionItem;
import com.jpexs.decompiler.flash.action.model.DeleteActionItem;
import com.jpexs.decompiler.flash.action.model.DirectValueActionItem;
import com.jpexs.decompiler.flash.action.model.EvalActionItem;
import com.jpexs.decompiler.flash.action.model.FunctionActionItem;
import com.jpexs.decompiler.flash.action.model.GetMemberActionItem;
import com.jpexs.decompiler.flash.action.model.GetTimeActionItem;
import com.jpexs.decompiler.flash.action.model.GetURL2ActionItem;
import com.jpexs.decompiler.flash.action.model.GetVariableActionItem;
import com.jpexs.decompiler.flash.action.model.GetVersionActionItem;
import com.jpexs.decompiler.flash.action.model.GotoFrame2ActionItem;
import com.jpexs.decompiler.flash.action.model.InitArrayActionItem;
import com.jpexs.decompiler.flash.action.model.InitObjectActionItem;
import com.jpexs.decompiler.flash.action.model.LoadMovieActionItem;
import com.jpexs.decompiler.flash.action.model.LoadMovieNumActionItem;
import com.jpexs.decompiler.flash.action.model.LoadVariablesActionItem;
import com.jpexs.decompiler.flash.action.model.LoadVariablesNumActionItem;
import com.jpexs.decompiler.flash.action.model.MBAsciiToCharActionItem;
import com.jpexs.decompiler.flash.action.model.MBCharToAsciiActionItem;
import com.jpexs.decompiler.flash.action.model.MBStringExtractActionItem;
import com.jpexs.decompiler.flash.action.model.MBStringLengthActionItem;
import com.jpexs.decompiler.flash.action.model.NewMethodActionItem;
import com.jpexs.decompiler.flash.action.model.NewObjectActionItem;
import com.jpexs.decompiler.flash.action.model.NextFrameActionItem;
import com.jpexs.decompiler.flash.action.model.PlayActionItem;
import com.jpexs.decompiler.flash.action.model.PostDecrementActionItem;
import com.jpexs.decompiler.flash.action.model.PostIncrementActionItem;
import com.jpexs.decompiler.flash.action.model.PrevFrameActionItem;
import com.jpexs.decompiler.flash.action.model.PrintActionItem;
import com.jpexs.decompiler.flash.action.model.PrintAsBitmapActionItem;
import com.jpexs.decompiler.flash.action.model.PrintAsBitmapNumActionItem;
import com.jpexs.decompiler.flash.action.model.PrintNumActionItem;
import com.jpexs.decompiler.flash.action.model.RandomNumberActionItem;
import com.jpexs.decompiler.flash.action.model.RemoveSpriteActionItem;
import com.jpexs.decompiler.flash.action.model.ReturnActionItem;
import com.jpexs.decompiler.flash.action.model.SetMemberActionItem;
import com.jpexs.decompiler.flash.action.model.SetVariableActionItem;
import com.jpexs.decompiler.flash.action.model.StartDragActionItem;
import com.jpexs.decompiler.flash.action.model.StopActionItem;
import com.jpexs.decompiler.flash.action.model.StopAllSoundsActionItem;
import com.jpexs.decompiler.flash.action.model.StopDragActionItem;
import com.jpexs.decompiler.flash.action.model.StoreRegisterActionItem;
import com.jpexs.decompiler.flash.action.model.StringExtractActionItem;
import com.jpexs.decompiler.flash.action.model.StringLengthActionItem;
import com.jpexs.decompiler.flash.action.model.ThrowActionItem;
import com.jpexs.decompiler.flash.action.model.ToIntegerActionItem;
import com.jpexs.decompiler.flash.action.model.ToNumberActionItem;
import com.jpexs.decompiler.flash.action.model.ToStringActionItem;
import com.jpexs.decompiler.flash.action.model.ToggleHighQualityActionItem;
import com.jpexs.decompiler.flash.action.model.TraceActionItem;
import com.jpexs.decompiler.flash.action.model.TypeOfActionItem;
import com.jpexs.decompiler.flash.action.model.UnLoadMovieActionItem;
import com.jpexs.decompiler.flash.action.model.UnLoadMovieNumActionItem;
import com.jpexs.decompiler.flash.action.model.clauses.ClassActionItem;
import com.jpexs.decompiler.flash.action.model.clauses.ForInActionItem;
import com.jpexs.decompiler.flash.action.model.clauses.IfFrameLoadedActionItem;
import com.jpexs.decompiler.flash.action.model.clauses.InterfaceActionItem;
import com.jpexs.decompiler.flash.action.model.clauses.TellTargetActionItem;
import com.jpexs.decompiler.flash.action.model.clauses.TryActionItem;
import com.jpexs.decompiler.flash.action.model.clauses.WithActionItem;
import com.jpexs.decompiler.flash.action.model.operations.AddActionItem;
import com.jpexs.decompiler.flash.action.model.operations.BitAndActionItem;
import com.jpexs.decompiler.flash.action.model.operations.BitOrActionItem;
import com.jpexs.decompiler.flash.action.model.operations.BitXorActionItem;
import com.jpexs.decompiler.flash.action.model.operations.DivideActionItem;
import com.jpexs.decompiler.flash.action.model.operations.EqActionItem;
import com.jpexs.decompiler.flash.action.model.operations.GeActionItem;
import com.jpexs.decompiler.flash.action.model.operations.GtActionItem;
import com.jpexs.decompiler.flash.action.model.operations.InstanceOfActionItem;
import com.jpexs.decompiler.flash.action.model.operations.LeActionItem;
import com.jpexs.decompiler.flash.action.model.operations.LtActionItem;
import com.jpexs.decompiler.flash.action.model.operations.ModuloActionItem;
import com.jpexs.decompiler.flash.action.model.operations.MultiplyActionItem;
import com.jpexs.decompiler.flash.action.model.operations.NeqActionItem;
import com.jpexs.decompiler.flash.action.model.operations.PreDecrementActionItem;
import com.jpexs.decompiler.flash.action.model.operations.PreIncrementActionItem;
import com.jpexs.decompiler.flash.action.model.operations.StrictEqActionItem;
import com.jpexs.decompiler.flash.action.model.operations.StrictNeqActionItem;
import com.jpexs.decompiler.flash.action.model.operations.SubtractActionItem;
import com.jpexs.decompiler.flash.action.parser.ParseException;
import com.jpexs.decompiler.flash.action.swf4.*;
import com.jpexs.decompiler.flash.action.swf5.*;
import com.jpexs.decompiler.flash.ecma.Null;
import com.jpexs.decompiler.flash.ecma.Undefined;
import com.jpexs.decompiler.flash.helpers.collections.MyEntry;
import com.jpexs.decompiler.graph.GraphSourceItem;
import com.jpexs.decompiler.graph.GraphTargetItem;
import com.jpexs.decompiler.graph.model.AndItem;
import com.jpexs.decompiler.graph.model.BinaryOp;
import com.jpexs.decompiler.graph.model.BlockItem;
import com.jpexs.decompiler.graph.model.BreakItem;
import com.jpexs.decompiler.graph.model.CommaExpressionItem;
import com.jpexs.decompiler.graph.model.ContinueItem;
import com.jpexs.decompiler.graph.model.DoWhileItem;
import com.jpexs.decompiler.graph.model.ForItem;
import com.jpexs.decompiler.graph.model.IfItem;
import com.jpexs.decompiler.graph.model.NotItem;
import com.jpexs.decompiler.graph.model.OrItem;
import com.jpexs.decompiler.graph.model.ParenthesisItem;
import com.jpexs.decompiler.graph.model.SwitchItem;
import com.jpexs.decompiler.graph.model.TernarOpItem;
import com.jpexs.decompiler.graph.model.WhileItem;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author JPEXS
 */
public class ActionScriptParser {

    public static final int REGISTER_THIS = 1;
    public static final int REGISTER_ARGUMENTS = 2;
    public static final int REGISTER_SUPER = 3;
    public static final int REGISTER_ROOT = 4;
    public static final int REGISTER_PARENT = 5;
    public static final int REGISTER_GLOBAL = 6;
    private long uniqLast = 0;
    private boolean debugMode = false;

    private String uniqId() {
        uniqLast++;
        return "" + uniqLast;
    }

    private List<GraphTargetItem> commands(HashMap<String, Integer> registerVars, boolean inFunction, boolean inMethod, int forinlevel) throws IOException, ParseException {
        List<GraphTargetItem> ret = new ArrayList<>();
        if (debugMode) {
            System.out.println("commands:");
        }
        GraphTargetItem cmd = null;
        while ((cmd = command(registerVars, inFunction, inMethod, forinlevel, true)) != null) {
            ret.add(cmd);
        }
        if (debugMode) {
            System.out.println("/commands");
        }
        return ret;
    }

    private List<GraphTargetItem> nonempty(List<GraphTargetItem> list) {
        if (list == null) {
            return new ArrayList<>();
        }
        return list;
    }

    private GraphTargetItem type() throws IOException, ParseException {
        GraphTargetItem ret = null;

        ParsedSymbol s = lex();
        expected(s, lexer.yyline(), SymbolType.IDENTIFIER);
        ret = new GetVariableActionItem(null, pushConst(s.value.toString()));
        s = lex();
        while (s.type == SymbolType.DOT) {
            s = lex();
            expected(s, lexer.yyline(), SymbolType.IDENTIFIER);
            ret = new GetMemberActionItem(null, ret, pushConst(s.value.toString()));
            s = lex();
        }
        lexer.pushback(s);
        return ret;
    }

    private GraphTargetItem memberOrCall(GraphTargetItem newcmds, HashMap<String, Integer> registerVars, boolean inFunction, boolean inMethod) throws IOException, ParseException {
        ParsedSymbol s = lex();
        GraphTargetItem ret = newcmds;
        while (s.isType(SymbolType.DOT, SymbolType.BRACKET_OPEN, SymbolType.PARENT_OPEN)) {
            switch (s.type) {
                case DOT:
                case BRACKET_OPEN:
                    lexer.pushback(s);
                    ret = member(ret, registerVars, inFunction, inMethod);
                    break;
                case PARENT_OPEN:
                    if (ret instanceof GetMemberActionItem) {
                        GetMemberActionItem mem = (GetMemberActionItem) ret;
                        ret = new CallMethodActionItem(null, mem.object, mem.memberName, call(registerVars, inFunction, inMethod));
                    } else if (ret instanceof GetVariableActionItem) {
                        GetVariableActionItem var = (GetVariableActionItem) ret;
                        ret = new CallFunctionActionItem(null, var.name, call(registerVars, inFunction, inMethod));
                    } else {
                        ret = new CallFunctionActionItem(null, ret, call(registerVars, inFunction, inMethod));
                    }
                    break;
            }
            s = lex();
        }
        lexer.pushback(s);
        return ret;
    }

    private GraphTargetItem member(GraphTargetItem obj, HashMap<String, Integer> registerVars, boolean inFunction, boolean inMethod) throws IOException, ParseException {
        GraphTargetItem ret = obj;
        ParsedSymbol s = lex();
        while (s.isType(SymbolType.DOT, SymbolType.BRACKET_OPEN)) {
            if (s.type == SymbolType.BRACKET_OPEN) {
                ret = new GetMemberActionItem(null, ret, expression(registerVars, inFunction, inMethod, true));
                expectedType(SymbolType.BRACKET_CLOSE);
                s = lex();
                continue;
            }
            s = lex();
            expected(s, lexer.yyline(), SymbolType.IDENTIFIER, SymbolType.THIS, SymbolType.SUPER, SymbolGroup.GLOBALFUNC, SymbolGroup.GLOBALCONST);
            //TODO: Handle properties (?)
            if (false) {//GraphTargetItem.propertyNamesList.contains(s.value.toString())) {
                //ret.add(new ActionPush((Long) (long) (int) GraphTargetItem.propertyNamesList.indexOf(s.value.toString())));
                //ret.add(new ActionGetProperty());
            } else {
                ret = new GetMemberActionItem(null, ret, pushConst(s.value.toString()));
            }
            s = lex();
        }
        lexer.pushback(s);
        return ret;
    }

    private GraphTargetItem variable(HashMap<String, Integer> registerVars, boolean inFunction, boolean inMethod) throws IOException, ParseException {
        GraphTargetItem ret = null;
        ParsedSymbol s = lex();
        expected(s, lexer.yyline(), SymbolType.IDENTIFIER, SymbolType.THIS, SymbolType.SUPER);
        if (registerVars.containsKey(s.value.toString())) {
            ret = new DirectValueActionItem(null, 0, new RegisterNumber(registerVars.get(s.value.toString())), new ArrayList<String>());
        } else {
            if (inMethod) {
                ret = new DirectValueActionItem(null, 0, new RegisterNumber(REGISTER_THIS), new ArrayList<String>());
                //TODO: Handle properties (?)
                if (false) { //GraphTargetItem.propertyNamesList.contains(s.value.toString())) {
                    //ret.add(new ActionPush((Long) (long) (int) GraphTargetItem.propertyNamesList.indexOf(s.value.toString())));
                    //ret.add(new ActionGetProperty());
                } else {
                    ret = new GetMemberActionItem(null, ret, pushConst(s.value.toString()));
                }
            } else {
                ret = new GetVariableActionItem(null, pushConst(s.value.toString()));
            }
        }
        ret = (member(ret, registerVars, inFunction, inMethod));
        return ret;
    }

    private void expected(ParsedSymbol symb, int line, Object... expected) throws IOException, ParseException {
        boolean found = false;
        for (Object t : expected) {
            if (symb.type == t) {
                found = true;
            }
            if (symb.group == t) {
                found = true;
            }
        }
        if (!found) {
            String expStr = "";
            boolean first = true;
            for (Object e : expected) {
                if (!first) {
                    expStr += " or ";
                }
                expStr += e;
                first = false;
            }
            throw new ParseException("" + expStr + " expected but " + symb.type + " found", line);
        }
    }

    private ParsedSymbol expectedType(Object... type) throws IOException, ParseException {
        ParsedSymbol symb = lex();
        expected(symb, lexer.yyline(), type);
        return symb;
    }

    private ParsedSymbol lex() throws IOException, ParseException {
        ParsedSymbol ret = lexer.lex();
        if (debugMode) {
            System.out.println(ret);
        }
        return ret;
    }

    private List<GraphTargetItem> call(HashMap<String, Integer> registerVars, boolean inFunction, boolean inMethod) throws IOException, ParseException {
        List<GraphTargetItem> ret = new ArrayList<>();
        //expected(SymbolType.PARENT_OPEN); //MUST BE HANDLED BY CALLER
        ParsedSymbol s = lex();
        while (s.type != SymbolType.PARENT_CLOSE) {
            if (s.type != SymbolType.COMMA) {
                lexer.pushback(s);
            }
            ret.add(expression(registerVars, inFunction, inMethod, true));
            s = lex();
            expected(s, lexer.yyline(), SymbolType.COMMA, SymbolType.PARENT_CLOSE);
        }
        return ret;
    }

    private FunctionActionItem function(boolean withBody, String functionName, boolean isMethod) throws IOException, ParseException {
        GraphTargetItem ret = null;
        ParsedSymbol s = null;
        expectedType(SymbolType.PARENT_OPEN);
        s = lex();
        List<String> paramNames = new ArrayList<>();

        while (s.type != SymbolType.PARENT_CLOSE) {
            if (s.type != SymbolType.COMMA) {
                lexer.pushback(s);
            }
            s = lex();
            expected(s, lexer.yyline(), SymbolType.IDENTIFIER);
            paramNames.add(s.value.toString());
            s = lex();
            if (s.type == SymbolType.COLON) {
                type();
                s = lex();
            }

            if (!s.isType(SymbolType.COMMA, SymbolType.PARENT_CLOSE)) {
                expected(s, lexer.yyline(), SymbolType.COMMA, SymbolType.PARENT_CLOSE);
            }
        }
        HashMap<String, Integer> registerVars = new HashMap<>();
        registerVars.put("_parent", REGISTER_PARENT);
        registerVars.put("_root", REGISTER_ROOT);
        registerVars.put("super", REGISTER_SUPER);
        registerVars.put("arguments", REGISTER_ARGUMENTS);
        registerVars.put("this", REGISTER_THIS);
        registerVars.put("_global", REGISTER_GLOBAL);
        for (int i = 0; i < paramNames.size(); i++) {
            registerVars.put(paramNames.get(i), (7 + i)); //(paramNames.size() - i)));
        }
        List<GraphTargetItem> body = null;
        if (withBody) {
            expectedType(SymbolType.CURLY_OPEN);
            body = commands(registerVars, true, isMethod, 0);
            expectedType(SymbolType.CURLY_CLOSE);
        }

        return new FunctionActionItem(null, functionName, paramNames, body, constantPool, -1);
    }

    private GraphTargetItem traits(boolean isInterface, GraphTargetItem nameStr, GraphTargetItem extendsStr, List<GraphTargetItem> implementsStr) throws IOException, ParseException {

        GraphTargetItem ret = null;
        /*for (int i = 0; i < nameStr.size() - 1; i++) {
         List<GraphTargetItem> notBody = new ArrayList<>();
         List<String> globalClassTypeStr = new ArrayList<>();
         globalClassTypeStr.add("_global");
         for (int j = 0; j <= i; j++) {
         globalClassTypeStr.add(nameStr.get(j));
         }

         List<GraphTargetItem> val = new ArrayList<>();
         val.add(new ActionPush((Long) 0L));
         val.add(pushConst("Object"));
         val.add(new ActionNewObject());
         notBody.addAll(typeToActions(globalClassTypeStr, val));
         ret.addAll(typeToActions(globalClassTypeStr, null));
         ret.add(new ActionNot());
         ret.add(new ActionNot());
         ret.add(new ActionIf(GraphTargetItem.actionsToBytes(notBody, false, SWF.DEFAULT_VERSION).length));
         ret.addAll(notBody);
         }
         List<GraphTargetItem> ifbody = new ArrayList<>();
         List<String> globalClassTypeStr = new ArrayList<>();
         globalClassTypeStr.add("_global");
         globalClassTypeStr.addAll(nameStr);*/


        ParsedSymbol s = null;
        FunctionActionItem constr = null;
        List<GraphTargetItem> staticFunctions = new ArrayList<>();
        List<MyEntry<GraphTargetItem, GraphTargetItem>> staticVars = new ArrayList<>();
        List<GraphTargetItem> functions = new ArrayList<>();
        List<MyEntry<GraphTargetItem, GraphTargetItem>> vars = new ArrayList<>();

        String classNameStr = "";
        if (nameStr instanceof GetMemberActionItem) {
            GetMemberActionItem mem = (GetMemberActionItem) nameStr;
            if (mem.memberName instanceof DirectValueActionItem) {
                classNameStr = ((DirectValueActionItem) mem.memberName).toStringNoQuotes(new ConstantPool(constantPool));
            }
        } else if (nameStr instanceof GetVariableActionItem) {
            GetVariableActionItem var = (GetVariableActionItem) nameStr;
            if (var.name instanceof DirectValueActionItem) {
                classNameStr = ((DirectValueActionItem) var.name).toStringNoQuotes(new ConstantPool(constantPool));
            }
        }

        looptrait:
        while (true) {
            s = lex();
            boolean isStatic = false;
            while (s.isType(SymbolType.STATIC, SymbolType.PUBLIC, SymbolType.PRIVATE)) {
                if (s.type == SymbolType.STATIC) {
                    isStatic = true;
                }
                s = lex();
            }
            switch (s.type) {
                case FUNCTION:
                    s = lex();
                    expected(s, lexer.yyline(), SymbolType.IDENTIFIER);
                    String fname = s.value.toString();
                    if (fname.equals(classNameStr)) { //constructor
                        constr = (function(!isInterface, "", true));
                    } else {
                        if (!isInterface) {
                            if (isStatic) {
                                FunctionActionItem ft = function(!isInterface, "", true);
                                ft.calculatedFunctionName = pushConst(fname);
                                staticFunctions.add(ft);
                            } else {
                                FunctionActionItem ft = function(!isInterface, "", true);
                                ft.calculatedFunctionName = pushConst(fname);
                                functions.add(ft);
                            }
                        }
                    }
                    break;
                case VAR:
                    s = lex();
                    expected(s, lexer.yyline(), SymbolType.IDENTIFIER);
                    String ident = s.value.toString();
                    s = lex();
                    if (s.type == SymbolType.COLON) {
                        type();
                        s = lex();
                    }
                    if (s.type == SymbolType.ASSIGN) {
                        if (isStatic) {
                            staticVars.add(new MyEntry<GraphTargetItem, GraphTargetItem>(pushConst(ident), expression(new HashMap<String, Integer>(), false, false, true)));
                        } else {
                            vars.add(new MyEntry<GraphTargetItem, GraphTargetItem>(pushConst(ident), expression(new HashMap<String, Integer>(), false, false, true)));
                        }
                        s = lex();
                    }
                    if (s.type != SymbolType.SEMICOLON) {
                        lexer.pushback(s);
                    }
                    break;
                default:
                    lexer.pushback(s);
                    break looptrait;

            }
        }

        if (isInterface) {
            return new InterfaceActionItem(nameStr, implementsStr);
        } else {
            return new ClassActionItem(nameStr, extendsStr, implementsStr, constr, functions, vars, staticFunctions, staticVars);
        }
    }

    private GraphTargetItem expressionCommands(ParsedSymbol s, HashMap<String, Integer> registerVars, boolean inFunction, boolean inMethod, int forinlevel) throws IOException, ParseException {
        GraphTargetItem ret = null;
        switch (s.type) {
            case GETVERSION:
                expectedType(SymbolType.PARENT_OPEN);
                ret = new GetVersionActionItem(null);
                expectedType(SymbolType.PARENT_CLOSE);
                break;
            case MBORD:
                expectedType(SymbolType.PARENT_OPEN);
                ret = new MBCharToAsciiActionItem(null, expression(registerVars, inFunction, inMethod, true));
                expectedType(SymbolType.PARENT_CLOSE);
                break;
            case MBCHR:
                expectedType(SymbolType.PARENT_OPEN);
                ret = new MBAsciiToCharActionItem(null, expression(registerVars, inFunction, inMethod, true));
                expectedType(SymbolType.PARENT_CLOSE);
                break;
            case MBLENGTH:
                expectedType(SymbolType.PARENT_OPEN);
                ret = new MBStringLengthActionItem(null, expression(registerVars, inFunction, inMethod, true));
                expectedType(SymbolType.PARENT_CLOSE);
                break;
            case MBSUBSTRING:
                expectedType(SymbolType.PARENT_OPEN);
                GraphTargetItem val1 = (expression(registerVars, inFunction, inMethod, true));
                expectedType(SymbolType.COMMA);
                GraphTargetItem index1 = (expression(registerVars, inFunction, inMethod, true));
                expectedType(SymbolType.COMMA);
                GraphTargetItem len1 = (expression(registerVars, inFunction, inMethod, true));
                expectedType(SymbolType.PARENT_CLOSE);
                ret = new MBStringExtractActionItem(null, val1, index1, len1);
                break;
            case SUBSTR:
                expectedType(SymbolType.PARENT_OPEN);
                GraphTargetItem val2 = (expression(registerVars, inFunction, inMethod, true));
                expectedType(SymbolType.COMMA);
                GraphTargetItem index2 = (expression(registerVars, inFunction, inMethod, true));
                expectedType(SymbolType.COMMA);
                GraphTargetItem len2 = (expression(registerVars, inFunction, inMethod, true));
                expectedType(SymbolType.PARENT_CLOSE);
                ret = new StringExtractActionItem(null, val2, index2, len2);
                break;
            case LENGTH:
                expectedType(SymbolType.PARENT_OPEN);
                ret = new StringLengthActionItem(null, expression(registerVars, inFunction, inMethod, true));
                expectedType(SymbolType.PARENT_CLOSE);
                break;
            case RANDOM:
                expectedType(SymbolType.PARENT_OPEN);
                ret = new RandomNumberActionItem(null, expression(registerVars, inFunction, inMethod, true));
                expectedType(SymbolType.PARENT_CLOSE);
                break;
            case INT:
                expectedType(SymbolType.PARENT_OPEN);
                ret = new ToIntegerActionItem(null, expression(registerVars, inFunction, inMethod, true));
                expectedType(SymbolType.PARENT_CLOSE);
                break;

            /*case TARGETPATH:
             expectedType(SymbolType.PARENT_OPEN);
             ret.addAll(expression(registerVars, inFunction, inMethod, true));
             expectedType(SymbolType.PARENT_CLOSE);
             ret.add(new ActionTargetPath());
             break;*/
            case NUMBER_OP:
                s = lex();
                if (s.type == SymbolType.DOT) {
                    ret = memberOrCall(new GetVariableActionItem(null, new DirectValueActionItem(null, 0, s.value, new ArrayList<String>())), registerVars, inFunction, inMethod);
                } else {
                    expected(s, lexer.yyline(), SymbolType.PARENT_OPEN);
                    ret = new ToNumberActionItem(null, expression(registerVars, inFunction, inMethod, true));
                    expectedType(SymbolType.PARENT_CLOSE);
                }
                break;
            case STRING_OP:
                ParsedSymbol sop = s;
                s = lex();
                if (s.type == SymbolType.DOT) {
                    lexer.pushback(s);
                    ret = memberOrCall(new GetVariableActionItem(null, new DirectValueActionItem(null, 0, sop.value, new ArrayList<String>())), registerVars, inFunction, inMethod);
                } else {
                    expected(s, lexer.yyline(), SymbolType.PARENT_OPEN);
                    ret = new ToStringActionItem(null, expression(registerVars, inFunction, inMethod, true));
                    expectedType(SymbolType.PARENT_CLOSE);
                    ret = memberOrCall(ret, registerVars, inFunction, inMethod);
                }
                break;
            case ORD:
                expectedType(SymbolType.PARENT_OPEN);
                ret = new CharToAsciiActionItem(null, expression(registerVars, inFunction, inMethod, true));
                expectedType(SymbolType.PARENT_CLOSE);
                break;
            case CHR:
                expectedType(SymbolType.PARENT_OPEN);
                ret = new AsciiToCharActionItem(null, expression(registerVars, inFunction, inMethod, true));
                expectedType(SymbolType.PARENT_CLOSE);
                break;
            case DUPLICATEMOVIECLIP:
                expectedType(SymbolType.PARENT_OPEN);
                GraphTargetItem src3 = (expression(registerVars, inFunction, inMethod, true));
                expectedType(SymbolType.COMMA);
                GraphTargetItem tar3 = (expression(registerVars, inFunction, inMethod, true));
                expectedType(SymbolType.COMMA);
                GraphTargetItem dep3 = (expression(registerVars, inFunction, inMethod, true));
                expectedType(SymbolType.PARENT_CLOSE);
                ret = new CloneSpriteActionItem(null, src3, tar3, dep3);
                break;
            case GETTIMER:
                expectedType(SymbolType.PARENT_OPEN);
                expectedType(SymbolType.PARENT_CLOSE);
                ret = new GetTimeActionItem(null);
                break;
            default:
                return null;
        }
        return ret;
    }

    private GraphTargetItem command(HashMap<String, Integer> registerVars, boolean inFunction, boolean inMethod, int forinlevel, boolean mustBeCommand) throws IOException, ParseException {
        LexBufferer buf = new LexBufferer();
        lexer.addListener(buf);
        GraphTargetItem ret = null;
        if (debugMode) {
            System.out.println("command:");
        }
        ParsedSymbol s = lex();
        if (s.type == SymbolType.EOF) {
            return null;
        }
        switch (s.type) {
            case CALL:
                expectedType(SymbolType.PARENT_OPEN);
                ret = new CallActionItem(null, (expression(registerVars, inFunction, inMethod, true)));
                expectedType(SymbolType.PARENT_CLOSE);
                break;
            case LENGTH:
                expectedType(SymbolType.PARENT_OPEN);
                ret = new StringLengthActionItem(null, (expression(registerVars, inFunction, inMethod, true)));
                expectedType(SymbolType.PARENT_CLOSE);
                break;
            case MBLENGTH:
                expectedType(SymbolType.PARENT_OPEN);
                ret = new MBStringLengthActionItem(null, (expression(registerVars, inFunction, inMethod, true)));
                expectedType(SymbolType.PARENT_CLOSE);
                break;
            case SET:
                expectedType(SymbolType.PARENT_OPEN);
                GraphTargetItem name1 = (expression(registerVars, inFunction, inMethod, true));
                expectedType(SymbolType.COMMA);
                GraphTargetItem value1 = (expression(registerVars, inFunction, inMethod, true));
                expectedType(SymbolType.PARENT_CLOSE);
                ret = new SetVariableActionItem(null, name1, value1);
                break;
            case WITH:
                expectedType(SymbolType.PARENT_OPEN);
                GraphTargetItem wvar = (variable(registerVars, inFunction, inMethod));
                expectedType(SymbolType.PARENT_CLOSE);
                expectedType(SymbolType.CURLY_OPEN);
                List<GraphTargetItem> wcmd = commands(registerVars, inFunction, inMethod, forinlevel);
                expectedType(SymbolType.CURLY_CLOSE);
                ret = new WithActionItem(null, wvar, wcmd);
                break;
            case DELETE:
                GraphTargetItem varDel = variable(registerVars, inFunction, inMethod);
                if (varDel instanceof GetMemberActionItem) {
                    GetMemberActionItem gm = (GetMemberActionItem) varDel;
                    ret = new DeleteActionItem(null, gm.object, gm.memberName);
                } else {
                    throw new ParseException("Not a property", lexer.yyline());
                }
                break;
            case TRACE:
                expectedType(SymbolType.PARENT_OPEN);
                ret = new TraceActionItem(null, (expression(registerVars, inFunction, inMethod, true)));
                expectedType(SymbolType.PARENT_CLOSE);
                break;

            case GETURL:
                expectedType(SymbolType.PARENT_OPEN);
                GraphTargetItem url = (expression(registerVars, inFunction, inMethod, true));
                s = lex();
                expected(s, lexer.yyline(), SymbolType.PARENT_CLOSE, SymbolType.COMMA);
                int getuMethod = 1;
                GraphTargetItem target = null;
                if (s.type == SymbolType.COMMA) {
                    target = (expression(registerVars, inFunction, inMethod, true));
                    s = lex();
                    if (s.type == SymbolType.COMMA) {
                        s = lex();
                        expected(s, lexer.yyline(), SymbolType.STRING);
                        if (s.value.equals("GET")) {
                            getuMethod = 1;
                        } else if (s.value.equals("POST")) {
                            getuMethod = 2;
                        } else {
                            throw new ParseException("Invalid method, \"GET\" or \"POST\" expected.", lexer.yyline());
                        }
                    } else {
                        lexer.pushback(s);
                    }
                } else {
                    lexer.pushback(s);
                    target = new DirectValueActionItem(null, 0, "", new ArrayList<String>());
                }
                expectedType(SymbolType.PARENT_CLOSE);
                ret = new GetURL2ActionItem(null, url, target, getuMethod);
                break;
            case GOTOANDSTOP:
                expectedType(SymbolType.PARENT_OPEN);
                GraphTargetItem gtsFrame = expression(registerVars, inFunction, inMethod, true);
                s = lex();
                if (s.type == SymbolType.COMMA) { //Handle scene?
                    s = lex();
                    gtsFrame = expression(registerVars, inFunction, inMethod, true);
                } else {
                    lexer.pushback(s);
                }
                ret = new GotoFrame2ActionItem(null, gtsFrame, false, false, 0);
                expectedType(SymbolType.PARENT_CLOSE);
                break;
            case NEXTFRAME:
                expectedType(SymbolType.PARENT_OPEN);
                expectedType(SymbolType.PARENT_CLOSE);
                ret = new NextFrameActionItem(null);
                break;
            case PLAY:
                expectedType(SymbolType.PARENT_OPEN);
                expectedType(SymbolType.PARENT_CLOSE);
                ret = new PlayActionItem(null);
                break;
            case PREVFRAME:
                expectedType(SymbolType.PARENT_OPEN);
                expectedType(SymbolType.PARENT_CLOSE);
                ret = new PrevFrameActionItem(null);
                break;
            case TELLTARGET:
                expectedType(SymbolType.PARENT_OPEN);
                GraphTargetItem tellTarget = expression(registerVars, inFunction, inMethod, true);
                expectedType(SymbolType.PARENT_CLOSE);
                expectedType(SymbolType.CURLY_OPEN);
                List<GraphTargetItem> tellcmds = commands(registerVars, inFunction, inMethod, forinlevel);
                expectedType(SymbolType.CURLY_CLOSE);
                ret = new TellTargetActionItem(null, tellTarget, tellcmds);
                break;
            case STOP:
                expectedType(SymbolType.PARENT_OPEN);
                expectedType(SymbolType.PARENT_CLOSE);
                ret = new StopActionItem(null);
                break;
            case STOPALLSOUNDS:
                expectedType(SymbolType.PARENT_OPEN);
                expectedType(SymbolType.PARENT_CLOSE);
                ret = new StopAllSoundsActionItem(null);
                break;
            case TOGGLEHIGHQUALITY:
                expectedType(SymbolType.PARENT_OPEN);
                expectedType(SymbolType.PARENT_CLOSE);
                ret = new ToggleHighQualityActionItem(null);
                break;

            case STOPDRAG:
                expectedType(SymbolType.PARENT_OPEN);
                expectedType(SymbolType.PARENT_CLOSE);
                ret = new StopDragActionItem(null);
                break;

            case UNLOADMOVIE:
            case UNLOADMOVIENUM:
                SymbolType unloadType = s.type;
                expectedType(SymbolType.PARENT_OPEN);
                GraphTargetItem unTargetOrNum = expression(registerVars, inFunction, inMethod, true);
                expectedType(SymbolType.PARENT_CLOSE);
                if (unloadType == SymbolType.UNLOADMOVIE) {
                    ret = new UnLoadMovieActionItem(null, unTargetOrNum);
                }
                if (unloadType == SymbolType.UNLOADMOVIENUM) {
                    ret = new UnLoadMovieNumActionItem(null, unTargetOrNum);
                }
                break;
            case PRINT:
            case PRINTASBITMAP:
            case PRINTASBITMAPNUM:
            case PRINTNUM:
                SymbolType printType = s.type;
                expectedType(SymbolType.PARENT_OPEN);
                GraphTargetItem printTarget = (expression(registerVars, inFunction, inMethod, true));
                expectedType(SymbolType.COMMA);
                GraphTargetItem printBBox = (expression(registerVars, inFunction, inMethod, true));
                expectedType(SymbolType.PARENT_CLOSE);

                switch (printType) {
                    case PRINT:
                        ret = new PrintActionItem(null, printTarget, printBBox);
                        break;
                    case PRINTNUM:
                        ret = new PrintNumActionItem(null, printTarget, printBBox);
                        break;
                    case PRINTASBITMAP:
                        ret = new PrintAsBitmapActionItem(null, printTarget, printBBox);
                        break;
                    case PRINTASBITMAPNUM:
                        ret = new PrintAsBitmapNumActionItem(null, printTarget, printBBox);
                        break;
                }
                break;
            case LOADVARIABLES:
            case LOADMOVIE:
            case LOADVARIABLESNUM:
            case LOADMOVIENUM:
                SymbolType loadType = s.type;
                expectedType(SymbolType.PARENT_OPEN);
                GraphTargetItem url2 = (expression(registerVars, inFunction, inMethod, true));
                expectedType(SymbolType.COMMA);
                GraphTargetItem targetOrNum = (expression(registerVars, inFunction, inMethod, true));



                s = lex();
                expected(s, lexer.yyline(), SymbolType.PARENT_CLOSE, SymbolType.COMMA);
                int lvmethod = 1;
                if (s.type == SymbolType.COMMA) {
                    s = lex();
                    expected(s, lexer.yyline(), SymbolType.STRING);
                    if (s.value.equals("POST")) {
                        lvmethod = 2;
                    } else if (s.value.equals("GET")) {
                        lvmethod = 1;
                    } else {
                        throw new ParseException("Invalid method, \"GET\" or \"POST\" expected.", lexer.yyline());
                    }
                } else {
                    lexer.pushback(s);
                }
                expectedType(SymbolType.PARENT_CLOSE);
                switch (loadType) {
                    case LOADVARIABLES:
                        ret = new LoadVariablesActionItem(null, url2, targetOrNum, lvmethod);
                        break;
                    case LOADMOVIE:
                        ret = new LoadMovieActionItem(null, url2, targetOrNum, lvmethod);
                        break;
                    case LOADVARIABLESNUM:
                        ret = new LoadVariablesNumActionItem(null, url2, targetOrNum, lvmethod);
                        break;
                    case LOADMOVIENUM:
                        ret = new LoadMovieNumActionItem(null, url2, targetOrNum, lvmethod);
                        break;
                }
                break;
            case GOTOANDPLAY:
                expectedType(SymbolType.PARENT_OPEN);
                GraphTargetItem gtpFrame = expression(registerVars, inFunction, inMethod, true);
                s = lex();
                if (s.type == SymbolType.COMMA) { //Handle scene?                    
                    s = lex();
                    gtpFrame = expression(registerVars, inFunction, inMethod, true);
                } else {
                    lexer.pushback(s);
                }
                ret = new GotoFrame2ActionItem(null, gtpFrame, true, false, 0);
                expectedType(SymbolType.PARENT_CLOSE);
                break;

            case REMOVEMOVIECLIP:
                expectedType(SymbolType.PARENT_OPEN);
                ret = new RemoveSpriteActionItem(null, (expression(registerVars, inFunction, inMethod, true)));
                expectedType(SymbolType.PARENT_CLOSE);
                break;
            case STARTDRAG:
                expectedType(SymbolType.PARENT_OPEN);
                GraphTargetItem dragTarget = (expression(registerVars, inFunction, inMethod, true));
                GraphTargetItem lockCenter = null;
                GraphTargetItem constrain = null;
                GraphTargetItem x1 = null;
                GraphTargetItem y1 = null;
                GraphTargetItem x2 = null;
                GraphTargetItem y2 = null;
                s = lex();
                if (s.type == SymbolType.COMMA) {
                    lockCenter = (expression(registerVars, inFunction, inMethod, true));
                    s = lex();
                    if (s.type == SymbolType.COMMA) {
                        constrain = new DirectValueActionItem(null, 0, Boolean.TRUE, new ArrayList<String>());
                        x1 = (expression(registerVars, inFunction, inMethod, true));
                        s = lex();
                        if (s.type == SymbolType.COMMA) {
                            y1 = (expression(registerVars, inFunction, inMethod, true));
                            s = lex();
                            if (s.type == SymbolType.COMMA) {
                                x2 = (expression(registerVars, inFunction, inMethod, true));
                                s = lex();
                                if (s.type == SymbolType.COMMA) {
                                    y2 = (expression(registerVars, inFunction, inMethod, true));
                                } else {
                                    lexer.pushback(s);
                                    y2 = new DirectValueActionItem(null, 0, (Long) 0L, new ArrayList<String>());
                                }
                            } else {
                                lexer.pushback(s);
                                x2 = new DirectValueActionItem(null, 0, (Long) 0L, new ArrayList<String>());
                                y2 = new DirectValueActionItem(null, 0, (Long) 0L, new ArrayList<String>());
                            }
                        } else {
                            lexer.pushback(s);
                            x2 = new DirectValueActionItem(null, 0, (Long) 0L, new ArrayList<String>());
                            y2 = new DirectValueActionItem(null, 0, (Long) 0L, new ArrayList<String>());
                            y1 = new DirectValueActionItem(null, 0, (Long) 0L, new ArrayList<String>());

                        }
                    } else {
                        lexer.pushback(s);
                        constrain = new DirectValueActionItem(null, 0, Boolean.FALSE, new ArrayList<String>());
                        //ret.add(new ActionPush(Boolean.FALSE));
                    }
                } else {
                    lockCenter = new DirectValueActionItem(null, 0, Boolean.FALSE, new ArrayList<String>());
                    constrain = new DirectValueActionItem(null, 0, Boolean.FALSE, new ArrayList<String>());
                    lexer.pushback(s);
                }
                expectedType(SymbolType.PARENT_CLOSE);
                ret = new StartDragActionItem(null, dragTarget, lockCenter, constrain, x1, y1, x2, y2);
                break;

            case IFFRAMELOADED:
                expectedType(SymbolType.PARENT_OPEN);
                GraphTargetItem iflExpr = (expression(registerVars, inFunction, inMethod, true));
                expectedType(SymbolType.PARENT_CLOSE);
                expectedType(SymbolType.CURLY_OPEN);
                List<GraphTargetItem> iflComs = commands(registerVars, inFunction, inMethod, forinlevel);
                expectedType(SymbolType.CURLY_CLOSE);
                ret = new IfFrameLoadedActionItem(iflExpr, iflComs, null);
                break;
            case CLASS:
                GraphTargetItem classTypeStr = type();
                s = lex();
                GraphTargetItem extendsTypeStr = null;
                if (s.type == SymbolType.EXTENDS) {
                    extendsTypeStr = type();
                    s = lex();
                }
                List<GraphTargetItem> implementsTypeStrs = new ArrayList<>();
                if (s.type == SymbolType.IMPLEMENTS) {
                    do {
                        GraphTargetItem implementsTypeStr = type();
                        implementsTypeStrs.add(implementsTypeStr);
                        s = lex();
                    } while (s.type == SymbolType.COMMA);
                }
                expected(s, lexer.yyline(), SymbolType.CURLY_OPEN);
                ret = (traits(false, classTypeStr, extendsTypeStr, implementsTypeStrs));
                expectedType(SymbolType.CURLY_CLOSE);
                break;
            case INTERFACE:
                GraphTargetItem interfaceTypeStr = type();
                s = lex();
                List<GraphTargetItem> intExtendsTypeStrs = new ArrayList<>();

                if (s.type == SymbolType.EXTENDS) {
                    do {
                        GraphTargetItem intExtendsTypeStr = type();
                        intExtendsTypeStrs.add(intExtendsTypeStr);
                        s = lex();
                    } while (s.type == SymbolType.COMMA);
                }
                expected(s, lexer.yyline(), SymbolType.CURLY_OPEN);
                ret = (traits(true, interfaceTypeStr, null, intExtendsTypeStrs));
                expectedType(SymbolType.CURLY_CLOSE);
                break;
            case FUNCTION:
                s = lexer.lex();
                expected(s, lexer.yyline(), SymbolType.IDENTIFIER);
                ret = (function(true, s.value.toString(), false));
                break;
            case NEW:
                GraphTargetItem type = type();
                expectedType(SymbolType.PARENT_OPEN);
                if (type instanceof GetMemberActionItem) {
                    GetMemberActionItem mem = (GetMemberActionItem) type;
                    ret = new NewMethodActionItem(null, mem.object, mem.memberName, call(registerVars, inFunction, inMethod));
                } else if (type instanceof GetVariableActionItem) {
                    GetVariableActionItem var = (GetVariableActionItem) type;
                    ret = new NewObjectActionItem(null, var.name, call(registerVars, inFunction, inMethod));
                } else {
                    ret = new NewObjectActionItem(null, ret, call(registerVars, inFunction, inMethod));
                }
                break;
            case VAR:
                s = lex();
                expected(s, lexer.yyline(), SymbolType.IDENTIFIER);
                String varIdentifier = s.value.toString();
                s = lex();
                if (s.type == SymbolType.COLON) {
                    type();
                    s = lex();
                    //TODO: handle value type
                }

                if (s.type == SymbolType.ASSIGN) {

                    if (!inFunction) {
                        //ret.add(pushConst(varIdentifier));
                    }
                    GraphTargetItem varval = (expression(registerVars, inFunction, inMethod, true));
                    if (inFunction) {
                        for (int i = 1; i < 256; i++) {
                            if (!registerVars.containsValue(i)) {
                                registerVars.put(varIdentifier, i);
                                ret = new StoreRegisterActionItem(null, new RegisterNumber(i), varval, true);
                                break;
                            }
                        }
                    } else {
                        ret = new DefineLocalActionItem(null, pushConst(varIdentifier), varval);
                    }
                } else {
                    if (inFunction) {
                        for (int i = 1; i < 256; i++) {
                            if (!registerVars.containsValue(i)) {
                                registerVars.put(varIdentifier, i);
                                ret = new DefineRegisterActionItem(varIdentifier, i);
                                break;
                            }
                        }
                    } else {
                        ret = new DefineLocalActionItem(null, pushConst(varIdentifier), null);
                    }
                    lexer.pushback(s);
                }
                break;
            case CURLY_OPEN:
                ret = new BlockItem(null, commands(registerVars, inFunction, inMethod, forinlevel));
                expectedType(SymbolType.CURLY_CLOSE);
                break;
            case INCREMENT: //preincrement
            case DECREMENT: //predecrement
                GraphTargetItem varincdec = variable(registerVars, inFunction, inMethod);
                if (s.type == SymbolType.INCREMENT) {
                    ret = new PreIncrementActionItem(null, varincdec);
                } else if (s.type == SymbolType.DECREMENT) {
                    ret = new PreDecrementActionItem(null, varincdec);
                }
                break;
            case IDENTIFIER:
            case THIS:
            case SUPER:
            case PARENT_OPEN:
            case EVAL:
                ParsedSymbol varS = s;
                boolean isEval = false;
                GraphTargetItem var;
                if (s.type == SymbolType.PARENT_OPEN) {
                    var = expression(registerVars, inFunction, inMethod, true);
                    expectedType(SymbolType.PARENT_CLOSE);
                    memberOrCall(var, registerVars, inFunction, inMethod);
                } else if (s.type == SymbolType.EVAL) {
                    expectedType(SymbolType.PARENT_OPEN);
                    var = expression(registerVars, inFunction, inMethod, true);
                    var = new EvalActionItem(null, var);
                    expectedType(SymbolType.PARENT_CLOSE);
                    var = memberOrCall(var, registerVars, inFunction, inMethod);
                    isEval = true;
                } else {
                    lexer.pushback(s);
                    var = variable(registerVars, inFunction, inMethod);
                }
                s = lex();
                switch (s.type) {
                    case ASSIGN:
                        ret = var;
                        ret = Action.gettoset(ret, expression(registerVars, inFunction, inMethod, true));
                        break;
                    case ASSIGN_BITAND:
                    case ASSIGN_BITOR:
                    case ASSIGN_DIVIDE:
                    case ASSIGN_MINUS:
                    case ASSIGN_MODULO:
                    case ASSIGN_MULTIPLY:
                    case ASSIGN_PLUS:
                    case ASSIGN_SHIFT_LEFT:
                    case ASSIGN_SHIFT_RIGHT:
                    case ASSIGN_USHIFT_RIGHT:
                    case ASSIGN_XOR:
                        GraphTargetItem valtoappend = (expression(registerVars, inFunction, inMethod, true));

                        switch (s.type) {
                            case ASSIGN_BITAND:
                                ret = Action.gettoset(var, new BitAndActionItem(null, var, valtoappend));
                                break;
                            case ASSIGN_BITOR:
                                ret = Action.gettoset(var, new BitOrActionItem(null, var, valtoappend));
                                break;
                            case ASSIGN_DIVIDE:
                                ret = Action.gettoset(var, new DivideActionItem(null, var, valtoappend));
                                break;
                            case ASSIGN_MINUS:
                                ret = Action.gettoset(var, new SubtractActionItem(null, var, valtoappend));
                                break;
                            case ASSIGN_MODULO:
                                ret = Action.gettoset(var, new ModuloActionItem(null, var, valtoappend));
                                break;
                            case ASSIGN_MULTIPLY:
                                ret = Action.gettoset(var, new MultiplyActionItem(null, var, valtoappend));
                                break;
                            case ASSIGN_PLUS:
                                ret = Action.gettoset(var, new AddActionItem(null, var, valtoappend, true));
                                break;
                        }
                        break;
                    case INCREMENT: //postincrement
                        ret = new PostIncrementActionItem(null, var);
                        break;
                    case DECREMENT: //postdecrement
                        ret = new PostDecrementActionItem(null, var);
                        break;
                    case PARENT_OPEN: //function call
                        ret = var;
                        if (varS.type == SymbolType.SUPER || varS.type == SymbolType.THIS) {
                            List<GraphTargetItem> args = call(registerVars, inFunction, inMethod);
                            ret = new CallMethodActionItem(null, ret, new DirectValueActionItem(null, 0, new Undefined(), constantPool), args);
                        } else {
                            lexer.pushback(s);
                            ret = memberOrCall(ret, registerVars, inFunction, inMethod);
                        }
                        break;
                    default:
                        if (isEval) {
                            ret = var;
                        } else {
                            if (mustBeCommand) {
                                throw new ParseException("Not a command", lexer.yyline());
                            }
                        }
                }
                break;
            case IF:
                expectedType(SymbolType.PARENT_OPEN);
                GraphTargetItem ifExpr = (expression(registerVars, inFunction, inMethod, true));
                expectedType(SymbolType.PARENT_CLOSE);
                GraphTargetItem onTrue = command(registerVars, inFunction, inMethod, forinlevel, true);
                List<GraphTargetItem> onTrueList = new ArrayList<>();
                onTrueList.add(onTrue);
                s = lex();
                List<GraphTargetItem> onFalseList = null;
                if (s.type == SymbolType.ELSE) {
                    onFalseList = new ArrayList<>();
                    onFalseList.add(command(registerVars, inFunction, inMethod, forinlevel, true));
                } else {
                    lexer.pushback(s);
                }
                ret = new IfItem(null, ifExpr, onTrueList, onFalseList);
                break;
            case WHILE:
                expectedType(SymbolType.PARENT_OPEN);
                List<GraphTargetItem> whileExpr = new ArrayList<>();
                whileExpr.add(commaExpression(registerVars, inFunction, inMethod, forinlevel));
                expectedType(SymbolType.PARENT_CLOSE);
                List<GraphTargetItem> whileBody = new ArrayList<>();
                whileBody.add(command(registerVars, inFunction, inMethod, forinlevel, true));
                ret = new WhileItem(null, null, whileExpr, whileBody);
                break;
            case DO:
                List<GraphTargetItem> doBody = new ArrayList<>();
                doBody.add(command(registerVars, inFunction, inMethod, forinlevel, true));
                expectedType(SymbolType.WHILE);
                expectedType(SymbolType.PARENT_OPEN);
                List<GraphTargetItem> doExpr = new ArrayList<>();
                doExpr.add(commaExpression(registerVars, inFunction, inMethod, forinlevel));
                expectedType(SymbolType.PARENT_CLOSE);
                ret = new DoWhileItem(null, null, doBody, doExpr);
                break;
            case FOR:
                expectedType(SymbolType.PARENT_OPEN);
                s = lex();
                boolean forin = false;
                GraphTargetItem collection = null;
                String objIdent = null;
                int innerExprReg = 0;
                if (s.type == SymbolType.VAR || s.type == SymbolType.IDENTIFIER) {
                    ParsedSymbol s2 = null;
                    ParsedSymbol ssel = s;
                    if (s.type == SymbolType.VAR) {
                        s2 = lex();
                        ssel = s2;
                    }

                    if (ssel.type == SymbolType.IDENTIFIER) {
                        objIdent = ssel.value.toString();

                        ParsedSymbol s3 = lex();
                        if (s3.type == SymbolType.IN) {
                            if (inFunction) {
                                for (int i = 0; i < 256; i++) {
                                    if (!registerVars.containsValue(i)) {
                                        registerVars.put(objIdent, i);
                                        innerExprReg = i;
                                        break;
                                    }
                                }
                            }
                            collection = expression(registerVars, inFunction, inMethod, true);
                            forin = true;
                        } else {
                            lexer.pushback(s3);
                            if (s2 != null) {
                                lexer.pushback(s2);
                            }
                            lexer.pushback(s);
                        }
                    } else {
                        if (s2 != null) {
                            lexer.pushback(s2);
                        }
                        lexer.pushback(s);
                    }
                } else {
                    lexer.pushback(s);
                }
                List<GraphTargetItem> forFinalCommands = new ArrayList<>();
                GraphTargetItem forExpr = null;
                List<GraphTargetItem> forFirstCommands = new ArrayList<>();
                if (!forin) {
                    forFirstCommands.add((command(registerVars, inFunction, inMethod, forinlevel, true)));
                    forExpr = (expression(registerVars, inFunction, inMethod, true));
                    expectedType(SymbolType.SEMICOLON);
                    forFinalCommands.add(command(registerVars, inFunction, inMethod, forinlevel, true));
                }
                expectedType(SymbolType.PARENT_CLOSE);
                List<GraphTargetItem> forBody = new ArrayList<>();
                forBody.add(command(registerVars, inFunction, inMethod, forin ? forinlevel + 1 : forinlevel, true));
                if (forin) {
                    ret = new ForInActionItem(null, null, pushConst(objIdent), collection, forBody);
                } else {
                    ret = new ForItem(null, null, forFirstCommands, forExpr, forFinalCommands, forBody);
                }
                break;
            case SWITCH:
                expectedType(SymbolType.PARENT_OPEN);
                GraphTargetItem switchExpr = expression(registerVars, inFunction, inMethod, true);
                expectedType(SymbolType.PARENT_CLOSE);
                expectedType(SymbolType.CURLY_OPEN);
                s = lex();
                //ret.addAll(switchExpr);
                int exprReg = 0;
                for (int i = 0; i < 256; i++) {
                    if (!registerVars.containsValue(i)) {
                        registerVars.put("__switch" + uniqId(), i);
                        exprReg = i;
                        break;
                    }
                }
                List<List<ActionIf>> caseIfs = new ArrayList<>();
                List<List<GraphTargetItem>> caseCmds = new ArrayList<>();
                List<GraphTargetItem> caseExprsAll = new ArrayList<>();
                List<Integer> valueMapping = new ArrayList<>();
                int pos = 0;
                while (s.type == SymbolType.CASE) {
                    List<GraphTargetItem> caseExprs = new ArrayList<>();
                    while (s.type == SymbolType.CASE) {
                        GraphTargetItem curCaseExpr = expression(registerVars, inFunction, inMethod, true);
                        caseExprs.add(curCaseExpr);
                        expectedType(SymbolType.COLON);
                        s = lex();
                        caseExprsAll.add(curCaseExpr);
                        valueMapping.add(pos);
                    }
                    pos++;
                    lexer.pushback(s);
                    List<GraphTargetItem> caseCmd = commands(registerVars, inFunction, inMethod, forinlevel);
                    caseCmds.add(caseCmd);
                    s = lex();
                }
                List<GraphTargetItem> defCmd = new ArrayList<>();
                if (s.type == SymbolType.DEFAULT) {
                    expectedType(SymbolType.COLON);
                    defCmd = commands(registerVars, inFunction, inMethod, forinlevel);
                    s = lexer.lex();
                }
                expected(s, lexer.yyline(), SymbolType.CURLY_CLOSE);
                ret = new SwitchItem(null, null, switchExpr, caseExprsAll, caseCmds, defCmd, valueMapping);
                break;
            case BREAK:
                ret = new BreakItem(null, 0); //? There is no more than 1 level continue/break in AS1/2
                break;
            case CONTINUE:
                ret = new ContinueItem(null, 0); //? There is no more than 1 level continue/break in AS1/2
                break;
            case RETURN:
                GraphTargetItem retexpr = expression(true, registerVars, inFunction, inMethod, true);
                if (retexpr == null) {
                    retexpr = new DirectValueActionItem(null, 0, new Undefined(), new ArrayList<String>());
                }
                ret = new ReturnActionItem(null, retexpr);
                break;
            case TRY:
                List<GraphTargetItem> tryCommands = new ArrayList<>();
                tryCommands.add(command(registerVars, inFunction, inMethod, forinlevel, true));
                s = lex();
                boolean found = false;
                List<List<GraphTargetItem>> catchCommands = null;
                List<GraphTargetItem> catchExceptions = new ArrayList<>();
                if (s.type == SymbolType.CATCH) {
                    expectedType(SymbolType.PARENT_OPEN);
                    s = lex();
                    expected(s, lexer.yyline(), SymbolType.IDENTIFIER, SymbolType.STRING);
                    catchExceptions.add(pushConst((String) s.value));
                    expectedType(SymbolType.PARENT_CLOSE);
                    catchCommands = new ArrayList<>();
                    List<GraphTargetItem> cc = new ArrayList<>();
                    cc.add(command(registerVars, inFunction, inMethod, forinlevel, true));
                    catchCommands.add(cc);
                    s = lex();
                    found = true;
                }
                List<GraphTargetItem> finallyCommands = null;
                if (s.type == SymbolType.FINALLY) {
                    finallyCommands = new ArrayList<>();
                    finallyCommands.add(command(registerVars, inFunction, inMethod, forinlevel, true));
                    found = true;
                    s = lex();
                }
                if (!found) {
                    expected(s, lexer.yyline(), SymbolType.CATCH, SymbolType.FINALLY);
                }
                lexer.pushback(s);
                ret = new TryActionItem(tryCommands, catchExceptions, catchCommands, finallyCommands);
                break;
            case THROW:
                ret = new ThrowActionItem(null, expression(registerVars, inFunction, inMethod, true));
                break;
            default:
                GraphTargetItem valcmd = expressionCommands(s, registerVars, inFunction, inMethod, forinlevel);
                if (valcmd != null) {
                    ret = valcmd;
                    break;
                }
                if (s.type != SymbolType.SEMICOLON) {
                    lexer.pushback(s);
                }
                if (debugMode) {
                    System.out.println("/command");
                }
                return null;
        }
        s = lex();
        if ((s != null) && (s.type != SymbolType.SEMICOLON)) {
            lexer.pushback(s);
        }
        if (debugMode) {
            System.out.println("/command");
        }
        if (ret == null && (!mustBeCommand)) {
            buf.pushAllBack(lexer);
        }
        lexer.removeListener(buf);
        return ret;

    }

    private GraphTargetItem expression(HashMap<String, Integer> registerVars, boolean inFunction, boolean inMethod, boolean allowRemainder) throws IOException, ParseException {
        return expression(false, registerVars, inFunction, inMethod, allowRemainder);
    }

    /*private List<GraphTargetItem> expressionRemainder(GraphTargetItem expr, HashMap<String, Integer> registerVars, boolean inFunction, boolean inMethod, boolean allowRemainder) throws IOException, ParseException {
     return expressionRemainder(null, registerVars, inFunction, inMethod, allowRemainder);
     }*/
    private GraphTargetItem fixPrecedence(GraphTargetItem expr) {
        GraphTargetItem ret = expr;
        if (expr instanceof BinaryOp) {
            BinaryOp bo = (BinaryOp) expr;
            GraphTargetItem left = bo.getLeftSide();
            //GraphTargetItem right=bo.getRightSide();
            if (left.getPrecedence() > bo.getPrecedence()) {
                if (left instanceof BinaryOp) {
                    BinaryOp leftBo = (BinaryOp) left;
                    bo.setLeftSide(leftBo.getRightSide());
                    leftBo.setRightSide(expr);
                    return left;
                }
            }
        }
        return ret;
    }

    private GraphTargetItem expressionRemainder(GraphTargetItem expr, HashMap<String, Integer> registerVars, boolean inFunction, boolean inMethod, boolean allowRemainder) throws IOException, ParseException {
        GraphTargetItem ret = null;
        ParsedSymbol s = lex();
        switch (s.type) {
            case TERNAR:
                GraphTargetItem terOnTrue = expression(registerVars, inFunction, inMethod, false);
                expectedType(SymbolType.COLON);
                GraphTargetItem terOnFalse = expression(registerVars, inFunction, inMethod, false);
                ret = new TernarOpItem(null, expr, terOnTrue, terOnFalse);
                break;
            case BITAND:
                ret = new BitAndActionItem(null, expr, expression(registerVars, inFunction, inMethod, false));
                break;
            case BITOR:
                ret = new BitOrActionItem(null, expr, expression(registerVars, inFunction, inMethod, false));
                break;
            case DIVIDE:
                ret = new DivideActionItem(null, expr, expression(registerVars, inFunction, inMethod, false));
                break;
            case MODULO:
                ret = new ModuloActionItem(null, expr, expression(registerVars, inFunction, inMethod, false));
                break;
            case EQUALS:
                ret = new EqActionItem(null, expr, expression(registerVars, inFunction, inMethod, false), true/*FIXME SWF version?*/);
                break;
            case STRICT_EQUALS:
                ret = new StrictEqActionItem(null, expr, expression(registerVars, inFunction, inMethod, false));
                break;
            case NOT_EQUAL:
                ret = new NeqActionItem(null, expr, expression(registerVars, inFunction, inMethod, false), true/*FIXME SWF version?*/);
                break;
            case STRICT_NOT_EQUAL:
                ret = new StrictNeqActionItem(null, expr, expression(registerVars, inFunction, inMethod, false));
                break;
            case LOWER_THAN:
                ret = new LtActionItem(null, expr, expression(registerVars, inFunction, inMethod, false), true/*FIXME SWF version?*/);
                break;
            case LOWER_EQUAL:
                ret = new LeActionItem(null, expr, expression(registerVars, inFunction, inMethod, false));
                break;
            case GREATER_THAN:
                ret = new GtActionItem(null, expr, expression(registerVars, inFunction, inMethod, false));
                break;
            case GREATER_EQUAL:
                ret = new GeActionItem(null, expr, expression(registerVars, inFunction, inMethod, false), true/*FIXME SWF version?*/);
                break;
            case AND:
                ret = new AndItem(null, expr, expression(registerVars, inFunction, inMethod, false));
                break;
            case OR:
                ret = new OrItem(null, expr, expression(registerVars, inFunction, inMethod, false));
                break;
            case MINUS:
                ret = new SubtractActionItem(null, expr, expression(registerVars, inFunction, inMethod, false));
                break;
            case MULTIPLY:
                ret = new MultiplyActionItem(null, expr, expression(registerVars, inFunction, inMethod, false));
                break;
            case PLUS:
                ret = new AddActionItem(null, expr, expression(registerVars, inFunction, inMethod, false), true);
                break;
            case XOR:
                ret = new BitXorActionItem(null, expr, expression(registerVars, inFunction, inMethod, false));
                break;
            case AS:

                break;
            case INSTANCEOF:
                ret = new InstanceOfActionItem(null, expr, expression(registerVars, inFunction, inMethod, false));
                break;
            case IS:

                break;
            default:
                lexer.pushback(s);
        }
        ret = fixPrecedence(ret);
        return ret;
    }

    private int brackets(List<GraphTargetItem> ret, HashMap<String, Integer> registerVars, boolean inFunction, boolean inMethod) throws IOException, ParseException {
        ParsedSymbol s = lex();
        int arrCnt = 0;
        if (s.type == SymbolType.BRACKET_OPEN) {
            s = lex();

            while (s.type != SymbolType.BRACKET_CLOSE) {
                if (s.type != SymbolType.COMMA) {
                    lexer.pushback(s);
                }
                arrCnt++;
                ret.add(expression(registerVars, inFunction, inMethod, true));
                s = lex();
                if (!s.isType(SymbolType.COMMA, SymbolType.BRACKET_CLOSE)) {
                    expected(s, lexer.yyline(), SymbolType.COMMA, SymbolType.BRACKET_CLOSE);
                }
            }
        } else {
            lexer.pushback(s);
            return -1;
        }
        return arrCnt;
    }

    private GraphTargetItem commaExpression(HashMap<String, Integer> registerVars, boolean inFunction, boolean inMethod, int forInLevel) throws IOException, ParseException {
        GraphTargetItem cmd = null;
        List<GraphTargetItem> expr = new ArrayList<>();
        ParsedSymbol s;
        do {
            cmd = command(registerVars, inFunction, inMethod, forInLevel, false);
            if (cmd != null) {
                expr.add(cmd);
            }
            s = lex();
        } while (s.type == SymbolType.COMMA && cmd != null);
        lexer.pushback(s);
        if (cmd == null) {
            expr.add(expression(registerVars, inFunction, inMethod, true));
        } else {
            if (!cmd.hasReturnValue()) {
                throw new ParseException("Expression expected", lexer.yyline());
            }
        }
        return new CommaExpressionItem(null, expr);
    }

    private GraphTargetItem expression(boolean allowEmpty, HashMap<String, Integer> registerVars, boolean inFunction, boolean inMethod, boolean allowRemainder) throws IOException, ParseException {
        if (debugMode) {
            System.out.println("expression:");
        }
        GraphTargetItem ret = null;
        ParsedSymbol s = lex();
        boolean existsRemainder = false;
        boolean assocRight = false;
        switch (s.type) {
            case MINUS:
                GraphTargetItem num = expression(registerVars, inFunction, inMethod, true);
                if ((num instanceof DirectValueActionItem)
                        && (((DirectValueActionItem) num).value instanceof Long)) {
                    ((DirectValueActionItem) num).value = -(Long) ((DirectValueActionItem) num).value;
                    ret = num;
                } else if ((num instanceof DirectValueActionItem)
                        && (((DirectValueActionItem) num).value instanceof Double)) {
                    Double d = (Double) ((DirectValueActionItem) num).value;
                    if (d.isInfinite()) {
                        ((DirectValueActionItem) num).value = Double.NEGATIVE_INFINITY;
                    } else {
                        ((DirectValueActionItem) num).value = -d;
                    }
                    ret = (num);
                } else if ((num instanceof DirectValueActionItem)
                        && (((DirectValueActionItem) num).value instanceof Float)) {
                    ((DirectValueActionItem) num).value = -(Float) ((DirectValueActionItem) num).value;
                    ret = (num);
                } else {;
                    ret = (new SubtractActionItem(null, new DirectValueActionItem(null, 0, (Long) 0L, new ArrayList<String>()), num));
                }
                break;
            case TYPEOF:
                expectedType(SymbolType.PARENT_OPEN);
                ret = new TypeOfActionItem(null, expression(registerVars, inFunction, inMethod, true));
                expectedType(SymbolType.PARENT_CLOSE);
                existsRemainder = true;
                break;
            case TRUE:
                ret = new DirectValueActionItem(null, 0, Boolean.TRUE, new ArrayList<String>());
                existsRemainder = true;
                break;
            case NULL:
                ret = new DirectValueActionItem(null, 0, new Null(), new ArrayList<String>());
                existsRemainder = true;
                break;
            case UNDEFINED:
                ret = new DirectValueActionItem(null, 0, new Undefined(), new ArrayList<String>());
                break;
            case FALSE:
                ret = new DirectValueActionItem(null, 0, Boolean.FALSE, new ArrayList<String>());
                existsRemainder = true;
                break;
            case CURLY_OPEN: //Object literal
                s = lex();
                List<GraphTargetItem> objectNames = new ArrayList<>();
                List<GraphTargetItem> objectValues = new ArrayList<>();
                while (s.type != SymbolType.CURLY_CLOSE) {
                    if (s.type != SymbolType.COMMA) {
                        lexer.pushback(s);
                    }
                    objectNames.add(expression(registerVars, inFunction, inMethod, true));
                    expectedType(SymbolType.COLON);
                    objectValues.add(expression(registerVars, inFunction, inMethod, true));
                    s = lex();
                    if (!s.isType(SymbolType.COMMA, SymbolType.CURLY_CLOSE)) {
                        expected(s, lexer.yyline(), SymbolType.COMMA, SymbolType.CURLY_CLOSE);
                    }
                }
                ret = new InitObjectActionItem(null, objectNames, objectValues);
                break;
            case BRACKET_OPEN: //Array literal or just brackets
                lexer.pushback(s);
                List<GraphTargetItem> inBrackets = new ArrayList<>();
                int arrCnt = brackets(inBrackets, registerVars, inFunction, inMethod);
                ret = new InitArrayActionItem(null, inBrackets);
                break;
            case FUNCTION:
                s = lexer.lex();
                String fname = "";
                if (s.type == SymbolType.IDENTIFIER) {
                    fname = s.value.toString();
                } else {
                    lexer.pushback(s);
                }
                ret = function(true, fname, false);
                break;
            case STRING:
                ret = pushConst(s.value.toString());
                ret = memberOrCall(ret, registerVars, inFunction, inMethod);
                existsRemainder = true;
                break;
            case NEWLINE:
                ret = new DirectValueActionItem(null, 0, "\r", new ArrayList<String>());
                existsRemainder = true;
                break;
            case NAN:
                ret = new DirectValueActionItem(null, 0, Double.NaN, new ArrayList<String>());
                existsRemainder = true;
                break;
            case INFINITY:
                ret = new DirectValueActionItem(null, 0, Double.POSITIVE_INFINITY, new ArrayList<String>());
                existsRemainder = true;
                break;
            case INTEGER:
            case DOUBLE:
                ret = new DirectValueActionItem(null, 0, s.value, new ArrayList<String>());
                existsRemainder = true;
                break;
            case DELETE:
                GraphTargetItem varDel = variable(registerVars, inFunction, inMethod);
                if (varDel instanceof GetMemberActionItem) {
                    GetMemberActionItem gm = (GetMemberActionItem) varDel;
                    ret = new DeleteActionItem(null, gm.object, gm.memberName);
                } else {
                    throw new ParseException("Not a property", lexer.yyline());
                }
                break;
            case INCREMENT:
            case DECREMENT: //preincrement
                GraphTargetItem prevar = variable(registerVars, inFunction, inMethod);
                if (s.type == SymbolType.INCREMENT) {
                    ret = new PreIncrementActionItem(null, prevar);
                }
                if (s.type == SymbolType.DECREMENT) {
                    ret = new PreDecrementActionItem(null, prevar);
                }
                existsRemainder = true;
                break;
            case NOT:
                ret = new NotItem(null, expression(registerVars, inFunction, inMethod, true));
                existsRemainder = true;
                break;
            case PARENT_OPEN:
                ret = new ParenthesisItem(null, expression(registerVars, inFunction, inMethod, true));
                expectedType(SymbolType.PARENT_CLOSE);
                ret = memberOrCall(ret, registerVars, inFunction, inMethod);
                existsRemainder = true;
                break;
            case NEW:
                GraphTargetItem newvar = variable(registerVars, inFunction, inMethod);
                expectedType(SymbolType.PARENT_OPEN);
                if (newvar instanceof GetMemberActionItem) {
                    GetMemberActionItem mem = (GetMemberActionItem) newvar;
                    ret = new NewMethodActionItem(null, mem.object, mem.memberName, call(registerVars, inFunction, inMethod));
                } else if (newvar instanceof GetVariableActionItem) {
                    GetVariableActionItem gv = (GetVariableActionItem) newvar;
                    ret = new NewObjectActionItem(null, gv.name, call(registerVars, inFunction, inMethod));
                } else {
                    throw new ParseException("Invalid new item", lexer.yyline());
                }
                break;
            case IDENTIFIER:
            case THIS:
            case EVAL:
                GraphTargetItem var;
                if (s.type == SymbolType.EVAL) {
                    expectedType(SymbolType.PARENT_OPEN);
                    var = new EvalActionItem(null, expression(registerVars, inFunction, inMethod, true));
                    expectedType(SymbolType.PARENT_CLOSE);
                    var = memberOrCall(var, registerVars, inFunction, inMethod);

                } else {
                    lexer.pushback(s);
                    var = variable(registerVars, inFunction, inMethod);
                }

                GetVariableActionItem gva = null;
                GetMemberActionItem gmb = null;
                RegisterNumber reg = null;
                if (var instanceof GetVariableActionItem) {
                    gva = (GetVariableActionItem) var;
                } else if (var instanceof GetMemberActionItem) {
                    gmb = (GetMemberActionItem) var;
                } else if (var instanceof DirectValueActionItem) {
                    if (((DirectValueActionItem) var).value instanceof RegisterNumber) {
                        reg = (RegisterNumber) ((DirectValueActionItem) var).value;
                    }
                }

                s = lex();
                switch (s.type) {
                    case ASSIGN:
                        GraphTargetItem varval = expression(registerVars, inFunction, inMethod, true);
                        if (gva != null) {
                            ret = new SetVariableActionItem(null, gva.name, varval);
                        } else if (gmb != null) {
                            ret = new SetMemberActionItem(null, gmb.object, gmb.memberName, varval);
                        } else if (reg != null) {
                            ret = new StoreRegisterActionItem(null, reg, varval, false);
                        } else {
                            throw new ParseException("Invalid assignment", lexer.yyline());
                        }
                        existsRemainder = true;
                        assocRight = true;
                        break;
                    case ASSIGN_BITAND:
                    case ASSIGN_BITOR:
                    case ASSIGN_DIVIDE:
                    case ASSIGN_MINUS:
                    case ASSIGN_MODULO:
                    case ASSIGN_MULTIPLY:
                    case ASSIGN_PLUS:
                    case ASSIGN_XOR:
                        //List<GraphTargetItem> varset = new ArrayList<>();
                        //varset.addAll(var);
                        if (gva == null && gmb == null) {
                            throw new ParseException("Invalid assignment", lexer.yyline());
                        }
                        GraphTargetItem varval2 = expression(registerVars, inFunction, inMethod, true);

                        switch (s.type) {
                            case ASSIGN_BITAND:
                                if (gva != null) {
                                    ret = new SetVariableActionItem(null, gva.name, new BitAndActionItem(null, gva, varval2));
                                } else {
                                    ret = new SetMemberActionItem(null, gmb.object, gmb.memberName, new BitAndActionItem(null, gmb, varval2));
                                }
                                break;
                            case ASSIGN_BITOR:
                                if (gva != null) {
                                    ret = new SetVariableActionItem(null, gva.name, new BitOrActionItem(null, gva, varval2));
                                } else {
                                    ret = new SetMemberActionItem(null, gmb.object, gmb.memberName, new BitOrActionItem(null, gmb, varval2));
                                }
                                break;
                            case ASSIGN_DIVIDE:
                                if (gva != null) {
                                    ret = new SetVariableActionItem(null, gva.name, new DivideActionItem(null, gva, varval2));
                                } else {
                                    ret = new SetMemberActionItem(null, gmb.object, gmb.memberName, new DivideActionItem(null, gmb, varval2));
                                }
                                break;
                            case ASSIGN_MINUS:
                                if (gva != null) {
                                    ret = new SetVariableActionItem(null, gva.name, new SubtractActionItem(null, gva, varval2));
                                } else {
                                    ret = new SetMemberActionItem(null, gmb.object, gmb.memberName, new SubtractActionItem(null, gmb, varval2));
                                }
                                break;
                            case ASSIGN_MODULO:
                                if (gva != null) {
                                    ret = new SetVariableActionItem(null, gva.name, new ModuloActionItem(null, gva, varval2));
                                } else {
                                    ret = new SetMemberActionItem(null, gmb.object, gmb.memberName, new ModuloActionItem(null, gmb, varval2));
                                }
                                break;
                            case ASSIGN_MULTIPLY:
                                if (gva != null) {
                                    ret = new SetVariableActionItem(null, gva.name, new MultiplyActionItem(null, gva, varval2));
                                } else {
                                    ret = new SetMemberActionItem(null, gmb.object, gmb.memberName, new MultiplyActionItem(null, gmb, varval2));
                                }
                                break;
                            case ASSIGN_PLUS:
                                if (gva != null) {
                                    ret = new SetVariableActionItem(null, gva.name, new AddActionItem(null, gva, varval2, true/*TODO:SWF version?*/));
                                } else {
                                    ret = new SetMemberActionItem(null, gmb.object, gmb.memberName, new AddActionItem(null, gmb, varval2, true/*TODO:SWF version?*/));
                                }
                                break;
                        }
                        existsRemainder = true;
                        assocRight = true;
                        break;
                    case INCREMENT: //postincrement
                        if (gva == null && gmb == null) {
                            throw new ParseException("Invalid assignment", lexer.yyline());
                        }
                        ret = new PostIncrementActionItem(null, var);
                        break;
                    case DECREMENT: //postdecrement
                        if (gva == null && gmb == null) {
                            throw new ParseException("Invalid assignment", lexer.yyline());
                        }
                        ret = new PostDecrementActionItem(null, var);
                        break;
                    case PARENT_OPEN: //function call
                        lexer.pushback(s);
                        ret = memberOrCall(var, registerVars, inFunction, inMethod);
                        existsRemainder = true;
                        break;
                    default:
                        ret = var;
                        lexer.pushback(s);
                        existsRemainder = true;
                    //ret.addAll(expressionRemainder(registerVars, inFunction, inMethod));
                }
                break;
            default:
                GraphTargetItem excmd = expressionCommands(s, registerVars, inFunction, inMethod, -1);
                if (excmd != null) {
                    existsRemainder = true; //?
                    ret = excmd;
                    break;
                }
                lexer.pushback(s);
        }
        if (allowRemainder && existsRemainder) {
            GraphTargetItem rem = ret;
            do {
                rem = expressionRemainder(rem, registerVars, inFunction, inMethod, assocRight);
                if (rem != null) {
                    ret = rem;
                }
            } while ((!assocRight) && (rem != null));
        }
        if (debugMode) {
            System.out.println("/expression");
        }
        return ret;
    }

    private DirectValueActionItem pushConst(String s) throws IOException, ParseException {
        int index = constantPool.indexOf(s);
        if (index == -1) {
            constantPool.add(s);
            index = constantPool.indexOf(s);
        }
        return new DirectValueActionItem(null, 0, new ConstantIndex(index), constantPool);
    }
    private ActionScriptLexer lexer = null;
    private List<String> constantPool;

    public List<GraphTargetItem> treeFromString(String str, List<String> constantPool) throws ParseException, IOException {
        List<GraphTargetItem> retTree = new ArrayList<>();
        this.constantPool = constantPool;
        try {
            lexer = new ActionScriptLexer(new InputStreamReader(new ByteArrayInputStream(str.getBytes("UTF8")), "UTF8"));
        } catch (UnsupportedEncodingException ex) {
            Logger.getLogger(ActionScriptParser.class.getName()).log(Level.SEVERE, null, ex);
            return retTree;
        }
        retTree.addAll(commands(new HashMap<String, Integer>(), false, false, 0));
        if (lexer.lex().type != SymbolType.EOF) {
            throw new ParseException("Parsing finisned before end of the file", lexer.yyline());
        }
        return retTree;
    }

    public List<Action> actionsFromTree(List<GraphTargetItem> tree, List<String> constantPool) {
        ActionSourceGenerator gen = new ActionSourceGenerator(constantPool);
        List<Action> ret = new ArrayList<>();
        List<Object> localDate = new ArrayList<>();
        localDate.add(new HashMap<String, Integer>()); //registerVars
        localDate.add(Boolean.FALSE); //inFunction
        localDate.add(Boolean.FALSE); //inMethod
        localDate.add((Integer) 0); //forInLevel
        List<GraphSourceItem> srcList = gen.generate(localDate, tree);
        for (GraphSourceItem s : srcList) {
            if (s instanceof Action) {
                ret.add((Action) s);
            }
        }
        ret.add(0, new ActionConstantPool(constantPool));
        return ret;
    }

    public List<Action> actionsFromString(String s) throws ParseException, IOException {
        List<String> constantPool = new ArrayList<>();
        List<GraphTargetItem> tree = treeFromString(s, constantPool);
        return actionsFromTree(tree, constantPool);
    }
}
