mirror of
https://github.com/yhirose/cpp-peglib.git
synced 2024-12-22 20:05:31 +00:00
Removed server mode from peglint and updated documentation
This commit is contained in:
parent
cc053fd9ad
commit
06fc879371
@ -4,9 +4,9 @@ cpp-peglib
|
|||||||
[![Build Status](https://travis-ci.org/yhirose/cpp-peglib.svg?branch=master)](https://travis-ci.org/yhirose/cpp-peglib)
|
[![Build Status](https://travis-ci.org/yhirose/cpp-peglib.svg?branch=master)](https://travis-ci.org/yhirose/cpp-peglib)
|
||||||
[![Bulid Status](https://ci.appveyor.com/api/projects/status/github/yhirose/cpp-peglib?branch=master&svg=true)](https://ci.appveyor.com/project/yhirose/cpp-peglib)
|
[![Bulid Status](https://ci.appveyor.com/api/projects/status/github/yhirose/cpp-peglib?branch=master&svg=true)](https://ci.appveyor.com/project/yhirose/cpp-peglib)
|
||||||
|
|
||||||
C++11 header-only [PEG](http://en.wikipedia.org/wiki/Parsing_expression_grammar) (Parsing Expression Grammars) library.
|
C++11 header-only [PEG](http://en.wikipedia.org/wiki/Parsing_expression_grammar) (Parsing Expression Grammars) library. You can start using it right away just by including `peglib.h` in your project.
|
||||||
|
|
||||||
*cpp-peglib* tries to provide more expressive parsing experience in a simple way. This library depends on only one header file. So, you can start using it right away just by including `peglib.h` in your project.
|
You can also try the online version at https://yhirose.github.io/cpp-peglib.
|
||||||
|
|
||||||
The PEG syntax is well described on page 2 in the [document](http://www.brynosaurus.com/pub/lang/peg.pdf). *cpp-peglib* also supports the following additional syntax for now:
|
The PEG syntax is well described on page 2 in the [document](http://www.brynosaurus.com/pub/lang/peg.pdf). *cpp-peglib* also supports the following additional syntax for now:
|
||||||
|
|
||||||
@ -433,7 +433,7 @@ peglint - PEG syntax lint utility
|
|||||||
> cmake ..
|
> cmake ..
|
||||||
> make
|
> make
|
||||||
> ./peglint
|
> ./peglint
|
||||||
usage: peglint [--ast] [--optimize_ast_nodes|--opt] [--source text] [--server [PORT]] [--trace] [grammar file path] [source file path]
|
usage: peglint [--ast] [--optimize_ast_nodes|--opt] [--source text] [--trace] [grammar file path] [source file path]
|
||||||
```
|
```
|
||||||
|
|
||||||
### Lint grammar
|
### Lint grammar
|
||||||
|
@ -1,13 +1,4 @@
|
|||||||
cmake_minimum_required(VERSION 2.8)
|
cmake_minimum_required(VERSION 2.8)
|
||||||
include_directories(..)
|
include_directories(..)
|
||||||
add_definitions("-std=c++11")
|
add_definitions("-std=c++11")
|
||||||
add_executable(peglint peglint.cc server.cc)
|
add_executable(peglint peglint.cc)
|
||||||
|
|
||||||
if(WIN32)
|
|
||||||
target_link_libraries(peglint wsock32 ws2_32)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
|
||||||
#set(add_link_deps pthread)
|
|
||||||
target_link_libraries(peglint pthread)
|
|
||||||
endif()
|
|
||||||
|
@ -4,5 +4,5 @@ peglint
|
|||||||
The lint utility for PEG.
|
The lint utility for PEG.
|
||||||
|
|
||||||
```
|
```
|
||||||
usage: peglint [--ast] [--optimize_ast_nodes|--opt] [--server [PORT]] [--trace] [grammar file path] [source file path]
|
usage: peglint [--ast] [--optimize_ast_nodes|--opt] [--trace] [grammar file path] [source file path]
|
||||||
```
|
```
|
||||||
|
2497
lint/httplib.h
2497
lint/httplib.h
File diff suppressed because it is too large
Load Diff
@ -10,8 +10,6 @@
|
|||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
int run_server(int port, const vector<char>& syntax, const vector<char>& source);
|
|
||||||
|
|
||||||
bool read_file(const char* path, vector<char>& buff)
|
bool read_file(const char* path, vector<char>& buff)
|
||||||
{
|
{
|
||||||
ifstream ifs(path, ios::in | ios::binary);
|
ifstream ifs(path, ios::in | ios::binary);
|
||||||
@ -33,8 +31,6 @@ int main(int argc, const char** argv)
|
|||||||
auto opt_help = false;
|
auto opt_help = false;
|
||||||
auto opt_source = false;
|
auto opt_source = false;
|
||||||
vector<char> source;
|
vector<char> source;
|
||||||
auto opt_server = false;
|
|
||||||
int port = 1234;
|
|
||||||
auto opt_trace = false;
|
auto opt_trace = false;
|
||||||
vector<const char*> path_list;
|
vector<const char*> path_list;
|
||||||
|
|
||||||
@ -53,11 +49,6 @@ int main(int argc, const char** argv)
|
|||||||
std::string text = argv[argi++];
|
std::string text = argv[argi++];
|
||||||
source.assign(text.begin(), text.end());
|
source.assign(text.begin(), text.end());
|
||||||
}
|
}
|
||||||
} else if (string("--server") == arg) {
|
|
||||||
opt_server = true;
|
|
||||||
if (argi < argc) {
|
|
||||||
port = std::stoi(argv[argi++]);
|
|
||||||
}
|
|
||||||
} else if (string("--trace") == arg) {
|
} else if (string("--trace") == arg) {
|
||||||
opt_trace = true;
|
opt_trace = true;
|
||||||
} else {
|
} else {
|
||||||
@ -65,28 +56,11 @@ int main(int argc, const char** argv)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((path_list.empty() && !opt_server) || opt_help) {
|
if (path_list.empty() || opt_help) {
|
||||||
cerr << "usage: peglint [--ast] [--optimize_ast_nodes|--opt] [--source text] [--server [PORT]] [--trace] [grammar file path] [source file path]" << endl;
|
cerr << "usage: peglint [--ast] [--optimize_ast_nodes|--opt] [--source text] [--trace] [grammar file path] [source file path]" << endl;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sever mode
|
|
||||||
if (opt_server) {
|
|
||||||
vector<char> syntax;
|
|
||||||
|
|
||||||
if (path_list.size() >= 1 && !read_file(path_list[0], syntax)) {
|
|
||||||
cerr << "can't open the grammar file." << endl;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (path_list.size() >= 2 && !read_file(path_list[1], source)) {
|
|
||||||
cerr << "can't open the code file." << endl;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return run_server(port, syntax, source);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check PEG grammar
|
// Check PEG grammar
|
||||||
auto syntax_path = path_list[0];
|
auto syntax_path = path_list[0];
|
||||||
|
|
||||||
|
371
lint/server.cc
371
lint/server.cc
@ -1,371 +0,0 @@
|
|||||||
#include "httplib.h"
|
|
||||||
#include "peglib.h"
|
|
||||||
#include <cstdio>
|
|
||||||
#include <functional>
|
|
||||||
#include <iomanip>
|
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
using namespace httplib;
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
static string indexHTML = R"(
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<title>PEG Playground</title>
|
|
||||||
<style>
|
|
||||||
* {
|
|
||||||
box-sizing: border-box;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
text-decoration: none;
|
|
||||||
list-style: none;
|
|
||||||
}
|
|
||||||
body {
|
|
||||||
display: flex;
|
|
||||||
display: -webkit-flex;
|
|
||||||
flex-direction: column;
|
|
||||||
-webkit-flex-direction: column;
|
|
||||||
height: 100vh;
|
|
||||||
}
|
|
||||||
#main {
|
|
||||||
flex: 1;
|
|
||||||
-webkit-flex: 1;
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
.editor-container {
|
|
||||||
flex: 1;
|
|
||||||
-webkit-flex: 1;
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
|
||||||
display: -webkit-flex;
|
|
||||||
flex-direction: column;
|
|
||||||
-webkit-flex-direction: column;
|
|
||||||
margin: 8px;
|
|
||||||
}
|
|
||||||
.editor-container:first-child {
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
.editor-header {
|
|
||||||
display: flex;
|
|
||||||
display: -webkit-flex;
|
|
||||||
height: 48px;
|
|
||||||
padding: 4px 8px;
|
|
||||||
}
|
|
||||||
.editor-header > li:last-child {
|
|
||||||
margin-left: auto;
|
|
||||||
}
|
|
||||||
.editor-header > li > span {
|
|
||||||
height: 38px;
|
|
||||||
line-height: 38px;
|
|
||||||
}
|
|
||||||
.editor-header > li > a {
|
|
||||||
height: 38px;
|
|
||||||
line-height: 38px;
|
|
||||||
padding: .3em .5em;
|
|
||||||
border: 1px solid red;
|
|
||||||
}
|
|
||||||
.editor-validation {
|
|
||||||
padding: 9px 11px;
|
|
||||||
color: green;
|
|
||||||
background-color: lightgreen;
|
|
||||||
border-radius: 5px;
|
|
||||||
}
|
|
||||||
.editor-validation-invalid {
|
|
||||||
color: red;
|
|
||||||
background-color: pink;
|
|
||||||
}
|
|
||||||
.editor-area {
|
|
||||||
flex: 1;
|
|
||||||
-webkit-flex: 1;
|
|
||||||
border: 1px solid lightgray;
|
|
||||||
}
|
|
||||||
.editor-info {
|
|
||||||
margin-top: 6px;
|
|
||||||
height: 160px;
|
|
||||||
border: 1px solid lightgray;
|
|
||||||
padding: 8px;
|
|
||||||
}
|
|
||||||
.editor-info li {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<div id="main">
|
|
||||||
<div class="editor-container">
|
|
||||||
<ul class="editor-header">
|
|
||||||
<li><span>Grammar:</span></li>
|
|
||||||
<li><span id="grammar-validation" class="editor-validation">Valid</span></li>
|
|
||||||
</ul>
|
|
||||||
<pre id="grammar-editor" class="editor-area">{{syntax}}</pre>
|
|
||||||
<div id="grammar-info" class="editor-info"></div>
|
|
||||||
</div>
|
|
||||||
<div class="editor-container">
|
|
||||||
<ul class="editor-header">
|
|
||||||
<li><span>Code:</span></li>
|
|
||||||
<li><span id="code-validation" class="editor-validation">Valid</span></li>
|
|
||||||
</ul>
|
|
||||||
<pre id="code-editor" class="editor-area">{{source}}</pre>
|
|
||||||
<pre id="code-ast" class="editor-area"></pre>
|
|
||||||
<pre id="code-ast-optimized" class="editor-area"></pre>
|
|
||||||
<div id="code-info" class="editor-info"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.1.9/ace.js"></script>
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
|
|
||||||
<script>
|
|
||||||
// Setup editros
|
|
||||||
var grammar = ace.edit("grammar-editor");
|
|
||||||
grammar.setShowPrintMargin(false);
|
|
||||||
grammar.setValue(localStorage.getItem('grammarText'));
|
|
||||||
grammar.moveCursorTo(0, 0);
|
|
||||||
|
|
||||||
var code = ace.edit("code-editor");
|
|
||||||
code.setShowPrintMargin(false);
|
|
||||||
code.setValue(localStorage.getItem('codeText'));
|
|
||||||
code.moveCursorTo(0, 0);
|
|
||||||
|
|
||||||
var codeAst = ace.edit("code-ast");
|
|
||||||
codeAst.setShowPrintMargin(false);
|
|
||||||
codeAst.setOptions({
|
|
||||||
readOnly: true,
|
|
||||||
highlightActiveLine: false,
|
|
||||||
highlightGutterLine: false
|
|
||||||
})
|
|
||||||
codeAst.renderer.$cursorLayer.element.style.opacity=0;
|
|
||||||
|
|
||||||
var codeAstOptimized = ace.edit("code-ast-optimized");
|
|
||||||
codeAstOptimized.setShowPrintMargin(false);
|
|
||||||
codeAstOptimized.setOptions({
|
|
||||||
readOnly: true,
|
|
||||||
highlightActiveLine: false,
|
|
||||||
highlightGutterLine: false
|
|
||||||
})
|
|
||||||
codeAstOptimized.renderer.$cursorLayer.element.style.opacity=0;
|
|
||||||
|
|
||||||
var generateErrorListHTML = function (errors) {
|
|
||||||
var html = '<ul>';
|
|
||||||
|
|
||||||
html += $.map(errors, function (x) {
|
|
||||||
return '<li data-ln="' + x.ln + '" data-col="' + x.col + '"><span>' + x.ln + ':' + x.col + '</span> <span>' + x.msg + '</span></li>';
|
|
||||||
}).join('');
|
|
||||||
|
|
||||||
html += '<ul>';
|
|
||||||
|
|
||||||
return html;
|
|
||||||
};
|
|
||||||
|
|
||||||
var parse = function () {
|
|
||||||
var $grammarValidation = $('#grammar-validation');
|
|
||||||
var $grammarInfo = $('#grammar-info');
|
|
||||||
var grammarText = grammar.getValue();
|
|
||||||
|
|
||||||
var $codeValidation = $('#code-validation');
|
|
||||||
var $codeInfo = $('#code-info');
|
|
||||||
var codeText = code.getValue();
|
|
||||||
|
|
||||||
localStorage.setItem('grammarText', grammarText);
|
|
||||||
localStorage.setItem('codeText', codeText);
|
|
||||||
|
|
||||||
$grammarInfo.html('');
|
|
||||||
$grammarValidation.hide();
|
|
||||||
$codeInfo.html('');
|
|
||||||
$codeValidation.hide();
|
|
||||||
codeAst.setValue('');
|
|
||||||
codeAstOptimized.setValue('');
|
|
||||||
|
|
||||||
if (grammarText.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$.post("/parse", {
|
|
||||||
grammar: grammarText,
|
|
||||||
code: codeText
|
|
||||||
}).done(function (data) {
|
|
||||||
var isValid = data.grammar.length === 0;
|
|
||||||
if (isValid) {
|
|
||||||
$grammarValidation.removeClass('editor-validation-invalid').text('Valid').show();
|
|
||||||
|
|
||||||
var isValid = data.code.length === 0;
|
|
||||||
if (isValid) {
|
|
||||||
codeAst.insert(data.ast);
|
|
||||||
codeAstOptimized.insert(data.astOptimized);
|
|
||||||
$codeValidation.removeClass('editor-validation-invalid').text('Valid').show();
|
|
||||||
} else {
|
|
||||||
var html = generateErrorListHTML(data.code);
|
|
||||||
$codeInfo.html(html);
|
|
||||||
$codeValidation.addClass('editor-validation-invalid').text('Invalid').show();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
var html = generateErrorListHTML(data.grammar);
|
|
||||||
$grammarInfo.html(html);
|
|
||||||
$grammarValidation.addClass('editor-validation-invalid').text('Invalid').show();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// Event handing for text editiing
|
|
||||||
var timer;
|
|
||||||
var setupTimer = function () {
|
|
||||||
clearTimeout(timer);
|
|
||||||
timer = setTimeout(parse, 750);
|
|
||||||
};
|
|
||||||
grammar.getSession().on('change', setupTimer);
|
|
||||||
code.getSession().on('change', setupTimer);
|
|
||||||
|
|
||||||
// Event handing in the info area
|
|
||||||
var makeOnClickInInfo = function (editor) {
|
|
||||||
return function () {
|
|
||||||
var el = $(this);
|
|
||||||
editor.navigateTo(el.data('ln') - 1, el.data('col') - 1);
|
|
||||||
editor.focus();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
$('#grammar-info').on('click', 'li', makeOnClickInInfo(grammar));
|
|
||||||
$('#code-info').on('click', 'li', makeOnClickInInfo(code));
|
|
||||||
|
|
||||||
// Show page
|
|
||||||
$('#main').css({
|
|
||||||
'display': 'flex',
|
|
||||||
'display': '-webkit-flex'
|
|
||||||
});
|
|
||||||
|
|
||||||
// Initial parse
|
|
||||||
parse();
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
)";
|
|
||||||
|
|
||||||
// https://stackoverflow.com/questions/7724448/simple-json-string-escape-for-c/33799784#33799784
|
|
||||||
std::string escape_json(const std::string &s) {
|
|
||||||
std::ostringstream o;
|
|
||||||
for (auto c : s) {
|
|
||||||
if (c == '"' || c == '\\' || ('\x00' <= c && c <= '\x1f')) {
|
|
||||||
o << "\\u"
|
|
||||||
<< std::hex << std::setw(4) << std::setfill('0') << (int)c;
|
|
||||||
} else {
|
|
||||||
o << c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return o.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
function<void (size_t, size_t, const string&)> makeJSONFormatter(string& json)
|
|
||||||
{
|
|
||||||
auto init = make_shared<bool>(true);
|
|
||||||
|
|
||||||
return [&json, init](size_t ln, size_t col, const string& msg) mutable {
|
|
||||||
if (!init) {
|
|
||||||
json += ",";
|
|
||||||
}
|
|
||||||
json += "{";
|
|
||||||
json += R"("ln":)" + to_string(ln) + ",";
|
|
||||||
json += R"("col":)" + to_string(col) + ",";
|
|
||||||
json += R"("msg":")" + escape_json(msg) + R"(")";
|
|
||||||
json += "}";
|
|
||||||
*init = false;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
bool parse_grammar(const string& text, peg::parser& peg, string& json)
|
|
||||||
{
|
|
||||||
peg.log = makeJSONFormatter(json);
|
|
||||||
json += "[";
|
|
||||||
auto ret = peg.load_grammar(text.data(), text.size());
|
|
||||||
json += "]";
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool parse_code(const string& text, peg::parser& peg, string& json, shared_ptr<peg::Ast>& ast)
|
|
||||||
{
|
|
||||||
peg.enable_ast();
|
|
||||||
peg.log = makeJSONFormatter(json);
|
|
||||||
json += "[";
|
|
||||||
auto ret = peg.parse_n(text.data(), text.size(), ast);
|
|
||||||
json += "]";
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
string replace_all(const string& str, const char* from, const char* to)
|
|
||||||
{
|
|
||||||
string ret;
|
|
||||||
ret.reserve(str.length());
|
|
||||||
|
|
||||||
size_t from_len = 0;
|
|
||||||
while (from[from_len]) {
|
|
||||||
from_len++;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t start_pos = 0, pos;
|
|
||||||
while ((pos = str.find(from, start_pos)) != string::npos) {
|
|
||||||
ret += str.substr(start_pos, pos - start_pos);
|
|
||||||
ret += to;
|
|
||||||
pos += from_len;
|
|
||||||
start_pos = pos;
|
|
||||||
}
|
|
||||||
ret += str.substr(start_pos);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
int run_server(int port, const vector<char>& syntax, const vector<char>& source)
|
|
||||||
{
|
|
||||||
Server svr;
|
|
||||||
|
|
||||||
svr.Get("/", [&](const Request& /*req*/, Response& res) {
|
|
||||||
indexHTML = replace_all(indexHTML, "{{syntax}}", string(syntax.data(), syntax.size()).c_str());
|
|
||||||
indexHTML = replace_all(indexHTML, "{{source}}", string(source.data(), source.size()).c_str());
|
|
||||||
|
|
||||||
res.set_content(indexHTML, "text/html");
|
|
||||||
});
|
|
||||||
|
|
||||||
svr.Post("/parse", [](const Request& req, Response& res) {
|
|
||||||
const auto& grammarText = req.get_param_value("grammar");
|
|
||||||
|
|
||||||
string grammarResult;
|
|
||||||
string codeResult;
|
|
||||||
string astResult;
|
|
||||||
string astResultOptimized;
|
|
||||||
|
|
||||||
peg::parser peg;
|
|
||||||
auto ret = parse_grammar(grammarText, peg, grammarResult);
|
|
||||||
|
|
||||||
if (ret && peg) {
|
|
||||||
const auto& codeText = req.get_param_value("code");
|
|
||||||
shared_ptr<peg::Ast> ast;
|
|
||||||
if (parse_code(codeText, peg, codeResult, ast)) {
|
|
||||||
astResult = escape_json(peg::ast_to_s(ast));
|
|
||||||
astResultOptimized = escape_json(peg::ast_to_s(peg::AstOptimizer(true).optimize(ast)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
string json;
|
|
||||||
json += "{";
|
|
||||||
json += "\"grammar\":" + grammarResult;
|
|
||||||
if (!codeResult.empty()) {
|
|
||||||
json += ",\"code\":" + codeResult;
|
|
||||||
json += ",\"ast\":\"" + astResult + "\"";
|
|
||||||
json += ",\"astOptimized\":\"" + astResultOptimized + "\"";
|
|
||||||
}
|
|
||||||
json += "}";
|
|
||||||
|
|
||||||
res.set_content(json, "application/json");
|
|
||||||
});
|
|
||||||
|
|
||||||
svr.set_error_handler([](const Request& /*req*/, Response& res) {
|
|
||||||
const char* fmt = "<p>Error Status: <span style='color:red;'>%d</span></p>";
|
|
||||||
char buf[BUFSIZ];
|
|
||||||
snprintf(buf, sizeof(buf), fmt, res.status);
|
|
||||||
res.set_content(buf, "text/html");
|
|
||||||
});
|
|
||||||
|
|
||||||
cerr << "Server running at http://localhost:" << port << "/" << endl;
|
|
||||||
svr.listen("localhost", port);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// vim: et ts=4 sw=4 cin cino={1s ff=unix
|
|
Loading…
Reference in New Issue
Block a user