Browse Source

Initial release

master
Ludovic 'Archivist' Lagouardette 5 years ago
parent
commit
7df3c83d58
13 changed files with 790 additions and 0 deletions
  1. +1
    -0
      .gitignore
  2. +4
    -0
      build.sh
  3. +84
    -0
      include/base.hpp
  4. +108
    -0
      include/drop_if.hpp
  5. +168
    -0
      include/inputs.hpp
  6. +110
    -0
      include/parse_tool.h
  7. +91
    -0
      include/tool.h
  8. +90
    -0
      src/main.cpp
  9. +107
    -0
      src/parse_tool.cpp
  10. +17
    -0
      src/tool.cpp
  11. +5
    -0
      tests/test001.r9
  12. +2
    -0
      tests/test002.r9
  13. +3
    -0
      tests/test003.r9

+ 1
- 0
.gitignore View File

@ -47,3 +47,4 @@
# .nfs files are created when an open file is removed but is still being accessed
.nfs*
bin/vlr

+ 4
- 0
build.sh View File

@ -0,0 +1,4 @@
#!/bin/bash
g++ -lstdc++ --std=c++17 -pthread -Iinclude src/*.cpp -o ./bin/vlr -g -lstdc++fs

+ 84
- 0
include/base.hpp View File

@ -0,0 +1,84 @@
#include "tool.h"
#include <deque>
#include <iostream>
#include <fstream>
class identity : public tool
{
std::optional<std::shared_ptr<tool>> n;
tool_env me;
std::deque<std::string> queue;
public:
identity(tool_env t)
: me{t}
{}
virtual void init(std::optional<std::shared_ptr<tool>> next)
{
n = next;
}
virtual void execute()
{
if(has_queue())
{
if(n.has_value())
n.value()->enqueue(queue.front());
queue.pop_front();
}
}
virtual void enqueue(std::string v)
{
queue.push_back(v);
}
virtual bool has_queue()
{
return queue.size()!=0;
}
};
class echo : public tool
{
std::optional<std::shared_ptr<tool>> n;
tool_env me;
std::deque<std::string> queue;
std::optional<std::unique_ptr<std::ofstream> > outlet = std::nullopt;
public:
echo(tool_env t)
: me{t}
{}
virtual void init(std::optional<std::shared_ptr<tool>> next)
{
n = next;
if(me.find("file")!=me.end())
{
outlet = std::make_unique<std::ofstream>(me["file"]);
}
}
virtual void execute()
{
if(has_queue())
{
if(outlet.has_value())
{
(*outlet.value())<<escape(queue.front()) << std::endl;
}
else
{
std::cout << escape(queue.front()) << std::endl;
}
if(n.has_value())
n.value()->enqueue(queue.front());
queue.pop_front();
}
}
virtual void enqueue(std::string v)
{
queue.push_back(v);
}
virtual bool has_queue()
{
return queue.size()!=0;
}
};

+ 108
- 0
include/drop_if.hpp View File

@ -0,0 +1,108 @@
#pragma once
#include "tool.h"
#include <deque>
#include <regex>
class filter_numbers : public tool
{
std::optional<std::shared_ptr<tool>> n;
tool_env me;
std::deque<std::string> queue;
bool filter_in = true;
public:
filter_numbers(tool_env t)
: me{t}
{}
virtual void init(std::optional<std::shared_ptr<tool>> next)
{
n = next;
if(me.find("reverse")!=me.end())
filter_in = false;
}
virtual void execute()
{
if(has_queue())
{
std::string v = queue.front();
queue.pop_front();
bool point_sigil = false;
auto it = v.begin();
if(*it=='-' || *it=='+')
{
++it;
}
for(;it!=v.end();++it)
{
if(!isdigit(*it))
{
if(*it == '.')
{
if(point_sigil)
{
if(n.has_value() && !filter_in)
n.value()->enqueue(v);
return;
}
else
{
point_sigil = true;
}
}
else
{
if(n.has_value() && !filter_in)
n.value()->enqueue(v);
return;
}
}
}
if(n.has_value() && filter_in)
n.value()->enqueue(v);
}
}
virtual void enqueue(std::string v)
{
queue.push_back(v);
}
virtual bool has_queue()
{
return queue.size()!=0;
}
};
class filter_in : public tool
{
std::optional<std::shared_ptr<tool>> n;
tool_env me;
std::deque<std::string> queue;
std::regex pattern;
public:
filter_in(tool_env t)
: me{t}
{}
virtual void init(std::optional<std::shared_ptr<tool>> next)
{
n = next;
pattern = std::regex(me["pattern"]);
}
virtual void execute()
{
if(has_queue())
{
if(n.has_value() && std::regex_match(queue.front(), pattern))
n.value()->enqueue(queue.front());
queue.pop_front();
}
}
virtual void enqueue(std::string v)
{
queue.push_back(v);
}
virtual bool has_queue()
{
return queue.size()!=0;
}
};

+ 168
- 0
include/inputs.hpp View File

@ -0,0 +1,168 @@
#pragma once
#include "tool.h"
#include <iostream>
#include <fstream>
#include <sstream>
#include <array>
#include <string_view>
#include <string>
class file_lines : public tool
{
std::optional<std::shared_ptr<tool>> n;
tool_env me;
std::ifstream source;
bool has_buffered = false;
std::string next;
std::stringstream buffer;
public:
file_lines(std::ifstream&& stream, tool_env t)
: me{t}
{
source = std::move(stream);
}
file_lines(tool_env t)
: me{t}
{
source.open(t["file"]);
}
virtual void init(std::optional<std::shared_ptr<tool>> next)
{
n = next;
}
/*
virtual void execute()
{
if(has_queue())
{
if(n.has_value() && next!="\0")
n.value()->enqueue(next);
has_buffered = false;
}
}
virtual void enqueue(std::string v)
{}
virtual bool has_queue()
{
if(has_buffered)
{
return true;
}
else
{
next = "";
std::array<char, 4096> buff;
if(source.good() && buffer.rdbuf()->in_avail() <= buff.size())
{
if(me["reload"]!="true")
{
source.readsome(&buff.front(), buff.size());
size_t sz = source.gcount();
std::string_view data{&buff.front(),sz};
buffer<<data;
}
else if(buffer.rdbuf()->in_avail()==0)
{
source.clear();
source.seekg(0, std::ios::beg);
while(source >> buffer.rdbuf());
}
}
{
std::string_view data;
do{
buffer.getline(&buff.front(), buff.size());
size_t sz = buffer.gcount();
data = std::string_view{&buff.front(),sz};
next+=data.size();
}
while(data.size() == buff.size());
if(next.empty())
{
return false;
}
has_buffered = true;
return true;
}
}
}*/
virtual void execute()
{
if(has_queue())
{
if(n.has_value())
n.value()->enqueue(next);
has_buffered = false;
}
}
virtual void enqueue(std::string v)
{}
virtual bool has_queue()
{
if(has_buffered) return true;
if(source.good() && !source.eof())
{
std::getline(source, next);
has_buffered=true;
return true;
}else if(me["reload"]=="true"){
source.clear();
source.seekg(0, std::ios::beg);
std::getline(source, next);
has_buffered=true;
return true;
}
return false;
}
};
class stdin_lines : public tool
{
std::optional<std::shared_ptr<tool>> n;
tool_env me;
bool has_buffered = false;
std::string next;
public:
stdin_lines(tool_env t)
: me{t}
{}
virtual void init(std::optional<std::shared_ptr<tool>> next)
{
n = next;
}
virtual void execute()
{
if(has_queue())
{
if(n.has_value())
n.value()->enqueue(next);
has_buffered = false;
}
}
virtual void enqueue(std::string v)
{}
virtual bool has_queue()
{
if(!has_buffered)
{
std::getline(std::cin, next);
has_buffered=true;
}
return true;
}
};

