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