mirror of https://github.com/bqv/weechat-xmpp
sexps in /xml
parent
efffd451fa
commit
40af811d42
@ -0,0 +1,93 @@
|
||||
#include <cctype>
|
||||
#include <sstream>
|
||||
#include <cassert>
|
||||
#include <exception>
|
||||
|
||||
#include "driver.hh"
|
||||
|
||||
sexp::driver::~driver()
|
||||
{
|
||||
delete(scanner);
|
||||
scanner = nullptr;
|
||||
delete(parser);
|
||||
parser = nullptr;
|
||||
}
|
||||
|
||||
bool sexp::driver::parse(const char *text, std::ostream *debug = nullptr)
|
||||
{
|
||||
assert(text != nullptr);
|
||||
std::istringstream stream{std::string(text)};
|
||||
return parse(stream, debug);
|
||||
}
|
||||
|
||||
bool sexp::driver::parse(std::istream &stream, std::ostream *debug = nullptr)
|
||||
{
|
||||
if(!stream.good() && stream.eof())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return parse_helper(stream, debug);
|
||||
}
|
||||
|
||||
|
||||
bool sexp::driver::parse_helper(std::istream &stream, std::ostream *debug)
|
||||
{
|
||||
delete(scanner);
|
||||
try
|
||||
{
|
||||
scanner = new sexp::scanner(&stream);
|
||||
}
|
||||
catch(std::bad_alloc &ba)
|
||||
{
|
||||
throw std::runtime_error("Failed to allocate scanner");
|
||||
}
|
||||
|
||||
delete(parser);
|
||||
try
|
||||
{
|
||||
parser = new sexp::parser((*scanner) /* scanner */,
|
||||
(*this) /* driver */);
|
||||
}
|
||||
catch(std::bad_alloc &ba)
|
||||
{
|
||||
throw std::runtime_error("Failed to allocate parser");
|
||||
}
|
||||
|
||||
const int accept = 0;
|
||||
if (debug)
|
||||
{
|
||||
parser->set_debug_level(1);
|
||||
parser->set_debug_stream(*debug);
|
||||
}
|
||||
return parser->parse() == accept;
|
||||
}
|
||||
|
||||
void sexp::driver::start_tag(const std::string &name)
|
||||
{
|
||||
auto *stanza = xmpp_stanza_new(context);
|
||||
xmpp_stanza_set_name(stanza, name.data());
|
||||
stack.push_back(stanza);
|
||||
}
|
||||
|
||||
void sexp::driver::end_tag()
|
||||
{
|
||||
auto *stanza = stack.back();
|
||||
stack.pop_back();
|
||||
if (stack.empty())
|
||||
elements.push_back(stanza);
|
||||
else
|
||||
xmpp_stanza_add_child_ex(stack.back(), stanza, false);
|
||||
}
|
||||
|
||||
void sexp::driver::add_text(const std::string &text)
|
||||
{
|
||||
auto *stanza = xmpp_stanza_new(context);
|
||||
xmpp_stanza_set_text(stanza, text.substr(1,text.length()-2).data());
|
||||
xmpp_stanza_add_child_ex(stack.back(), stanza, false);
|
||||
}
|
||||
|
||||
void sexp::driver::add_attr(const std::string &name, const std::string &value)
|
||||
{
|
||||
xmpp_stanza_set_attribute(stack.back(), name.data(),
|
||||
value.substr(1,value.length()-2).data());
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <cstddef>
|
||||
#include <istream>
|
||||
#include <vector>
|
||||
|
||||
#include <strophe.h>
|
||||
|
||||
#include "scanner.hh"
|
||||
#include "parser.tab.hh"
|
||||
|
||||
namespace sexp {
|
||||
|
||||
class driver {
|
||||
public:
|
||||
driver(xmpp_ctx_t *context) : context(context) {}
|
||||
|
||||
virtual ~driver();
|
||||
|
||||
/**
|
||||
* parse - parse from a file
|
||||
* @param text - valid string
|
||||
*/
|
||||
bool parse(const char *text, std::ostream *debug);
|
||||
|
||||
/**
|
||||
* parse - parse from a c++ input stream
|
||||
* @param is - std::istream&, valid input stream
|
||||
*/
|
||||
bool parse(std::istream &iss, std::ostream *debug);
|
||||
|
||||
void start_tag(const std::string &name);
|
||||
void end_tag();
|
||||
void add_text(const std::string &text);
|
||||
void add_attr(const std::string &name, const std::string &value);
|
||||
|
||||
std::vector<xmpp_stanza_t*> elements;
|
||||
|
||||
private:
|
||||
bool parse_helper(std::istream &stream, std::ostream *debug);
|
||||
|
||||
xmpp_ctx_t *context;
|
||||
|
||||
std::vector<xmpp_stanza_t*> stack;
|
||||
sexp::parser *parser = nullptr;
|
||||
sexp::scanner *scanner = nullptr;
|
||||
};
|
||||
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
%option debug
|
||||
%option nodefault
|
||||
%option yyclass="sexp::scanner"
|
||||
%option noyywrap
|
||||
%option c++
|
||||
|
||||
%{
|
||||
#include <string>
|
||||
|
||||
#include "scanner.hh"
|
||||
#undef YY_DECL
|
||||
#define YY_DECL int sexp::scanner::yylex(sexp::parser::semantic_type *const lval, sexp::parser::location_type *loc)
|
||||
|
||||
using token = sexp::parser::token;
|
||||
|
||||
// defaults to NULL
|
||||
#define yyterminate() return token::END
|
||||
|
||||
// update location on matching
|
||||
#define YY_USER_ACTION loc->step(); loc->columns(yyleng);
|
||||
%}
|
||||
|
||||
/* Regular Expressions */
|
||||
%%
|
||||
%{
|
||||
yylval = lval;
|
||||
%}
|
||||
|
||||
"(" {
|
||||
return token::LPAREN;
|
||||
}
|
||||
|
||||
")" {
|
||||
return token::RPAREN;
|
||||
}
|
||||
|
||||
[\n \t\r]+ {
|
||||
// Update line number
|
||||
loc->lines();
|
||||
return token::SPACE;
|
||||
}
|
||||
|
||||
\"([^"\\]|\\.)*\" {
|
||||
yylval->build<std::string>(yytext);
|
||||
return token::STRING;
|
||||
}
|
||||
|
||||
[^ "(:@)]+ {
|
||||
yylval->build<std::string>(yytext);
|
||||
return token::NAME;
|
||||
}
|
||||
|
||||
. {
|
||||
return *yytext;
|
||||
}
|
||||
%%
|
@ -0,0 +1,78 @@
|
||||
%skeleton "lalr1.cc"
|
||||
%require "3.0"
|
||||
%debug
|
||||
%defines
|
||||
|
||||
%define api.namespace {sexp}
|
||||
%define api.parser.class {parser}
|
||||
|
||||
%code requires{
|
||||
namespace sexp {
|
||||
class driver;
|
||||
class scanner;
|
||||
}
|
||||
}
|
||||
|
||||
%parse-param { sexp::scanner &scanner }
|
||||
%parse-param { sexp::driver &driver }
|
||||
|
||||
%code{
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
#include <sstream>
|
||||
|
||||
/* include for all driver functions */
|
||||
#include "driver.hh"
|
||||
|
||||
#undef yylex
|
||||
#define yylex scanner.yylex
|
||||
}
|
||||
|
||||
%define api.value.type variant
|
||||
%define parse.assert
|
||||
%define parse.error verbose
|
||||
|
||||
%token END 0 "end of file"
|
||||
%token LPAREN
|
||||
%token RPAREN
|
||||
%token <std::string> NAME
|
||||
%token <std::string> STRING
|
||||
%token SPACE
|
||||
|
||||
%locations
|
||||
|
||||
%start input
|
||||
|
||||
%%
|
||||
input : ws END | element input;
|
||||
|
||||
element : lparen attributeset rparenq
|
||||
| lparen tag children rparen;
|
||||
|
||||
ws : | gap;
|
||||
gap : SPACE;
|
||||
|
||||
lparen : ws LPAREN;
|
||||
rparen : ws RPAREN { driver.end_tag(); };
|
||||
rparenq : ws RPAREN;
|
||||
|
||||
tag : ws NAME { driver.start_tag($2); }
|
||||
| ws NAME ':' NAME { driver.start_tag($4); };
|
||||
|
||||
attributeset : '@' attributes;
|
||||
|
||||
attributes : ws | gap attribute attributes;
|
||||
|
||||
attribute : lparen ws NAME gap STRING rparenq { driver.add_attr($3, $5); };
|
||||
|
||||
children : ws
|
||||
| gap STRING children { driver.add_text($2); }
|
||||
| gap element children;
|
||||
%%
|
||||
|
||||
void sexp::parser::error(const sexp::parser::location_type &l, const std::string &err_message)
|
||||
{
|
||||
std::ostringstream ss;
|
||||
ss << "parsing " << err_message << " at " << l;
|
||||
throw std::invalid_argument(ss.str());
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef yyFlexLexer
|
||||
#include <FlexLexer.h>
|
||||
#endif
|
||||
|
||||
#include "parser.tab.hh"
|
||||
#include "location.hh"
|
||||
|
||||
namespace sexp {
|
||||
|
||||
class scanner : public yyFlexLexer {
|
||||
public:
|
||||
|
||||
scanner(std::istream *in) : yyFlexLexer(in)
|
||||
{
|
||||
};
|
||||
|
||||
virtual ~scanner() {
|
||||
};
|
||||
|
||||
// get rid of override virtual function warning
|
||||
using FlexLexer::yylex;
|
||||
|
||||
virtual
|
||||
int yylex(sexp::parser::semantic_type *const lval,
|
||||
sexp::parser::location_type *location);
|
||||
// YY_DECL defined in lexer.l
|
||||
// Method body created by flex in lexer.yy.cc
|
||||
|
||||
|
||||
private:
|
||||
/* yyval ptr */
|
||||
sexp::parser::semantic_type *yylval = nullptr;
|
||||
};
|
||||
|
||||
}
|
Loading…
Reference in New Issue