+ 110
- 0
include/parse_tool.h View File

@ -0,0 +1,110 @@
#pragma once
#include "tool.h"
#include <vector>
#include <algorithm>
#include <cassert>
#include <deque>
enum class m_t{
key = 0, value = 1
};
template<typename iterator>
tool_env parse_toolenv(tool_env source, iterator env_start, iterator env_end)
{
std::pair<std::string, std::string> entry;
tool_env lenv = source;
m_t mode = m_t::key;
int rec = 0;
auto it = env_start;
if(it!=env_end) ++it;
for(;it!=env_end;++it)
{
if(*it=='\\')
{
++it;
char c = *it;
switch(c)
{
case 'n':
c = '\n';
break;
case 't':
c = '\t';
break;
}
switch(mode)
{
case m_t::key:
entry.first+=c;
break;
case m_t::value:
entry.second+=c;
break;
}
continue;
}
if(*it=='[')
rec++;
if(*it==']')
rec--;
if(*it=='=' && rec==0 && mode==m_t::key)
{
mode=m_t::value;
continue;
}
if(*it!=',' || rec!=0)
{
switch(mode)
{
case m_t::key:
entry.first+=*it;
break;
case m_t::value:
entry.second+=*it;
break;
}
}
else
{
assert(rec==0);
assert(mode==m_t::value);
mode=m_t::key;
lenv[entry.first]=std::string{entry.second};
entry=std::make_pair(std::string{},std::string{});
}
}
if(mode==m_t::value)
{
lenv[entry.first]=std::string{entry.second};
}
return lenv;
}
class parse_tool : public tool
{
std::optional<std::shared_ptr<tool>> next;
std::deque<std::string> queue;
std::vector<std::shared_ptr<tool>> pipeline;
public:
parse_tool(std::string tool, tool_env env);
virtual void init(std::optional<std::shared_ptr<tool>> next);
virtual void execute();
virtual void enqueue(std::string message)
{
queue.push_back(message);
}
virtual bool has_queue()
{
return queue.size()!=0 || std::any_of(pipeline.begin(), pipeline.end(), [](auto& t){
return t->has_queue();
});
}
};
class pipe : public parse_tool
{
public:
pipe(tool_env env);
};

