jsonsax.cpp
author Fabien Ninoles <fabien@tzone.org>
Sat, 06 Oct 2012 21:08:55 -0400
changeset 0 d187e7fc9970
permissions -rw-r--r--
First version, c++.

#include "jsonsax.hpp"

#include <assert.h>

#define CASE_IS_SPACE \
	case ' ':		  \
case '\n':			  \
case '\t':			  \
case '\r'

#define CASE_IS_DIGIT \
	case '0':		  \
case '1':			  \
case '2':			  \
case '3':			  \
case '4':			  \
case '5':			  \
case '6':			  \
case '7':			  \
case '8':			  \
case '9'

JSLXParser::JSLXParser(IJSLXBackend* pBackend)
    : m_pBackend(pBackend)
{}
bool JSLXParser::Parse()
{
    return GetNextChar() && ParseValue();
}

bool JSLXParser::_GetNextChar()
{
    if (!m_pBackend->Eof())
    {
        m_lastChar = m_pBackend->GetChar();
        return true;
    }
    return false;
}

bool JSLXParser::TrimWhiteSpace()
{
    do
    {
        switch(m_lastChar)
        {
        CASE_IS_SPACE:
            break;
        default:
            return true;
        }
    }
    while(_GetNextChar());
    return false;
}

bool JSLXParser::GetNextChar(bool trim /* = true */)
{
    if (trim)
    {
        return _GetNextChar() && TrimWhiteSpace();
    }
    return _GetNextChar();
}

bool JSLXParser::ParseValue()
{
    switch(m_lastChar)
    {
    case '[':
        return ParseArray();
    case '{':
        return ParseObject();
    case '"':
        return ParseString();
    case '-':
    CASE_IS_DIGIT:
        return ParseNumber();
    case 'n':
        return ParseNull();
    case 't':
        return ParseTrue();
    case 'f':
        return ParseFalse();
    default:
        return false;
    }
}

bool JSLXParser::ParseObject()
{
    assert(m_lastChar == '{');
    m_pBackend->OnStartObject();
    GetNextChar();
    while(ParsePair())
    {
        switch(m_lastChar)
        {
        case ',':
            GetNextChar();
            break;
        case '}':
            m_pBackend->OnStopObject();
            GetNextChar();
            return true;
        default:
            m_pBackend->OnError("ParseObject: invalid char");
            return false;
        }
    }
    if (m_lastChar != '}')
    {
        m_pBackend->OnError("ParseObject: invalid char");
        return false;
    }
    m_pBackend->OnStopObject();
    GetNextChar();
    return true;
}

bool JSLXParser::ParsePair()
{
    if (m_lastChar != '"')
    {
        m_pBackend->OnError("ParsePair: Invalid Key");
        return false;
    }
    if (!ParseString())
    {
        m_pBackend->OnError("ParsePair: Invalid Key");
        return false;
    }
    if (m_lastChar != ':')
    {
        m_pBackend->OnError("ParsePair: Missing pair separator");
        return false;
    }
    if (!GetNextChar())
    {
        m_pBackend->OnError("ParsePair: Missing value");
        return false;
    }
    return ParseValue();
}

bool JSLXParser::ParseArray()
{
    assert(m_lastChar == '[');
    if (!GetNextChar()) return false;
    while(ParseValue())
    {
        switch(m_lastChar)
        {
        case ',':
            GetNextChar();
            break;
        case ']':
            m_pBackend->OnStopArray();
            GetNextChar();
            return true;
        default:
            m_pBackend->OnError("ParseArray: Missing seperator in array");
            return false;
        }
    }
    if (m_lastChar != ']')
    {
        m_pBackend->OnError("ParseArray: Bad value in array");
        return false;
    }
    m_pBackend->OnStopArray();
    GetNextChar();
    return true;
}

bool JSLXParser::ParseString()
{
    assert(m_lastChar == '"');
    m_pBackend->OnStartString();
    while(GetNextChar())
    {
        switch(m_lastChar)
        {
        case '\\':
            GetNextChar();
            break;
        case '"':
            m_pBackend->OnStopString();
            GetNextChar();
            return true;
        }
    }
    return false;
}

bool JSLXParser::ParseNumber()
{
    assert(m_lastChar == '-' ||
        m_lastChar == '0' ||
        m_lastChar == '1' ||
        m_lastChar == '2' ||
        m_lastChar == '3' ||
        m_lastChar == '4' ||
        m_lastChar == '5' ||
        m_lastChar == '6' ||
        m_lastChar == '7' ||
        m_lastChar == '8' ||
        m_lastChar == '9');

    m_pBackend->OnStartNumber();
    while(GetNextChar())
    {
        switch(m_lastChar)
        {
            // TODO: lookup for correct number format.
        case 'e':
        case '-':
        case '.':
        CASE_IS_DIGIT:
            break;
        default:
            m_pBackend->OnStopNumber();
            return true;
        }
    }
    return true;
}

bool JSLXParser::ParseNull()
{
    if (ParseConstant("null"))
    {
        m_pBackend->OnNull();
        return true;
    }
    return false;
}

bool JSLXParser::ParseTrue()
{
    if (ParseConstant("true"))
    {
        m_pBackend->OnTrue();
        return true;
    }
    return false;
}

bool JSLXParser::ParseFalse()
{
    if (ParseConstant("false"))
    {
        m_pBackend->OnFalse();
        return true;
    }
    return false;
}

bool JSLXParser::ParseConstant(const char* constant)
{
    assert(m_lastChar == constant[0]);
    ++constant;
    while(GetNextChar())
    {
        if (*constant == '\0')
            return true;
        if (*constant != m_lastChar)
            return false;
        constant++;
    }
    return *constant == '\0';
}

#undef CASE_IS_DIGIT
#undef CASE_IS_SPACE