예제 #1
0
        public static SavedVariablesDictionary parse(string sFileName)
        {
            SavedVariablesDictionary savedVariables = new SavedVariablesDictionary();

            // Note -- file errors are deliberately allowed to bubble to the caller.
            // Note 2: This function reads in the entire file.  There are undoubtedly better ways to do this.
            SavedVariablesFileStream stream = new SavedVariablesFileStream(sFileName);

            for (LuaToken variable = getNextToken(stream); variable != null; variable = getNextToken(stream))
            {
                if (stream.getNextCharacter(false) != '=')
                {
                    throw new InvalidDataException("Expected 'variable_name = value' pairs in saved variables file.");
                }

                savedVariables.Add(((LuaKeywordToken)variable).getKeyword(), getNextToken(stream).getInterpretedValue());
            }

            /*
             * // Sanity check tests
             * // First, Save -> Reload should yield equivalent databases
             * // Second, Save File 1 -> Load File 1 -> Save File 2 should yield identical files
             *
             * string sExportTest = "c:\\sv\\original\\" + sFileName.Substring(sFileName.LastIndexOf('\\'));
             * string sReexportTest = "c:\\sv\\reexport\\" + sFileName.Substring(sFileName.LastIndexOf('\\'));
             *
             * if (sFileName != sExportTest)
             * {
             *  // Test export functionality
             *  export(sExportTest, savedVariables);
             *  // And reimport test file
             *  SavedVariablesDictionary reimport = parse(sExportTest);
             *
             *  if (!savedVariables.Equivalent(reimport))
             *  {
             *      throw new InvalidDataException("Export file does not match import file.");
             *  }
             *
             *  // And test export the reimport
             *  export(sReexportTest, reimport);
             * }
             */

            stream = null;

            return(savedVariables);
        }
