@ -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 |