+ 91
- 0
include/tool.h View File

@ -0,0 +1,91 @@
#pragma once
#include <map>
#include <string>
#include <sstream>
#include <optional>
#include <memory>
using tool_env = std::map<std::string, std::string>;
class tool
{
public:
virtual void init(std::optional<std::shared_ptr<tool>> next) = 0;
virtual void execute() = 0;
virtual void enqueue(std::string) = 0;
virtual bool has_queue() = 0;
};
inline std::string escape(std::string v)
{
std::stringstream sstr;
for(char c : v)
{
switch(c)
{
case '\\':
sstr<<"\\\\";
break;
case '\n':
sstr<<"\\n";
break;
case '\t':
sstr<<"\\t";
break;
case ':':
sstr<<"\\:";
break;
case 0:
sstr<<"\\0";
break;
default:
sstr<<c;
break;
}
}
return sstr.str();
}
inline std::string unescape(std::string v)
{
std::stringstream sstr;
std::stringstream orig{v};
char point;
while(orig.good())
{
orig >> point;
if(point!='\\')
{
sstr << point;
}
else
{
orig >> point;
switch(point)
{
case '\\':
sstr<<"\\";
break;
case 'n':
sstr<<"\n";
break;
case 't':
sstr<<"\t";
break;
case '0':
sstr<<"\0";
break;
case ':':
sstr<<":";
break;
default:
sstr<<point;
break;
}
}
}
return sstr.str();
}
std::optional<std::shared_ptr<tool> > mktool(std::string toolname, tool_env env);

+ 90
- 0
src/main.cpp View File

@ -0,0 +1,90 @@
#include "parse_tool.h"
#include <cassert>
#include <fstream>
#include <sstream>
#include <cstdio>
#include <chrono>
#include <filesystem>
#include <thread>
using namespace std::chrono_literals;
struct parameters_t{
tool_env default_env{};
std::string exec;
std::chrono::microseconds delay = 0us;
};
parameters_t parse_cmd(const std::vector<std::string>& args)
{
parameters_t ret;
auto it = args.begin();
for(;it!=args.end();it++)
{
if((*it)[0]!='-')
{
ret.exec = *it;
}
else
{
switch((*it)[1])
{
case 'c':
++it;
if(it->size()>=2)
ret.default_env = parse_toolenv(ret.default_env, it->begin(), --(it->end()));
case 'd':
ret.delay = std::chrono::microseconds{std::strtoll(it->c_str()+2, nullptr, 10)};
break;
}
}
}
return ret;
}
int MAIN(std::vector<std::string> args)
{
bool used_tmp = false;
std::string code;
auto parameters = parse_cmd(args);
if(parameters.exec.empty())
{
used_tmp = true;
std::stringstream command{};
auto v = std::getenv("EDITOR");
command<< (v!=NULL ? v : "vi");
std::array<char, L_tmpnam> buff;
parameters.exec = std::tmpnam(&buff.front());
command<<" "<<parameters.exec;
std::system(command.str().c_str());
}
std::ifstream input(parameters.exec);
std::stringstream sstr;
while(input >> sstr.rdbuf());
if(used_tmp)
{
std::filesystem::remove(parameters.exec);
}
code = sstr.str();
tool_env glob = parameters.default_env;
parse_tool p{code, glob};
while(p.has_queue())
{
std::this_thread::sleep_for(parameters.delay);
p.execute();
}
return 0;
}
int main(int argc, char** argv)
{
std::vector<std::string> args;
for(int v=1;v<argc;v++)
args.emplace_back(argv[v]);
return MAIN(args);
}

