PLUGIN inline lang: "C++" version: "1.0" date: "2022-03-07" author: "Julien BRUGUIER" maintainer: "Julien BRUGUIER " synopsis: "Plugin engine to inline code within files" description: %{ This plugin allows construction of files by inlining SVM code in the file. .P The inlined code shall be surrounded by a starting and an ending tag. %} example: "HTML" %{ .nf #!===SVMBIN=== LOG PLUGIN "svmcom.so" PLUGIN "===PLUGINLIB===" PROCESS "test" CODE "main" INLINE :memory com.device/file, STR/text, com.device/result :com.open com.file < "text.html" -> &file :com.read @&file com.all -> &text :com.open inline.file -> &result :interruption FAILURE fail :interruption MEMORY fail :interruption SECURITY fail :inline.generate @&text "" result PROTECTED :com.command @&result GET -> &text :com.message @&text :shutdown :label fail :com.message STDERR "Error occured" END SEQUENCER inline.generator END .fi .B Content of text.html: .nf Example &i 2 -> &j ?>

2+2=.

&s 0 -> &i :label loop :com.write @&P "\t\t\t\n" :shift @&i &s :shift &i :shift &P :goto loop :when @&i IN &0*10 :shutdown :label ignore :return ?>
isum 0 -> i
" @&i "" @&s "
.fi .B Produces: .nf Example

2+2=4.

isum 0 -> i
00
10
21
33
46
510
615
721
828
936
.fi %} includes: %{ #include #include %} USE TYPE com.device help: "The inline plugin provides a com.device interface to generate the file." WAITING INSTRUCTION com.write com.device STR help: "This instruction is used to render constant parts of the file." DEFINE INSTRUCTION inline.generate STR:file STR:start STR:end PTR:arguments 'PROTECTED' ? %{ SVM_String raw_file = ARGV_VALUE(0,string); SVM_String raw_start = ARGV_VALUE(1,string); SVM_String raw_end = ARGV_VALUE(2,string); std::string file(raw_file.string,raw_file.size); std::string start(raw_start.string,raw_start.size); std::string end(raw_end.string,raw_end.size); std::vector fragments; for( ; ; ) { auto its = file.find(start); if(its==std::string::npos) { fragments.push_back(file); break; } fragments.push_back(file.substr(0,its)); file = file.substr(its+start.size()); auto ite = file.find(end); if(ite==std::string::npos) { fragments.push_back(file); break; } fragments.push_back(file.substr(0,ite)); file = file.substr(ite+end.size()); } bool text = true; bool first = true; for(auto& f:fragments) { SVM_Code code; if(text) { std::ostringstream oss; oss << ":com.write @&P \"\"\"" << f << "\"\"\"\n"; code = ::svm_code_new__raw(svm,"text",oss.str().c_str()); } else { code = ::svm_code_new__raw(svm,"inline",f.c_str()); } SVM_Kernel k = ::svm_kernel_new_code(svm,TRUE,FALSE,argc>4?TRUE:FALSE,nullptr,code); if(first) { SVM_Value_Pointer p = ::svm_parameter_value_get(svm,argv[3]); SVM_Memory_Zone zone = ::svm_memory_zone_new(svm); ::svm_memory_zone_append_internal__raw(svm,zone,AUTOMATIC,::svm_value_pointer_get_size(svm,p)); SVM_Value_Pointer cp = ::svm_memory_allocate(svm,k,zone); ::svm_memory_share(svm,::svm_kernel_get_current(svm),p,k,cp); ::svm_memory_synchronisation_disable(svm,k,cp); ::svm_processor_set_currentpointer(svm,k,cp); first = false; } ::svm_process_kernel_attach(svm,::svm_process_get_current(svm),k,1,nullptr); text = not text; } ::svm_kernel_suspend(svm,::svm_kernel_get_current(svm)); %} help: %{ This instruction is the core of this plugin, and can be called to compute an instance of a inlined file. .P - The first parameter contains the source of the file with inlined code, - The second and third parameters contains the SVM code delimiters within the inlined code (like in HTML), - The fourth parameter are the arguments available within the inlined code, - The optional fifth parameter switch the execution of inlined code in protected mode. .P Please note that the inlined code shall be surrounded by the start and end delimiters. .P Among the arguments available to the inlined code, it is required to put at the first position a com.device value accepting the write operation, like the inline.file device. This first argument can be used by the code to provide the content of the generated file. Other arguments are free and can be used as input and output by the inlined code. .P Finally, it is recommended to run this instruction in a process having the sequencer inline.generator, and use the inline.file device type to generate the file. %} STRUCT inline.file %{ std::ostringstream _oss; std::ostringstream _dump; %} delete default: %{} help: "This structure contains the definition of the device containing the resulting file." FUNCTION inline.device_file_open -> $inline.file %{ struct_file *f = new struct_file(); return NEW_STRUCT(inline,file,f); %} help: "This function is used to open an inline.file device." FUNCTION inline.device_file_print $inline.file -> STR %{ struct_file *f = ARGV_STRUCT(0,inline,file); return ::svm_value_string_new__buffer(svm,f->_dump.str().c_str(),f->_dump.str().size()); %} help: "This function is used to print an inline.file device." FUNCTION inline.device_file_read $inline.file -> STR ? %{ struct_file *f = ARGV_STRUCT(0,inline,file); std::string s = f->_oss.str(); f->_oss.str(""); if(s.empty()) { return NEW_NULL_VALUE(string); } return ::svm_value_string_new__buffer(svm,s.c_str(),s.size()); %} help: "This function is used to read from an inline.file device." FUNCTION inline.device_file_write $inline.file STR %{ struct_file *f = ARGV_STRUCT(0,inline,file); SVM_String rs = ARGV_VALUE(1,string); std::string s(rs.string,rs.size); f->_oss << s; f->_dump << s; %} help: "This function is used to write to an inline.file device." FUNCTION inline.device_file_command $inline.file .* -> VALUE ? %{ struct_file *f = ARGV_STRUCT(0,inline,file); if(argc<2) { ERROR_INTERNAL(DEVICE,"Missing command"); } if(not ::svm_parameter_type_is_keyword(svm,argv[1])) { ERROR_INTERNAL(DEVICE,"Invalid command"); } auto cmd = ARGV_KEYWORD(1); if(cmd=="GET") { std::string s = f->_dump.str(); f->_oss.str(""); f->_dump.str(""); return ::svm_value_string_new__buffer(svm,s.c_str(),s.size()); } return NEW_NULL_VALUE(string); %} help: %{ This function is used to get the full content of an inline.file device, by using the GET command on the device. %} FUNCTION inline.device_file_close $inline.file -> BLN %{ return NEW_VALUE(boolean,TRUE); %} help: "This function is used to close an inline.file device." SEQUENCER inline.generator %{ sequencer_generator() :_main(nullptr) {}; SVM_Kernel _main; std::list _workers; %} create default: %{ %} delete default: %{ if(object->_main) { ::svm_variable_scope_set_local(svm,object->_main); } for(auto& k:object->_workers) { ::svm_variable_scope_set_local(svm,k); } %} current object: %{ if(not object->_workers.empty()) { return object->_workers.front(); } return object->_main; %} attach object: %{ if(argc==0) { if(object->_main!=nullptr) { return FALSE; } ::svm_variable_scope_set_global(svm,kernel); object->_main = kernel; return TRUE; } else { ::svm_variable_scope_set_global(svm,kernel); object->_workers.push_back(kernel); return TRUE; } %} detach object: %{ if(kernel==object->_main) { ::svm_variable_scope_set_local(svm,kernel); object->_main = nullptr; for(auto& k:object->_workers) { ::svm_variable_scope_set_local(svm,k); } object->_workers.clear(); return TRUE; } else { // std::find() fails to compile auto it = object->_workers.begin(); for( ; it!=object->_workers.end() ; ++it) { if(*it==kernel) break; } if(it==object->_workers.end()) return FALSE; if(it!=object->_workers.begin()) { ERROR_INTERNAL(FAILURE,"Removing non next worker."); } SVM_Kernel k = object->_workers.front(); ::svm_variable_scope_set_local(svm,k); object->_workers.pop_front(); if(::svm_kernel_get_interruption(svm,k)!=nullptr) { for(auto& k:object->_workers) { ::svm_variable_scope_set_local(svm,k); } object->_workers.clear(); } if(not object->_workers.empty()) { ::svm_kernel_swap_memory(svm,k,object->_workers.front()); } return TRUE; } %} print object: %{ std::ostringstream oss; oss << "main: "; if(object->_main) { oss << ::svm_kernel_print(svm,object->_main).string; } oss << std::endl; for(auto& k:object->_workers) { oss << "Worker: " << ::svm_kernel_print(svm,k).string << std::endl; } return ::svm_string_new__raw(svm,oss.str().c_str()); %} help: %{ This sequencer is specific to the instruction inline.generate. .P The instruction creates several kernels, attachs them in a particular way supported by this sequencer and suspends the current kernel. Then this sequencer will run the attached kernels in the order the instruction attached them, and will provide to all the kernels the same memory. .P When an attached kernel is interrupted or the last attached kernel is terminated, the main kernel is activated back. The eventual interruption is transmitted to the main kernel. %}