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 $< > $@
%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. \$\endgroup\$