Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How could I refactor this into more manageable code?

Is this code even complex enough to deserve a higher level of abstraction?

public static JsonStructure Parse(string jsonText)
{
    var result = default(JsonStructure);
    var structureStack = new Stack<JsonStructure>();
    var keyStack = new Stack<string>();
    var current = default(JsonStructure);
    var currentState = ParserState.Begin;
    var key = default(string);
    var value = default(object);

    foreach (var token in Lexer.Tokenize(jsonText))
    {
        switch (currentState)
        {
            case ParserState.Begin:
                switch (token.Type)
                {
                    case TokenType.BeginObject:
                        currentState = ParserState.Name;
                        current = result = new JsonObject();
                        break;
                    case TokenType.BeginArray:
                        currentState = ParserState.Value;
                        current = result = new JsonArray();
                        break;
                    default:
                        throw new JsonException(token, currentState);
                }
                break;
            case ParserState.Name:
                switch (token.Type)
                {
                    case TokenType.String:
                        currentState = ParserState.NameSeparator;
                        key = (string)token.Value;
                        break;
                    default:
                        throw new JsonException(token, currentState);
                }
                break;
            case ParserState.NameSeparator:
                switch (token.Type)
                {
                    case TokenType.NameSeparator:
                        currentState = ParserState.Value;
                        break;
                    default:
                        throw new JsonException(token, currentState);
                }
                break;
            case ParserState.Value:
                switch (token.Type)
                {
                    case TokenType.Number:
                    case TokenType.String:
                    case TokenType.True:
                    case TokenType.False:
                    case TokenType.Null:
                        currentState = ParserState.ValueSeparator;
                        value = token.Value;
                        break;
                    case TokenType.BeginObject:
                        structureStack.Push(current);
                        keyStack.Push(key);
                        currentState = ParserState.Name;
                        current = new JsonObject();
                        break;
                    case TokenType.BeginArray:
                        structureStack.Push(current);
                        currentState = ParserState.Value;
                        current = new JsonArray();
                        break;
                    default:
                        throw new JsonException(token, currentState);
                }
                break;
            case ParserState.ValueSeparator:
                var jsonObject = (current as JsonObject);
                var jsonArray = (current as JsonArray);
                if (jsonObject != null)
                {
                    jsonObject.Add(key, value);
                    currentState = ParserState.Name;
                }
                if (jsonArray != null)
                {
                    jsonArray.Add(value);
                    currentState = ParserState.Value;
                }
                switch (token.Type)
                {
                    case TokenType.EndObject:
                    case TokenType.EndArray:
                        currentState = ParserState.End;
                        break;
                    case TokenType.ValueSeparator:
                        break;
                    default:
                        throw new JsonException(token, currentState);
                }
                break;
            case ParserState.End:
                switch (token.Type)
                {
                    case TokenType.EndObject:
                    case TokenType.EndArray:
                    case TokenType.ValueSeparator:
                        var previous = structureStack.Pop();
                        var previousJsonObject = (previous as JsonObject);
                        var previousJsonArray = (previous as JsonArray);
                        if (previousJsonObject != null)
                        {
                            previousJsonObject.Add(keyStack.Pop(), current);
                            currentState = ParserState.Name;
                        }
                        if (previousJsonArray != null)
                        {
                            previousJsonArray.Add(current);
                            currentState = ParserState.Value;
                        }
                        if (token.Type != TokenType.ValueSeparator)
                        {
                            currentState = ParserState.End;
                        }
                        current = previous;
                        break;
                    default:
                        throw new JsonException(token, currentState);
                }
                break;
            default:
                break;
        }
    }
    return result;
}
like image 296
ChaosPandion Avatar asked Feb 03 '23 05:02

ChaosPandion


1 Answers

Without looking at it in detail, as you are parsing based on state, could you use the state pattern to break it up and parse each bit in a separate class based on the state?

something like this might be a start, although this is just pseudo code...

public interface IParserState
    {
    IParserState ParseToken (IToken token);
    }

public class BeginState : IParserState
    {
    private readonly Stack<JsonStructure> m_structureStack;
    private readonly Stack<String> m_keyStack;

    public BeginState (Stack<JsonStructure> structureStack, Stack<String> keyStack)
        {
        m_structureStack = structureStack;
        m_keyStack = keyStack;
        }

    public IParserState ParseToken(IToken token)
        {
        switch (token.Type)
            {
            case TokenType.OpenBrace:
                return new ObjectKeyParserState(m_structureStack,m_keyStack);
            case TokenType.OpenBracket:
                return new ArrayValueParserState(m_structureStack, m_keyStack);
            default:
                throw new JsonException (token);    
            }
        }
    }

public class ObjectKeyParserState : IParserState
    {
    private readonly Stack<JsonStructure> m_structureStack;
    private readonly Stack<String> m_keyStack;
    private readonly JsonObject m_current;

    public ObjectKeyParserState (Stack<JsonStructure> structureStack, Stack<String> keyStack)
        {
        m_current = new JsonObject();
        }

    public IParserState ParseToken (IToken token)
        {
        switch (token.Type)
            {
            case TokenType.StringLiteral:
                key = (string)token.Value;
                return new ColonSeperatorParserState(m_structureStack, m_keyStack, m_current,key);
            default:
                throw new JsonException(token);
            }
        }
like image 95
Sam Holder Avatar answered Feb 07 '23 11:02

Sam Holder