예제 #2
0
        private static LuaToken getNextToken(SavedVariablesFileStream stream)
        {
            // Note:
            // The general rule on the true/false parameter of getNextCharacter/peekNextCharacter.
            // That boolean indicates whether the function should parse out comments & white-spaces
            // from the next possible character.  While it's useful to ignore those characters
            // when talking about separators between keywords, those characters should not
            // be ignored when the next keyword is being processed.  Imagine the following
            // example:
            // var1 = 0xc0
            // ffee = 72
            // In this case, if we were processing the numer '0xc0' and asked for the next
            // character ignoring white spaces, 'ffee' would be appended letting var1 = 0xc0ffee
            // when in actuality ffee is the name of the next keyword / variable.
            char?cCharacter = stream.getNextCharacter(false);

            if (cCharacter == null)
            {
                return(null);
            }

            if ((cCharacter >= '0' && cCharacter <= '9') || (cCharacter == '-' || cCharacter == '+'))
            {
                char?cNextCharacter = stream.peekNextCharacter(true);
                // Numeric token
                if (cCharacter == '0' && cNextCharacter != null && Char.ToUpper((char)cNextCharacter) == 'X')
                {
                    // Hexadecimal character
                    long lValue = 0;
                    // Consume 'X'
                    stream.getNextCharacter(true);

                    while (true)
                    {
                        cNextCharacter = stream.peekNextCharacter(true);

                        if (cNextCharacter != null && Char.ToUpper((char)cNextCharacter) >= 'A' && Char.ToUpper((char)cNextCharacter) <= 'F')
                        {
                            lValue = lValue * 16 + (10 + (char)cNextCharacter - 'A');
                        }
                        else if (cNextCharacter >= '0' && (char)cNextCharacter <= '9')
                        {
                            lValue = lValue * 16 + (char)cNextCharacter - '0';
                        }
                        else
                        {
                            break;
                        }

                        // Consume the character we just read in
                        stream.getNextCharacter(true);
                    }
                    return(new LuaIntegerToken(lValue));
                }
                else
                {
                    // Numeric token -- scan for the whole string then choose the number format
                    String sValue     = null;
                    bool   bIsInteger = true;

                    sValue += cCharacter;

                    while (true)
                    {
                        cNextCharacter = stream.peekNextCharacter(true);

                        if (cNextCharacter == 'e' || cNextCharacter == 'E' || cNextCharacter == '.' || cNextCharacter == '-')
                        {
                            bIsInteger = false;
                        }
                        else if (!(cNextCharacter >= '0' && cNextCharacter <= '9'))
                        {
                            break;
                        }

                        sValue += cNextCharacter;
                        // Consume the character we just read in
                        stream.getNextCharacter(true);
                    }

                    if (bIsInteger)
                    {
                        return(new LuaIntegerToken(Int64.Parse(sValue)));
                    }
                    else
                    {
                        return(new LuaDoubleToken(Double.Parse(sValue, System.Globalization.CultureInfo.InvariantCulture)));
                    }
                }
            }
            else if (cCharacter == '"' || cCharacter == '\'')
            {
                string sValue = "";

                while (true)
                {
                    char?cNextCharacter = stream.getNextCharacter(true);

                    // String terminates when it ends with the same character
                    // it began with (i.e. ' is needed to close ', and " for ")
                    if (cNextCharacter == cCharacter)
                    {
                        break;
                    }
                    else if (cNextCharacter == null)
                    {
                        throw new InvalidDataException("Unterminated lua string.");
                    }
                    else if (cNextCharacter == '\\')
                    {
                        // Escape the next character...  Read it in whatever it is.
                        cNextCharacter = stream.getNextCharacter(true);

                        switch (cNextCharacter)
                        {
                        case '0':
                            cNextCharacter = '\0';
                            break;

                        case 'a':
                            cNextCharacter = '\a';
                            break;

                        case 'b':
                            cNextCharacter = '\b';
                            break;

                        case 'f':
                            cNextCharacter = '\f';
                            break;

                        case 'n':
                            cNextCharacter = '\n';
                            break;

                        case 'r':
                            cNextCharacter = '\r';
                            break;

                        case 't':
                            cNextCharacter = '\t';
                            break;

                        case 'v':
                            cNextCharacter = '\v';
                            break;

                        case '\'':
                        case '\"':
                            // These types pass through
                            break;

                        default:
                            // If it's an escape sequence we don't understand
                            // just reproduce it.  Sometimes it's not properly formatted
                            // strings (e.g. !Swatter had some paths that used the '\' from
                            // the path separator without escape codes)
                            sValue += '\\';
                            break;
                        }
                    }
                    sValue += cNextCharacter;
                }

                return(new LuaStringToken(sValue));
            }
            else if (cCharacter == '[')
            {
                LuaToken key = getNextToken(stream);

                if (key == null)
                {
                    throw new InvalidDataException("Expected a field identifier after [");
                }
                else if (stream.getNextCharacter(false) != ']')
                {
                    throw new InvalidDataException("Expected a ] after field key");
                }
                else if (stream.getNextCharacter(false) != '=')
                {
                    throw new InvalidDataException("Expected a value after field key");
                }
                else
                {
                    LuaToken value = getNextToken(stream);

                    if (value == null)
                    {
                        throw new InvalidDataException("Expected value after [" + key + "] = ");
                    }

                    return(new LuaFieldToken(key, value.getInterpretedValue()));
                }
            }
            else if (cCharacter == '{')
            {
                SavedVariablesDictionary items = new SavedVariablesDictionary();
                long lAnonymousKey             = 1;

                while (true)
                {
                    char?cNextCharacter = stream.peekNextCharacter(false);

                    if (cNextCharacter == null)
                    {
                        throw new InvalidDataException("Unterminated lua table (no '}' after '{' in file).");
                    }
                    else if (cNextCharacter == '}')
                    {
                        // Consume the close brackets
                        stream.getNextCharacter(false);
                        break;
                    }
                    else if (cNextCharacter == ',' || cNextCharacter == ';')
                    {
                        stream.getNextCharacter(false);
                    }
                    else
                    {
                        LuaToken nextToken = getNextToken(stream);

                        if (nextToken == null)
                        {
                            throw new InvalidDataException("Unterminated lua table (no '}' after '{' in file).");
                        }

                        // Case 1: [key] = value pair
                        if (nextToken.getTokenType() == LuaTokenTypes.LUA_FIELD)
                        {
                            items.Add(((LuaFieldToken)nextToken).getKeyValueKey(), ((LuaFieldToken)nextToken).getKeyValueValue());
                        }
                        else if (stream.peekNextCharacter(false) == '=')
                        // Case 2: key = value pair
                        {
                            // Consume the '='
                            stream.getNextCharacter(false);
                            switch (nextToken.getTokenType())
                            {
                            case LuaTokenTypes.LUA_INTEGER:
                            case LuaTokenTypes.LUA_KEYWORD:
                            case LuaTokenTypes.LUA_STRING:
                                items.Add((IComparable)nextToken.getValue(), getNextToken(stream).getInterpretedValue());
                                break;

                            default:
                                throw new InvalidDataException("Don't know how to hash by " + nextToken.getTokenType() + " token type.");
                            }
                        }
                        else
                        // Case 3: value
                        {
                            items.Add(lAnonymousKey++, nextToken.getInterpretedValue());
                        }
                    }
                }

                return(new LuaTableToken(items));
            }
            else if ((cCharacter >= 'a' && cCharacter <= 'z') || (cCharacter >= 'A' && cCharacter <= 'Z') || cCharacter == '_')
            {
                string sKeyword = null;

                sKeyword += cCharacter;

                while (true)
                {
                    char?cNextCharacter = stream.peekNextCharacter(true);

                    if ((cNextCharacter >= 'a' && cNextCharacter <= 'z') || (cNextCharacter >= 'A' && cNextCharacter <= 'Z') || (cNextCharacter >= '0' && cNextCharacter <= '9') || cNextCharacter == '_')
                    {
                        sKeyword += stream.getNextCharacter(true);
                    }
                    else
                    {
                        return(new LuaKeywordToken(sKeyword));
                    }
                }
            }

            throw new InvalidDataException("Can't parse string " + stream.getErrorContext());
        }