+ 107
- 0
src/parse_tool.cpp View File

@ -0,0 +1,107 @@
#include "parse_tool.h"
#include <sstream>
#include <fstream>
#include <iostream>
#include <algorithm>
#include <cassert>
#include <utility>
#include <filesystem>
parse_tool::parse_tool(std::string tool, tool_env env)
{
std::stringstream generator(tool);
while(generator.good())
{
std::string line;
std::getline(generator,line);
assert(!line.empty());
if((*line.begin())==';') break;
auto env_start = std::find(line.begin(),line.end(),'[');
auto env_end_pos = line.rfind(']');
env_end_pos = env_end_pos >= line.size() ? line.size() : env_end_pos;
auto env_end = line.begin()+env_end_pos;
std::string operand{line.begin(), env_start};
auto lenv = parse_toolenv(env, env_start, env_end);
auto mtool = mktool(operand, lenv);
assert(mtool.has_value());
if(pipeline.size()!=0)
{
pipeline[pipeline.size()-1]->init(mtool);
}
pipeline.push_back(mtool.value());
}
init(next);
}
void parse_tool::init(std::optional<std::shared_ptr<tool>> next)
{
pipeline[pipeline.size()-1]->init(next);
}
void parse_tool::execute()
{
auto it = pipeline.rbegin();
for(;it!=pipeline.rend();it++)
{
if((*it)->has_queue())
{
(*it)->execute();
return;
}
}
pipeline[0]->enqueue(queue.front());
queue.pop_front();
}
std::string mod_open(tool_env env)
{
auto fname = env["file"];
if(!std::filesystem::exists(fname))
{
assert(env.count("_path"));
assert(env.count("module"));
std::vector<std::filesystem::path> path;
std::stringstream curr;
for(auto it : env["_path"])
{
if(it == '\0')
{
path.push_back(curr.str());
curr = std::stringstream{};
}
else
{
curr << it;
}
}
if(curr.str().size())
{
path.push_back(curr.str());
}
for(auto dir : path)
{
auto file = (dir/std::filesystem::path{env["module"]}).replace_extension("r9");
if(!std::filesystem::exists(file))
{
continue;
}
fname = file;
}
}
assert(std::filesystem::exists(fname));
std::ifstream input(fname);
std::stringstream sstr;
while(input >> sstr.rdbuf());
return sstr.str();
}
pipe::pipe(tool_env env)
: parse_tool(mod_open(env), env)
{}

+ 17
- 0
src/tool.cpp View File

@ -0,0 +1,17 @@
#include "tool.h"
#include "drop_if.hpp"
#include "inputs.hpp"
#include "parse_tool.h"
#include "base.hpp"
std::optional<std::shared_ptr<tool> > mktool(std::string toolname, tool_env env)
{
if(toolname=="identity") return std::make_shared<identity>(env);
if(toolname=="echo") return std::make_shared<echo>(env);
if(toolname=="pipe") return std::make_shared<pipe>(env);
if(toolname=="filter_numbers") return std::make_shared<filter_numbers>(env);
if(toolname=="filter_in") return std::make_shared<filter_in>(env);
if(toolname=="fin") return std::make_shared<file_lines>(env);
if(toolname=="stdin") return std::make_shared<stdin_lines>(env);
return std::nullopt;
}

+ 5
- 0
tests/test001.r9 View File

@ -0,0 +1,5 @@
identity
identity[param=value]
pipe[module=test002]
echo[param2=value,param5=value,param9=value]
identity

+ 2
- 0
tests/test002.r9 View File

@ -0,0 +1,2 @@
identity
filter_numbers

+ 3
- 0
tests/test003.r9 View File

@ -0,0 +1,3 @@
fin[file=/proc/cpuinfo,reload=true]
filter_in[pattern=cpu MHz.*]
echo

Loading…
Cancel
Save