Take the 2-minute tour ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

This is a framework for a json Parser I put together last night.

Any comments appreciated.

  • JsonLexer.l: Breaks the input into lexemes
  • JsonParser.y: Understands the language syntax
  • JsonParser.h: Header file to bring it all togeter
  • main.cpp: Test harness so it can be tested.
  • Makefile: Sinmple build script.

Testing:

echo '{ "Plop": "Test" }' | ./test

JsonLexer.l

%option c++
%option noyywrap

%{
#define  IN_LEXER
#include "JsonParser.tab.hpp"
%}

DIGIT           [0-9]
DIGIT1          [1-9]
INTNUM          {DIGIT1}{DIGIT}*
FRACT           "."{DIGIT}+
FLOAT           ({INTNUM}|0){FRACT}?
EXP             [eE][+-]?{DIGIT}+
NUMBER          -?{FLOAT}{EXP}?

UNICODE         \\u[A-Fa-f0-9]{4}
ESCAPECHAR      \\["\\/bfnrt]
CHAR            [^"\\]|{ESCAPECHAR}|{UNICODE}
STRING          \"{CHAR}*\"

WHITESPACE      [ \t\n]


%%

\{              {LOG("LEX({)");     return '{';}
\}              {LOG("LEX(})");     return '}';}
\[              {LOG("LEX([)");     return '[';}
\]              {LOG("LEX(])");     return ']';}
,               {LOG("LEX(,)");     return ',';}
:               {LOG("LEX(:)");     return ':';}
true            {LOG("LEX(true)");  return yy::JsonParser::token::JSON_TRUE;}
false           {LOG("LEX(false)"); return yy::JsonParser::token::JSON_FALSE;}
null            {LOG("LEX(null)");  return yy::JsonParser::token::JSON_NULL;}
{STRING}        {LOG("LEX(String)");return yy::JsonParser::token::JSON_STRING;}
{NUMBER}        {LOG("LEX(Number)");return yy::JsonParser::token::JSON_NUMBER;}

{WHITESPACE}    {/*IGNORE*/}

%%

JsonParser.y

%skeleton "lalr1.cc"
%require "2.1a"
%defines
%define "parser_class_name" "JsonParser"

%{

#include "JsonParser.h"
#include <stdexcept>

%}

%parse-param {FlexLexer& lexer}
%lex-param   {FlexLexer& lexer}

%token  JSON_STRING
%token  JSON_NUMBER
%token  JSON_TRUE
%token  JSON_FALSE
%token  JSON_NULL


%%

JsonObject              :   JsonMap                                 {LOG("JsonObject: JsonMap");}
                        |   JsonArray                               {LOG("JsonObject: JsonArray");}

JsonMap                 :   '{' JsonMapValueListOpt '}'             {LOG("JsonMap: { JsonMapValueListOpt }");}
JsonMapValueListOpt     :                                           {LOG("JsonMapValueListOpt: EMPTY");}
                        |   JsonMapValueList                        {LOG("JsonMapValueListOpt: JsonMapValueList");}
JsonMapValueList        :   JsonMapValue                            {LOG("JsonMapValueList: JsonMapValue");}
                        |   JsonMapValueList ',' JsonMapValue       {LOG("JsonMapValueList: JsonMapValueList , JsonMapValue");}
JsonMapValue            :   JSON_STRING ':' JsonValue               {LOG("JsonMapValue: JSON_STRING : JsonValue");}    

JsonArray               :   '[' JsonArrayValueListOpt ']'           {LOG("JsonArray: [ JsonArrayValueListOpt ]");}
JsonArrayValueListOpt   :                                           {LOG("JsonArrayValueListOpt: EMPTY");}
                        |   JsonArrayValueList                      {LOG("JsonArrayValueListOpt: JsonArrayValueList");}
JsonArrayValueList      :   JsonValue                               {LOG("JsonArrayValueList: JsonValue");}
                        |   JsonArrayValueList ',' JsonValue        {LOG("JsonArrayValueList: JsonArrayValueList , JsonValue");}

JsonValue               :   JsonMap                                 {LOG("JsonValue: JsonMap");}
                        |   JsonArray                               {LOG("JsonValue: JsonArray");}
                        |   JSON_STRING                             {LOG("JsonValue: JSON_STRING");}
                        |   JSON_NUMBER                             {LOG("JsonValue: JSON_NUMBER");}
                        |   JSON_TRUE                               {LOG("JsonValue: JSON_TRUE");}
                        |   JSON_FALSE                              {LOG("JsonValue: JSON_FALSE");}
                        |   JSON_NULL                               {LOG("JsonValue: JSON_NULL");}


%%

int yylex(int*, FlexLexer& lexer)
{
    return lexer.yylex();
}

void yy::JsonParser::error(yy::location const&, std::string const& msg)
{
    throw std::runtime_error(msg);
}

main.cpp

#include "JsonParser.tab.hpp"
#include <iostream>

int main()
{
    try
    {
        yyFlexLexer         lexer(&std::cin, &std::cout);
        yy::JsonParser      parser(lexer);

        std::cout << (parser.parse() == 0 ? "OK" : "FAIL") << "\n";
    }
    catch(std::exception const& e)
    {
        std::cout << "Exception: " << e.what() << "\n";
    }
}

JsonParser.h

#ifndef THORSANVIL_JSON_PARSER_H
#define THORSANVIL_JSON_PARSER_H

#ifndef IN_LEXER
#include <FlexLexer.h>
#endif

int yylex(int*, FlexLexer& lexer);

#ifdef DEBUG_LOG
#include <iostream>
#define LOG(x)      std::cout << x << "\n"
#else
#define LOG(x)      0 /*Empty Statement that will be optimized out*/
#endif

#endif

Makefile

YACC            = bison
LEX             = flex
CXX             = g++

CXXFLAGS        = -DDEBUG_LOG

TARGET          = json
LEX_SRC         = $(wildcard *.l)
YACC_SRC        = $(wildcard *.y)
CPP_SRC         = $(filter-out %.lex.cpp %.tab.cpp,$(wildcard *.cpp))
SRC             = $(patsubst %.y,%.tab.cpp,$(YACC_SRC)) $(patsubst %.l,%.lex.cpp,$(LEX_SRC)) $(CPP_SRC)
OBJ             = $(patsubst %.cpp,%.o,$(SRC))


all: $(OBJ)
    $(CXX) -o test $(OBJ) -lFl

clean:
    rm $(OBJ) $(patsubst %.y,%.tab.cpp,$(YACC_SRC)) $(patsubst %.l,%.lex.cpp,$(LEX_SRC))


$(TARGET):  $(OBJ)
    $(CXX)  -o $* $(OBJ)

.PRECIOUS: %.tab.cpp

%.tab.cpp: %.y
    $(YACC) -o $@ -d $<

.PRECIOUS: %.lex.cpp

%.lex.cpp: %.l
    $(LEX) -t $< > $@
share|improve this question
    
What is C++ or object oriented about this? Flex & Bison generate C code. The only C++ code that is see is the test code written in main.cpp. Either you call it a C parser or use flex and bison equivalent for C++ –  apeirogon Oct 8 '12 at 8:30
3  
@apeirogon: Actually flex and bison both generate C++ classes see %option c++ and %skeleton "lalr1.cc". But this is not really relevant. It is the interface the lexer and scanner present to the world that make the code OO. The current interface is OO allowing you to use parser/lexer independently and in a very OO manor. –  Loki Astari Oct 8 '12 at 15:53
1  
@apeirogon: Also the link you provide actually states that those tools have now atrophied while the real flex and bison have continued to be developed. <quote> But releases ended in 1993 and flex++ and bison++ fell out of date as C++, gcc, flex,and bison all evolved </quote>. So I think I will stick to using real development tools rather than somebodies abandoned project. –  Loki Astari Oct 8 '12 at 15:57
    
The real reason that I put C++ rather than C down is that I don't write C anymore. So the glue code is C++. –  Loki Astari Oct 8 '12 at 16:00
    
@LokiAstari: May I ask, why do you not write C anymore? The topic interests me, as I see multiple different people both criticize C++ for its weaknesses and praise it for its strengths, many of which seem like valid arguments to me. –  voithos Oct 14 '12 at 6:27
show 3 more comments

1 Answer

The code looks good: nothing to add or remove.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.