@ -0,0 +1,4 @@ | |||
#!/bin/bash | |||
g++ -lstdc++ --std=c++17 -pthread -Iinclude src/*.cpp -o ./bin/vlr -g -lstdc++fs | |||
@ -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; | |||
} | |||
}; |
@ -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; | |||
} | |||
}; |
@ -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; | |||
} | |||
}; |
@ -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); | |||
}; |
@ -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); |
@ -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); | |||
} | |||
@ -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) | |||
{} |
@ -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; | |||
} |
@ -0,0 +1,5 @@ | |||
identity | |||
identity[param=value] | |||
pipe[module=test002] | |||
echo[param2=value,param5=value,param9=value] | |||
identity |
@ -0,0 +1,2 @@ | |||
identity | |||
filter_numbers |
@ -0,0 +1,3 @@ | |||
fin[file=/proc/cpuinfo,reload=true] | |||
filter_in[pattern=cpu MHz.*] | |||
echo |