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