@ -1,3 +1,5 @@ | |||
# Crank | |||
A Tcl like command language made for the Clinl kernel testing | |||
A Tcl like command language made for the Clinl kernel testing | |||
Build it just by building crank.cpp and main.cpp together in a C++17 compiler and you are done. |
@ -0,0 +1,68 @@ | |||
#pragma once | |||
#include "buffer.hpp" | |||
#include "stdlib.h" | |||
#include "string.h" | |||
class arena{ | |||
ksdk::buffer<char> data; | |||
size_t last; | |||
size_t count; | |||
public: | |||
arena() | |||
:last(0) | |||
,count(0) | |||
,data(ksdk::buffer<char>(nullptr,(size_t)0)) | |||
{} | |||
arena(size_t sz) | |||
:last(0) | |||
,count(0) | |||
,data(ksdk::buffer<char>(nullptr,(size_t)0)) | |||
{ | |||
if(sz!=0) | |||
{ | |||
auto v=malloc(sz); | |||
if(v!=nullptr) | |||
{ | |||
data=ksdk::buffer<char>((char*)v,sz); | |||
} | |||
} | |||
} | |||
arena(char* pos,size_t sz) | |||
:last(0) | |||
,count(0) | |||
,data(ksdk::buffer<char>(pos,(size_t)sz)) | |||
{ | |||
} | |||
void* allocate(size_t sz) | |||
{ | |||
auto ret=data.begin()+last; | |||
if(ret<data.end()) | |||
{ | |||
count++; | |||
last+=sz; | |||
return ret; | |||
} | |||
else | |||
{ | |||
return nullptr; | |||
} | |||
} | |||
bool desallocate(void* ptr) | |||
{ | |||
if(ptr>=data.begin() && ptr<data.end()) | |||
{ | |||
count--; | |||
if(count==0) | |||
{ | |||
memset(data.begin(),data.size(),0); | |||
last=0; | |||
} | |||
return true; | |||
} | |||
return false; | |||
} | |||
}; |
@ -0,0 +1,136 @@ | |||
#pragma once | |||
#include <stddef.h> | |||
#include "container.hpp" | |||
namespace __internals{ | |||
template<typename T> | |||
T& ref(T& n) | |||
{ | |||
return n; | |||
} | |||
template<class T> | |||
struct r_op_iterator | |||
{ | |||
r_op_iterator(T* in):it(in){} | |||
T* it; | |||
auto operator++() | |||
{ | |||
return it--; | |||
} | |||
auto operator++(int) | |||
{ | |||
return --it; | |||
} | |||
auto operator--() | |||
{ | |||
return it--; | |||
} | |||
auto operator--(int) | |||
{ | |||
return --it; | |||
} | |||
T& operator*() | |||
{ | |||
return *it; | |||
} | |||
T& operator[](const size_t idx) | |||
{ | |||
return *(it-idx); | |||
} | |||
auto operator==(const r_op_iterator& rhs) | |||
{ | |||
return it==rhs.it; | |||
} | |||
auto operator!=(const r_op_iterator& rhs) | |||
{ | |||
return it!=rhs.it; | |||
} | |||
auto operator<=(const r_op_iterator& rhs) | |||
{ | |||
return (rhs.it<=it); | |||
} | |||
auto operator>=(const r_op_iterator& rhs) | |||
{ | |||
return (rhs.it>=it); | |||
} | |||
auto operator<(const r_op_iterator& rhs) | |||
{ | |||
return (rhs.it<it); | |||
} | |||
auto operator>(const r_op_iterator& rhs) | |||
{ | |||
return (rhs.it>it); | |||
} | |||
}; | |||
} | |||
namespace ksdk{ | |||
template<typename T> | |||
class buffer : public typed_container<T> | |||
{ | |||
protected: | |||
T* _buffer; | |||
size_t _size; | |||
public: | |||
constexpr buffer(T* b, T* e) | |||
:_buffer(b) | |||
,_size(e-b) | |||
{ | |||
} | |||
constexpr buffer() | |||
:_buffer(nullptr) | |||
,_size(0) | |||
{ | |||
} | |||
constexpr buffer(T* b, size_t sz) | |||
:_buffer(b) | |||
,_size(sz) | |||
{ | |||
} | |||
size_t size() | |||
{ | |||
return _size; | |||
} | |||
T* begin() | |||
{ | |||
return _buffer; | |||
} | |||
T* end() | |||
{ | |||
return _buffer+_size; | |||
} | |||
__internals::r_op_iterator<T> rbegin() | |||
{ | |||
return __internals::r_op_iterator<T>(_buffer-1+_size); | |||
} | |||
__internals::r_op_iterator<T> rend() | |||
{ | |||
return __internals::r_op_iterator<T>(_buffer-1); | |||
} | |||
T& operator[](size_t idx) | |||
{ | |||
return _buffer[idx]; | |||
} | |||
bool operator==(buffer<T> oth) | |||
{ | |||
if(size()!=oth.size()) | |||
return false; | |||
for(size_t i=0; i<size(); i++) | |||
if(_buffer[i]!=oth[i]) | |||
return false; | |||
return true; | |||
} | |||
}; | |||
} |
@ -0,0 +1,37 @@ | |||
#pragma once | |||
#include <stddef.h> | |||
namespace __internals{ | |||
/* Enable_if implementation */ | |||
template<bool b, class T = void> | |||
struct enable_if{}; | |||
template<class T> | |||
struct enable_if<true,T>{ | |||
typedef T type; | |||
}; | |||
/* Enable_if helper implementation */ | |||
template<bool b, class T = void> | |||
using enable_if_t = typename enable_if<b,T>::type; | |||
/* static_assert implementaion for comparing integers */ | |||
template<bool b, typename t = enable_if_t<b>> | |||
class check{ | |||
}; | |||
} | |||
namespace ksdk{ | |||
class container | |||
{ | |||
public: | |||
size_t size(); | |||
}; | |||
template<class T> | |||
class typed_container : public container | |||
{ | |||
}; | |||
} |
@ -0,0 +1,203 @@ | |||
#include "crank.h" | |||
kstd::string_view crank_context::eval(kstd::string_view code) | |||
{ | |||
auto ret = eval_no_copy(copy(code)); | |||
return ret; | |||
} | |||
kstd::string_view identity(kstd::string_view args, crank_context& ctx) | |||
{ | |||
return args; | |||
} | |||
kstd::string_view set(kstd::string_view args, crank_context& ctx) | |||
{ | |||
args = skip_whitespace(args); | |||
auto key = extract_token(args); | |||
auto value = skip_linearspace(kstd::string_view(args.begin()+key.size(),args.end())); | |||
return ctx.store(key,value); | |||
} | |||
bool istokenok(int m) | |||
{ | |||
return ( | |||
isalpha(m) | |||
|| isdigit(m) | |||
|| m=='$' | |||
|| m=='-' | |||
|| m=='_' | |||
); | |||
} | |||
bool islinearspace(int m) | |||
{ | |||
return ( | |||
m==' ' | |||
|| m=='\t' | |||
); | |||
} | |||
kstd::string_view skip_whitespace(kstd::string_view code) | |||
{ | |||
auto m = code.begin(); | |||
while(m<code.end() && isspace(*m)) | |||
{ | |||
m++; | |||
} | |||
return kstd::string_view(m,code.end()); | |||
} | |||
kstd::string_view skip_linearspace(kstd::string_view code) | |||
{ | |||
auto m = code.begin(); | |||
while(m<code.end() && islinearspace(*m)) | |||
{ | |||
m++; | |||
} | |||
return kstd::string_view(m,code.end()); | |||
} | |||
template<char bound> | |||
kstd::string_view extract_bounded(kstd::string_view code) | |||
{ | |||
auto m = code.begin(); | |||
while(m<code.end() && *m!=bound) | |||
{ | |||
if(*m=='"') | |||
{ | |||
m++; | |||
m+=extract_string(kstd::string_view(m,code.end())).size(); | |||
m++; | |||
} | |||
else if(*m=='{') | |||
{ | |||
m++; | |||
m+=extract_array(kstd::string_view(m,code.end())).size(); | |||
m++; | |||
} | |||
else if(*m=='[') | |||
{ | |||
m++; | |||
m+=extract_subcommand(kstd::string_view(m,code.end())).size(); | |||
m++; | |||
} | |||
else if(istokenok(*m)) | |||
{ | |||
m+=extract_token(kstd::string_view(m,code.end())).size(); | |||
} | |||
else | |||
{ | |||
m = skip_whitespace(kstd::string_view(m,code.end())).begin(); | |||
} | |||
} | |||
return kstd::string_view(code.begin(), m); | |||
} | |||
kstd::string_view extract_command(kstd::string_view code) | |||
{ | |||
code = skip_whitespace(code); | |||
return extract_bounded<'\n'>(code); | |||
} | |||
kstd::string_view extract_subcommand(kstd::string_view code) | |||
{ | |||
return extract_bounded<']'>(code); | |||
} | |||
kstd::string_view extract_string(kstd::string_view code) | |||
{ | |||
auto m = code.begin(); | |||
while(m<code.end() && *m!='"') | |||
{ | |||
m+=(*m!='\\')?1:2; | |||
} | |||
return kstd::string_view(code.begin(), m); | |||
} | |||
kstd::string_view extract_token(kstd::string_view code) | |||
{ | |||
auto m = code.begin(); | |||
while(m<code.end() && istokenok(*m)) | |||
{ | |||
m++; | |||
} | |||
return kstd::string_view(code.begin(), m); | |||
} | |||
kstd::string_view extract_array(kstd::string_view code) | |||
{ | |||
return extract_bounded<'}'>(code); | |||
} | |||
kstd::string_view crank_context::execute_command(kstd::string_view code) | |||
{ | |||
code = copy_replace(code); | |||
auto token = extract_token(code); | |||
auto symbol = get_symbol(token); | |||
auto args = skip_linearspace(kstd::string_view(code.begin()+token.size(),code.end())); | |||
if(symbol.size()) | |||
{ | |||
if(symbol[0]=='@') | |||
{ | |||
symbol = skip_linearspace(kstd::string_view(symbol.begin()+1, symbol.end())); | |||
while(istokenok(args[0])) | |||
{ | |||
auto argname = extract_token(symbol); | |||
symbol = skip_linearspace(kstd::string_view(symbol.begin()+argname.size(), symbol.end())); | |||
auto argvalue = kstd::string_view(); | |||
if(args[0]=='{') | |||
{ | |||
argvalue=extract_array(kstd::string_view(args.begin()+1, args.end())); | |||
args = skip_linearspace(kstd::string_view(args.begin()+argvalue.size()+2, args.end())); | |||
} | |||
else if(args[0]=='[') | |||
{ | |||
argvalue=crank_context::execute_command(extract_subcommand(kstd::string_view(args.begin()+1, args.end()))); | |||
args = skip_linearspace(kstd::string_view(args.begin()+argvalue.size()+2, args.end())); | |||
} | |||
else if(args[0]=='"') | |||
{ | |||
argvalue=extract_string(kstd::string_view(args.begin()+1, args.end())); | |||
args = skip_linearspace(kstd::string_view(args.begin()+argvalue.size()+2, args.end())); | |||
} | |||
else | |||
{ | |||
argvalue=extract_token(kstd::string_view(args.begin()+1, args.end())); | |||
args = skip_linearspace(kstd::string_view(args.begin()+argvalue.size(), args.end())); | |||
} | |||
store(argname,argvalue); | |||
} | |||
symbol = extract_array(symbol); | |||
auto command = extract_command(symbol); | |||
auto ret = kstd::string_view(); | |||
while(command.size()) | |||
{ | |||
ret = execute_command(command); | |||
symbol = kstd::string_view(symbol.begin()+command.size(),symbol.end()); | |||
command = extract_command(symbol); | |||
} | |||
return ret; | |||
} | |||
else | |||
{ | |||
return symbol; | |||
} | |||
} | |||
else | |||
{ | |||
return get_native(token)(args,this); | |||
} | |||
} | |||
kstd::string_view crank_context::eval_no_copy(kstd::string_view code) | |||
{ | |||
auto command = extract_command(code); | |||
auto ret = kstd::string_view();; | |||
while(command.size()) | |||
{ | |||
ret = execute_command(command); | |||
code = kstd::string_view(code.begin()+command.size(),code.end()); | |||
command = extract_command(code); | |||
} | |||
return ret; | |||
} |
@ -0,0 +1,319 @@ | |||
#pragma once | |||
#include "memory_pool.hpp" | |||
#include "string_view.hpp" | |||
#include "2CL.hpp" | |||
class crank_context; | |||
kstd::string_view identity(kstd::string_view args, crank_context& ctx); | |||
kstd::string_view set(kstd::string_view args, crank_context& ctx); | |||
bool istokenok(int m); | |||
bool islinearspace(int m); | |||
kstd::string_view skip_whitespace(kstd::string_view code); | |||
kstd::string_view skip_linearspace(kstd::string_view code); | |||
template<typename t1, typename t2> | |||
struct pair{ | |||
pair(t1 f, t2 s) | |||
: first{f} | |||
, second{s} | |||
{} | |||
t1 first; | |||
t2 second; | |||
}; | |||
struct crank_macro{ | |||
kstd::string_view(*execute)(kstd::string_view, crank_context&); | |||
crank_macro() | |||
{ | |||
execute=identity; | |||
} | |||
crank_macro(kstd::string_view(*f)(kstd::string_view, crank_context&)) | |||
{ | |||
execute=f; | |||
} | |||
kstd::string_view operator()(kstd::string_view args, crank_context* ctx) | |||
{ | |||
return execute(args, *ctx); | |||
} | |||
}; | |||
struct crank_elem; | |||
template<typename T> | |||
struct crank_ptr{ | |||
T* ptr=nullptr; | |||
memory_pool* memory=nullptr; | |||
crank_ptr(T* _ptr, memory_pool* _memory) | |||
: ptr{_ptr} | |||
, memory{_memory} | |||
{} | |||
crank_ptr(){} | |||
crank_ptr(crank_ptr&) = delete; | |||
crank_ptr(crank_ptr&& oth) | |||
{ | |||
auto tmp = oth.ptr; | |||
oth.ptr=ptr; | |||
ptr=tmp; | |||
auto tmpm = oth.memory; | |||
oth.memory=memory; | |||
memory=tmpm; | |||
} | |||
void operator=(crank_ptr&& oth) | |||
{ | |||
auto tmp = oth.ptr; | |||
oth.ptr=ptr; | |||
ptr=tmp; | |||
auto tmpm = oth.memory; | |||
oth.memory=memory; | |||
memory=tmpm; | |||
} | |||
~crank_ptr() | |||
{ | |||
if(ptr&&memory) | |||
{ | |||
memory->desallocate(ptr); | |||
} | |||
} | |||
}; | |||
struct crank_elem{ | |||
crank_ptr<crank_elem> next; | |||
pair<kstd::string_view,kstd::string_view> data; | |||
}; | |||
struct crank_copy{ | |||
crank_ptr<crank_copy> next; | |||
kstd::string_view data; | |||
}; | |||
struct crank_native{ | |||
crank_ptr<crank_native> next; | |||
pair<kstd::string_view,crank_macro> data; | |||
}; | |||
kstd::string_view skip_whitespace(kstd::string_view code); | |||
kstd::string_view extract_subcommand(kstd::string_view code); | |||
kstd::string_view extract_array(kstd::string_view code); | |||
kstd::string_view extract_token(kstd::string_view code); | |||
kstd::string_view extract_command(kstd::string_view code); | |||
kstd::string_view extract_string(kstd::string_view code); | |||
class crank_context{ | |||
memory_pool memory; | |||
crank_ptr<crank_elem> elements; | |||
crank_ptr<crank_copy> copies; | |||
crank_ptr<crank_native> natives; | |||
public: | |||
kstd::string_view copy(kstd::string_view src) | |||
{ | |||
char* ptr = (char*)memory.allocate(src.size()); | |||
if(ptr) | |||
{ | |||
crank_copy* cpy = (crank_copy*)memory.allocate(sizeof(crank_copy)); | |||
if(cpy) | |||
{ | |||
new(cpy) crank_copy{.next=std::move(copies), .data=kstd::string_view(ptr,src.size())}; | |||
copies = std::move(crank_ptr{cpy, &memory}); | |||
memcpy(ptr,src.begin(),src.size()); | |||
return copies.ptr->data; | |||
} | |||
else | |||
{ | |||
memory.desallocate(ptr); | |||
return kstd::string_view(); | |||
} | |||
} | |||
return kstd::string_view(); | |||
} | |||
kstd::string_view copy_replace(kstd::string_view src) | |||
{ | |||
size_t new_len=src.size(); | |||
auto m = src.begin(); | |||
while(m!=src.end()) | |||
{ | |||
if(!istokenok(*m)) | |||
{ | |||
m++; | |||
} | |||
else | |||
{ | |||
auto token = extract_token(kstd::string_view(m,src.end())); | |||
if(token[0]=='$') | |||
{ | |||
new_len-=token.size(); | |||
new_len+=get_symbol(kstd::string_view(token.begin()+1,token.end())).size(); | |||
} | |||
m+=token.size(); | |||
} | |||
} | |||
char* ptr = (char*)memory.allocate(new_len); | |||
if(ptr) | |||
{ | |||
crank_copy* cpy = (crank_copy*)memory.allocate(sizeof(crank_copy)); | |||
if(cpy) | |||
{ | |||
new(cpy) crank_copy{.next=std::move(copies), .data=kstd::string_view(ptr,src.size())}; | |||
copies = std::move(crank_ptr{cpy, &memory}); | |||
auto dest = kstd::string_view(ptr,new_len); | |||
auto src_it = src.begin(); | |||
auto dest_it = dest.begin(); | |||
while(src_it!=src.end() && dest_it!=dest.end()) | |||
{ | |||
if(!istokenok(*src_it)) | |||
{ | |||
*dest_it = *src_it; | |||
dest_it++; | |||
src_it++; | |||
} | |||
else | |||
{ | |||
auto token = extract_token(kstd::string_view(src_it,src.end())); | |||
if(token[0]=='$') | |||
{ | |||
auto resolved = get_symbol(kstd::string_view(token.begin()+1,token.end())); | |||
memcpy(dest_it,resolved.begin(),resolved.size()); | |||
src_it+=token.size(); | |||
dest_it+=resolved.size(); | |||
} | |||
else | |||
{ | |||
memcpy(dest_it,token.begin(),token.size()); | |||
src_it+=token.size(); | |||
dest_it+=token.size(); | |||
} | |||
} | |||
} | |||
return copies.ptr->data; | |||
} | |||
else | |||
{ | |||
memory.desallocate(ptr); | |||
return kstd::string_view(); | |||
} | |||
} | |||
return kstd::string_view(); | |||
} | |||
kstd::string_view store(kstd::string_view key, kstd::string_view value) | |||
{ | |||
crank_elem* elem = (crank_elem*)memory.allocate(sizeof(crank_elem)); | |||
if(elem) | |||
{ | |||
new(elem) crank_elem{.next=std::move(elements), .data=pair(key, value)}; | |||
elements = std::move(crank_ptr{elem, &memory}); | |||
return value; | |||
} | |||
return kstd::string_view(); | |||
} | |||
kstd::string_view add_native(kstd::string_view key, crank_macro value) | |||
{ | |||
crank_native* native = (crank_native*)memory.allocate(sizeof(crank_native)); | |||
if(native) | |||
{ | |||
new(native) crank_native{.next=std::move(natives), .data=pair(key, value)}; | |||
natives = std::move(crank_ptr{native, &memory}); | |||
return key; | |||
} | |||
return kstd::string_view(); | |||
} | |||
bool string_is_in_range(kstd::string_view range, char* ptr) | |||
{ | |||
return ptr>=range.begin() && ptr<range.end(); | |||
} | |||
bool any_elem_is_in_range(kstd::string_view range) | |||
{ | |||
auto elems = &elements; | |||
while(elems->ptr) | |||
{ | |||
if( | |||
string_is_in_range(range, elems->ptr->data.first.begin()) | |||
|| string_is_in_range(range, elems->ptr->data.second.begin()) | |||
) | |||
{ | |||
return true; | |||
} | |||
elems = &(elems->ptr->next); | |||
} | |||
return false; | |||
} | |||
kstd::string_view get_symbol(kstd::string_view key) | |||
{ | |||
auto elems = &elements; | |||
while(elems->ptr) | |||
{ | |||
if( | |||
elems->ptr->data.first == key | |||
) | |||
{ | |||
return elems->ptr->data.second; | |||
} | |||
elems = &(elems->ptr->next); | |||
} | |||
return kstd::string_view(); | |||
} | |||
crank_macro& get_native(kstd::string_view key) | |||
{ | |||
auto elems = &natives; | |||
while(elems->ptr) | |||
{ | |||
if( | |||
elems->ptr->data.first == key | |||
) | |||
{ | |||
return elems->ptr->data.second; | |||
} | |||
elems = &(elems->ptr->next); | |||
} | |||
static const char* id = "identity"; | |||
return get_native(kstd::string_view((char*)id,8)); | |||
} | |||
void collect() | |||
{ | |||
auto cps = &copies; | |||
while(cps->ptr) | |||
{ | |||
if( | |||
! any_elem_is_in_range(cps->ptr->data) | |||
) | |||
{ | |||
memory.desallocate(cps->ptr->data.begin()); | |||
*cps=std::move(cps->ptr->next); | |||
} | |||
else | |||
{ | |||
cps = &(cps->ptr->next); | |||
} | |||
} | |||
} | |||
crank_context() | |||
{ | |||
static const char* str_set = "set"; | |||
add_native(kstd::string_view((char*)str_set,3), set); | |||
static const char* str_identity = "identity"; | |||
add_native(kstd::string_view((char*)str_identity,8), identity); | |||
} | |||
kstd::string_view execute_command(kstd::string_view code); | |||
kstd::string_view eval(kstd::string_view); | |||
kstd::string_view eval_no_copy(kstd::string_view); | |||
}; |
@ -0,0 +1,46 @@ | |||
/*#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file | |||
#include "catch.hpp" | |||
*/ | |||
#include "crank.h" | |||
#include <iostream> | |||
#include <string> | |||
kstd::string_view set2(kstd::string_view args, crank_context& ctx) | |||
{ | |||
args = skip_whitespace(args); | |||
auto key = extract_token(args); | |||
auto value = skip_linearspace(kstd::string_view(args.begin()+key.size(),args.end())); | |||
for(auto c : value) | |||
std::cout<<c; | |||
std::cout<<" out:"<<value.size()<<std::endl; | |||
return ctx.store(key,value); | |||
} | |||
int main() | |||
{ | |||
const char* set2text = "set2"; | |||
std::string get=""; | |||
crank_context ctx; | |||
ctx.add_native(kstd::string_view((char*)set2text,4), set2); | |||
while((std::getline(std::cin,get)).good()) | |||
{ | |||
auto ret = ctx.eval(kstd::string_view(get.data(), get.size())); | |||
for(auto c : ret) | |||
std::cout<<c; | |||
std::cout<<" out:"<<ret.size()<<std::endl; | |||
} | |||
} | |||
/* | |||
TEST_CASE("crank") | |||
{ | |||
crank_context ctx; | |||
static const char* setter_code = "set sample \"Hello world\""; | |||
kstd::string_view setter_c{(char*)setter_code, 24}; | |||
ctx.eval_no_copy(setter_c); | |||
static const char* getter_code = "$sample"; | |||
kstd::string_view getter_c{(char*)getter_code, 7}; | |||
ctx.eval_no_copy(getter_c); | |||
}*/ |
@ -0,0 +1,39 @@ | |||
#pragma once | |||
#include "arena.hpp" | |||
#include <array> | |||
class memory_pool{ | |||
std::array<arena,256> _pool; | |||
public: | |||
memory_pool() | |||
: _pool() | |||
{ | |||
_pool.fill(arena()); | |||
for(auto& a : _pool) | |||
a = arena(4096); | |||
} | |||
memory_pool(memory_pool&) = delete; | |||
memory_pool(memory_pool&&) = delete; | |||
void* allocate(size_t sz) | |||
{ | |||
for(auto& a : _pool) | |||
{ | |||
void* ptr = a.allocate(sz); | |||
if(ptr!=nullptr) | |||
return ptr; | |||
} | |||
return nullptr; | |||
} | |||
void desallocate(void* ptr) | |||
{ | |||
for(auto& a : _pool) | |||
{ | |||
bool success = a.desallocate(ptr); | |||
if(success) | |||
return; | |||
} | |||
} | |||
}; |
@ -0,0 +1,6 @@ | |||
#pragma once | |||
#include "buffer.hpp" | |||
namespace kstd{ | |||
using string_view = ksdk::buffer<char>; | |||
} |