diff --git a/.gitignore b/.gitignore index f4d9441..9ae1522 100644 --- a/.gitignore +++ b/.gitignore @@ -47,3 +47,4 @@ # .nfs files are created when an open file is removed but is still being accessed .nfs* +bin/vlr diff --git a/build.sh b/build.sh new file mode 100644 index 0000000..1d2403b --- /dev/null +++ b/build.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +g++ -lstdc++ --std=c++17 -pthread -Iinclude src/*.cpp -o ./bin/vlr -g -lstdc++fs + diff --git a/include/base.hpp b/include/base.hpp new file mode 100644 index 0000000..1c2e413 --- /dev/null +++ b/include/base.hpp @@ -0,0 +1,84 @@ +#include "tool.h" +#include +#include +#include + +class identity : public tool +{ + std::optional> n; + tool_env me; + std::deque queue; +public: + identity(tool_env t) + : me{t} + {} + + virtual void init(std::optional> 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> n; + tool_env me; + std::deque queue; + std::optional > outlet = std::nullopt; +public: + echo(tool_env t) + : me{t} + {} + + virtual void init(std::optional> next) + { + n = next; + if(me.find("file")!=me.end()) + { + outlet = std::make_unique(me["file"]); + } + } + virtual void execute() + { + if(has_queue()) + { + if(outlet.has_value()) + { + (*outlet.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; + } +}; \ No newline at end of file diff --git a/include/drop_if.hpp b/include/drop_if.hpp new file mode 100644 index 0000000..163eb84 --- /dev/null +++ b/include/drop_if.hpp @@ -0,0 +1,108 @@ +#pragma once +#include "tool.h" +#include +#include + +class filter_numbers : public tool +{ + std::optional> n; + tool_env me; + std::deque queue; + bool filter_in = true; +public: + filter_numbers(tool_env t) + : me{t} + {} + + virtual void init(std::optional> 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> n; + tool_env me; + std::deque queue; + std::regex pattern; +public: + filter_in(tool_env t) + : me{t} + {} + + virtual void init(std::optional> 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; + } +}; \ No newline at end of file diff --git a/include/inputs.hpp b/include/inputs.hpp new file mode 100644 index 0000000..643eb0f --- /dev/null +++ b/include/inputs.hpp @@ -0,0 +1,168 @@ +#pragma once +#include "tool.h" +#include +#include +#include +#include +#include +#include + +class file_lines : public tool +{ + std::optional> 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> 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 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<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> n; + tool_env me; + bool has_buffered = false; + std::string next; +public: + + stdin_lines(tool_env t) + : me{t} + {} + + virtual void init(std::optional> 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; + } +}; \ No newline at end of file diff --git a/include/parse_tool.h b/include/parse_tool.h new file mode 100644 index 0000000..8e6af2a --- /dev/null +++ b/include/parse_tool.h @@ -0,0 +1,110 @@ +#pragma once +#include "tool.h" +#include +#include +#include +#include + +enum class m_t{ + key = 0, value = 1 +}; + +template +tool_env parse_toolenv(tool_env source, iterator env_start, iterator env_end) +{ + std::pair 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> next; + std::deque queue; + std::vector> pipeline; +public: + parse_tool(std::string tool, tool_env env); + virtual void init(std::optional> 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); +}; \ No newline at end of file diff --git a/include/tool.h b/include/tool.h new file mode 100644 index 0000000..10b9039 --- /dev/null +++ b/include/tool.h @@ -0,0 +1,91 @@ +#pragma once +#include +#include +#include +#include +#include + +using tool_env = std::map; + +class tool +{ +public: + virtual void init(std::optional> 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<> 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< > mktool(std::string toolname, tool_env env); \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..5793830 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,90 @@ +#include "parse_tool.h" +#include +#include +#include +#include +#include +#include +#include + +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& 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 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 buff; + parameters.exec = std::tmpnam(&buff.front()); + + command<<" "<> 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 args; + for(int v=1;v +#include +#include +#include +#include +#include +#include + +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> 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 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) +{} \ No newline at end of file diff --git a/src/tool.cpp b/src/tool.cpp new file mode 100644 index 0000000..c227433 --- /dev/null +++ b/src/tool.cpp @@ -0,0 +1,17 @@ +#include "tool.h" +#include "drop_if.hpp" +#include "inputs.hpp" +#include "parse_tool.h" +#include "base.hpp" + +std::optional > mktool(std::string toolname, tool_env env) +{ + if(toolname=="identity") return std::make_shared(env); + if(toolname=="echo") return std::make_shared(env); + if(toolname=="pipe") return std::make_shared(env); + if(toolname=="filter_numbers") return std::make_shared(env); + if(toolname=="filter_in") return std::make_shared(env); + if(toolname=="fin") return std::make_shared(env); + if(toolname=="stdin") return std::make_shared(env); + return std::nullopt; +} \ No newline at end of file diff --git a/tests/test001.r9 b/tests/test001.r9 new file mode 100644 index 0000000..78b6af4 --- /dev/null +++ b/tests/test001.r9 @@ -0,0 +1,5 @@ +identity +identity[param=value] +pipe[module=test002] +echo[param2=value,param5=value,param9=value] +identity \ No newline at end of file diff --git a/tests/test002.r9 b/tests/test002.r9 new file mode 100644 index 0000000..95b9426 --- /dev/null +++ b/tests/test002.r9 @@ -0,0 +1,2 @@ +identity +filter_numbers \ No newline at end of file diff --git a/tests/test003.r9 b/tests/test003.r9 new file mode 100644 index 0000000..d356010 --- /dev/null +++ b/tests/test003.r9 @@ -0,0 +1,3 @@ +fin[file=/proc/cpuinfo,reload=true] +filter_in[pattern=cpu MHz.*] +echo \ No newline at end of file