The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

The Kollos code

Table of contents

* [About Kollos](#about-kollos) * [Kollos object](#kollos-object) * [Kollos Lua interpreter](#kollos-lua-interpreter) * [Kollos registry objects](#kollos-registry-objects) * [Kollos recognizer registry object](#kollos-recognizer-registry-object) * [Kollos semantics](#kollos-semantics) * [VM operations](#vm-operations) * [VM debug operation](#vm-debug-operation) * [VM no-op operation](#vm-no-op-operation) * [VM bail operation](#vm-bail-operation) * [VM result operations](#vm-result-operations) * [VM "result is undef" operation](#vm-result-is-undef-operation) * [VM "result is token value" operation](#vm-result-is-token-value-operation) * [VM "result is N of RHS" operation](#vm-result-is-n-of-rhs-operation) * [VM "result is N of sequence" operation](#vm-result-is-n-of-sequence-operation) * [VM operation: result is constant](#vm-operation-result-is-constant) * [Operation of the values array](#operation-of-the-values-array) * [VM "push undef" operation](#vm-push-undef-operation) * [VM "push one" operation](#vm-push-one-operation) * [Find current token literal](#find-current-token-literal) * [VM "push values" operation](#vm-push-values-operation) * [VM operation: push start location](#vm-operation-push-start-location) * [VM operation: push length](#vm-operation-push-length) * [VM operation: push G1 start location](#vm-operation-push-g1-start-location) * [VM operation: push G1 length](#vm-operation-push-g1-length) * [VM operation: push constant onto values array](#vm-operation-push-constant-onto-values-array) * [VM operation: set the array blessing](#vm-operation-set-the-array-blessing) * [VM operation: result is array](#vm-operation-result-is-array) * [VM operation: callback](#vm-operation-callback) * [Run the virtual machine](#run-the-virtual-machine) * [Find and perform the VM operations](#find-and-perform-the-vm-operations) * [VM-related utilities for use in the Perl code](#vm-related-utilities-for-use-in-the-perl-code) * [Return operation key given its name](#return-operation-key-given-its-name) * [Return operation name given its key](#return-operation-name-given-its-key) * [Register a constant](#register-a-constant) * [Register semantics for a token](#register-semantics-for-a-token) * [Register semantics for a nulling symbol](#register-semantics-for-a-nulling-symbol) * [Register semantics for a rule](#register-semantics-for-a-rule) * [Return the top index of the stack](#return-the-top-index-of-the-stack) * [Return the value of a stack entry](#return-the-value-of-a-stack-entry) * [Set the value of a stack entry](#set-the-value-of-a-stack-entry) * [The Kollos valuator](#the-kollos-valuator) * [Initialize a valuator](#initialize-a-valuator) * [Reset a valuator](#reset-a-valuator) * [Libmarpa interface](#libmarpa-interface) * [Standard template methods](#standard-template-methods) * [The main Lua code file](#the-main-lua-code-file) * [Preliminaries to the main code](#preliminaries-to-the-main-code) * [The Kollos C code file](#the-kollos-c-code-file) * [Stuff from okollos](#stuff-from-okollos) * [`marpa_luaopen_kollos`](#marpa-luaopen-kollos) * [Create a sandbox](#create-a-sandbox) * [Preliminaries to the C library code](#preliminaries-to-the-c-library-code) * [The Kollos C header file](#the-kollos-c-header-file) * [Preliminaries to the C header file](#preliminaries-to-the-c-header-file) * [Meta-coding utilities](#meta-coding-utilities) * [Metacode execution sequence](#metacode-execution-sequence) * [Dedent method](#dedent-method) * [`c_safe_string` method](#c-safe-string-method) * [Meta code argument processing](#meta-code-argument-processing) bf2a558f14c663091120f585ef578ac9 This is the code for Kollos, the "middle layer" of Marpa. Below it is Libmarpa, a library written in the C language which contains the actual parse engine. Above it is code in a higher level language -- at this point Perl. This document is evolving. Most of the "middle layer" is still in the Perl code or in the Perl XS, and not represented here. This document only contains those portions converted to Lua or to Lua-centeric C code. The intent is that eventually all the code in this file will be "pure" Kollos -- no Perl knowledge. That is not the case at the moment. 0588207666332b8ab79ee6ce927acf39 `ref_count` maintains a reference count that controls the destruction of Kollos interpreters. `warn` is for a warning callback -- it's not currently used. `buffer` is used by kollos internally, usually for buffering symbol ID's in the libmarpa wrappers. `buffer_capacity` is its current capacity. The buffer strategy currently is to set its capacity to the maximum symbol count of any of the grammars in the Kollos interpreter. ``` -- miranda: section C function declarations struct kollos_extraspace { int ref_count; int (*warn)(const char* format, ...); Marpa_Symbol_ID *buffer; size_t buffer_capacity; }; ``` 1173d082650b8da97d45d0ceae562b13 A Kollos object is a Lua interpreter. It keeps its own reference count, in its Lua registry. `kollos_newstate()`, the constructor, creates one reference and gives its caller ownership. When the reference count falls to zero, the interpreter (Kollos object) is destroyed. ``` 8dc5321d52246096af3fce6bf7079ead ``` ``` -- miranda: section+ lua interpreter management static int default_warn(const char *format, ...) { va_list args; va_start (args, format); vfprintf (stderr, format, args); va_end (args); fputs("\n", stderr); return 1; } ``` `kollos_refinc()` creates a new reference to a Kollos interpreter, and takes ownership of it. ``` d8a818172d3e6c6c60120195d23c8957 ``` Give up ownership of a reference to a Kollos interpreter. Deletes the interpreter if the reference count drops to zero. ``` 624852863f01be8f2a031e9ad45fd5f1 ``` Set the warning function of the Kollos interpreter. ``` 758bbaadef7734c52c63f5c8a619b770 ``` Write a warning message using Kollos's warning handler. ``` 16a17165e2618fde2932b56b60feae54 ``` 5c2608a9792709eface89be5423234ac A Kollos registry object is an object kept in its registry. These generated ID's which allow them to be identified safely to non-Lua code. They have increment and decrement methods. These increment and decrement methods are intended only for non-Lua code. They make it possible for the non-Lua code to be sure that the Lua registry object exists for as long as they require it. Lua code should not use the reference counter. Lua code should simply copy the table object -- in Lua this is a reference and Lua's GC will do the right thing. `kollos_robrefinc()` creates a new reference to a Kollos registry object, and takes ownership of it. ``` a29029d9e6a9b931e05c2cbfea26c949 ``` Give up ownership of a reference to a Kollos registry object. Deletes the interpreter if the reference count drops to zero. ``` 097a87815361f731eb63264dc2c16c70 ``` 62bd3d6ba9532bf11e748f0471c2ecc4 Add a recce to the Kollos object, returning its "lua_id". The inner SLR C structure is passed in for now, because it uses a lot of PERL/XS data structures. ``` -- miranda: section+ C function declarations #define MT_NAME_RECCE "Marpa_recce" int kollos_slr_new(lua_State* L, void* slr, int slg_ref); -- miranda: section+ lua interpreter management int kollos_slr_new(lua_State* L, void* slr, int slg_ref) { int lua_id; const int base_of_stack = marpa_lua_gettop(L); marpa_lua_checkstack(L, 20); /* Lua stack: [] */ /* Create a table for this recce */ marpa_lua_newtable(L); /* Lua stack: [ recce_table ] */ /* No lock held -- SLR must delete recce table in its */ /* destructor. */ /* Set the metatable for the recce table */ marpa_luaL_setmetatable(L, MT_NAME_RECCE); /* Lua stack: [ recce_table ] */ c1fd400d0b5b5ace768706beb58858fa ``` 5b9a9112f57b37f75fd55930c211df74 Initially, Marpa's semantics were performed using a VM (virtual machine) of about two dozen operations. I am converting them to Lua, one by one. Once they are in Lua, the flexibility in defining operations becomes much greater than when they were in C/XS. The set of operations which can be defined becomes literally open-ended. With Lua replacing C, the constraints which dictated the original design of this VM are completely altered. It remains an open question what becomes of this VM and its operation set as Marpa evolves. For example, at the extreme end, every program in the old VM could be replaced with one that is a single instruction long, with that single instruction written entirely in Lua. If this were done, there no longer would be a VM, in any real sense of the word. 4ae16c025d4dc446cd1f3a6f1caf8f52 A return value of -1 indicates this should be the last VM operation. A return value of 0 or greater indicates this is the last VM operation, and that there is a Perl callback with the contents of the values array as its arguments. A return value of -2 or less indicates that the reading of VM operations should continue. Note the use of tails calls in the Lua code. Maintainters should be aware that these are finicky. In particular, while `return f(x)` is turned into a tail call, `return (f(x))` is not. 2f313f5ac2063d40305558f28f692d57 Was used for development. Perhaps I should delete this. ``` -- miranda: section VM operations c4904e2f72cc7db901151f0779f32e68 ``` 44a4004e70e89025a1c728637ca1d67f This is to be kept after development, even if not used. It may be useful in debugging. ``` -- miranda: section+ VM operations 9722a601245229d5e245e63bc47d98ff ``` e3f53a85136413a47ccd6a60af813a59 This is to used for development. Its intended use is as a dummy argument, which, if it is used by accident as a VM operation, fast fails with a clear message. ``` -- miranda: section+ VM operations b2ddb036f551a285ac82152397e4ae2f ``` 34aa84263d21d3e220f21ceec0d7a2c7 If an operation in the VM returns -1, it is a "result operation". The actual result is expected to be in the stack at index `recce.this_step.result`. The result operation is not required to be the last operation in a sequence, and a sequence of operations does not have to contain a result operation. If there are other operations after the result operation, they will not be performed. If a sequence ends without encountering a result operation, an implicit "no-op" result operation is assumed and, as usual, the result is the value in the stack at index `recce.this_step.result`. 32f82d9c075283bbb3cb4174a8c16470 Perhaps the simplest operation. The result of the semantics is a Perl undef. ``` -- miranda: section+ VM operations a7d39bbbdbcc273c2b940371356db93b ``` fe90344d864bba24a43f406f6d3af84d The result of the semantics is the value of the token at the current location. It's assumed to be a MARPA_STEP_TOKEN step -- if not the value is an undef. ``` -- miranda: section+ VM operations 2aebdb72fa38eea8ae37f7d598f78107 ``` 3b34f3dc418a7f6e4b2205e6c5422596 ``` -- miranda: section+ VM operations function op_fn_result_is_n_of_rhs(recce, rhs_ix) if recce.this_step.type ~= 'MARPA_STEP_RULE' then return op_fn_result_is_undef(recce) end local stack = recce.lmw_v.stack local result_ix = recce.this_step.result repeat if rhs_ix == 0 then break end local fetch_ix = result_ix + rhs_ix if fetch_ix > recce.this_step.arg_n then stack[result_ix] = marpa.sv.undef() break end stack[result_ix] = stack[fetch_ix] until 1 return -1 end ``` cd80be994a4857cc082c703e5179040d In `stack`, set the result to the `item_ix`'th item of a sequence. `stack` is a 0-based Perl AV. Here "sequence" means a sequence in which the separators have been kept. For those with separators discarded, the "N of RHS" operation should be used. ``` -- miranda: section+ VM operations function op_fn_result_is_n_of_sequence(recce, item_ix) if recce.this_step.type ~= 'MARPA_STEP_RULE' then return op_fn_result_is_undef(recce) end local result_ix = recce.this_step.result local fetch_ix = result_ix + item_ix * 2 if fetch_ix > recce.this_step.arg_n then return op_fn_result_is_undef(recce) end local stack = recce.lmw_v.stack if item_ix > 0 then stack[result_ix] = stack[fetch_ix] end return -1 end ``` 40e7018a85d565854fdb79cd9ef85870 Returns a constant result. ``` -- miranda: section+ VM operations function op_fn_result_is_constant(recce, constant_ix) local constants = recce:constants() local constant = constants[constant_ix] local stack = recce.lmw_v.stack local result_ix = recce.this_step.result stack[result_ix] = constant if recce.trace_values > 0 and recce.this_step.type == 'MARPA_STEP_TOKEN' then local top_of_queue = #recce.trace_values_queue recce.trace_values_queue[top_of_queue+1] = { "valuator unknown step", recce.this_step.type, recce.token, constant} -- io.stderr:write('valuator unknown step: ', inspect(recce)) end return -1 end ``` 213d43d4daa1b8176e29a5e16b995b06 The following operations add elements to the `values` array. This is a special array which may eventually be the result of the sequence of operations. c97efefd1aeba097cce106af5310b8ac Push an undef on the values array. ``` -- miranda: section+ VM operations 4688b5ed213f8f9d47db310042bfb0f7 ``` 3105ae29bd58291bc331b2d8337f713f Push one of the RHS child values onto the values array. ``` -- miranda: section+ VM operations 61d74f60453e18a9914e711a2d635b5a ``` 607f9d33c8519236b071d773a415bdc3 `current_token_literal` return the literal equivalent of the current token. It assumes that there *is* a current token, that is, it assumes that the caller has ensured that `recce.this_step.type ~= 'MARPA_STEP_TOKEN'`. ``` -- miranda: section+ VM operations function current_token_literal(recce) if recce.token_is_literal == recce.this_step.value then local start_es = recce.this_step.start_es_id local end_es = recce.this_step.es_id return recce:literal_of_es_span(start_es, end_es) end return recce.token_values[recce.this_step.value] end ``` 9eac4c7ab8443d2b081c4a567504ce44 Push the child values onto the `values` list. If it is a token step, then the token at the current location is pushed onto the `values` list. If it is a nulling step, the nothing is pushed. Otherwise the values of the RHS children are pushed. `increment` is 2 for sequences where separators must be discarded, 1 otherwise. ``` -- miranda: section+ VM operations 845aaf32c7b6a13f850fb90fb7722a22 ``` bd96c22212c42eeb154d27741a7c3907 The current start location in input location terms -- that is, in terms of the input string. ``` -- miranda: section+ VM operations function op_fn_push_start(recce, dummy, new_values) local start_es = recce.this_step.start_es_id local end_es = recce.this_step.es_id local start, l = recce:span(start_es, end_es) local next_ix = marpa.sv.top_index(new_values) + 1; local _ new_values[next_ix], _ = recce:span(start_es, end_es) return -2 end ``` 9881048d9a6628ba3918cec5c11799a7 The length of the current step in input location terms -- that is, in terms of the input string ``` -- miranda: section+ VM operations function op_fn_push_length(recce, dummy, new_values) local start_es = recce.this_step.start_es_id local end_es = recce.this_step.es_id local next_ix = marpa.sv.top_index(new_values) + 1; local _ _, new_values[next_ix] = recce:span(start_es, end_es) return -2 end ``` 5060000ef023dbe5de55262804395b4e The current start location in G1 location terms -- that is, in terms of G1 Earley sets. ``` -- miranda: section+ VM operations function op_fn_push_g1_start(recce, dummy, new_values) local next_ix = marpa.sv.top_index(new_values) + 1; new_values[next_ix] = recce.this_step.start_es_id return -2 end ``` a076ffdd247bf6503b3cefd8f3e7aa9a The length of the current step in G1 terms -- that is, in terms of G1 Earley sets. ``` -- miranda: section+ VM operations function op_fn_push_g1_length(recce, dummy, new_values) local next_ix = marpa.sv.top_index(new_values) + 1; new_values[next_ix] = (recce.this_step.es_id - recce.this_step.start_es_id) + 1 return -2 end ``` 20c9eb68722cd214070737fa5ca649a5 ``` -- miranda: section+ VM operations function op_fn_push_constant(recce, constant_ix, new_values) local constants = recce:constants() -- io.stderr:write('constants: ', inspect(constants), "\n") -- io.stderr:write('constant_ix: ', constant_ix, "\n") -- io.stderr:write('constants top ix: ', marpa.sv.top_index(constants), "\n") bcd3517f0e80d9a9dfa779dedae3b4f1 ``` 24ea833cc539cfcdcd2eaa2cc836ac32 The blessing is registered in a constant, and this operation lets the VM know its index. The index is cleared at the beginning of every sequence of operations ``` -- miranda: section+ VM operations function op_fn_bless(recce, blessing_ix) recce.this_step.blessing_ix = blessing_ix return -2 end ``` c9752cce1826bbf8bd52c0d93bc788b6 This operation tells the VM that the current `values` array is the result of this sequence of operations. ``` -- miranda: section+ VM operations function op_fn_result_is_array(recce, dummy, new_values) local blessing_ix = recce.this_step.blessing_ix if blessing_ix then local constants = recce:constants() local blessing = constants[blessing_ix] marpa.sv.bless(new_values, blessing) end local stack = recce.lmw_v.stack local result_ix = recce.this_step.result stack[result_ix] = new_values return -1 end ``` e38e80875d78f9d94f7aea75359401de Tells the VM to create a callback to Perl, with the `values` array as an argument. The return value of 3 is a vestige of an earlier implementation, which returned the size of the `values` array. ``` -- miranda: section+ VM operations function op_fn_callback(recce, dummy, new_values) local step_type = recce.this_step.type if step_type ~= 'MARPA_STEP_RULE' and step_type ~= 'MARPA_STEP_NULLING_SYMBOL' then io.stderr:write( 'Internal error: callback for wrong step type ', step_type ) os.exit(false) end local blessing_ix = recce.this_step.blessing_ix if blessing_ix then local constants = recce:constants() local blessing = constants[blessing_ix] marpa.sv.bless(new_values, blessing) end return 3 end ``` 73a6d1e740a3922624acc288aa423f48 ``` -- miranda: section+ VM operations function do_ops(recce, ops, new_values) local op_ix = 1 while op_ix <= #ops do local op_code = ops[op_ix] if op_code == 0 then return -1 end if op_code ~= op_lua then end -- io.stderr:write('op_code: ', inspect(op_code), '\\n') -- io.stderr:write('op_lua: ', inspect(op_lua), '\\n') local fn_key = ops[op_ix+1] -- io.stderr:write('ops: ', inspect(ops), '\\n') -- io.stderr:write('fn_key: ', inspect(fn_key), '\\n') -- io.stderr:write('fn name: ', recce.op_fn_key[fn_key], '\\n') local arg = ops[op_ix+2] -- io.stderr:write('arg: ', inspect(arg), '\\n') if recce.trace_values >= 3 then local queue = recce.trace_values_queue local tag = 'starting lua op' queue[#queue+1] = {'starting op', recce.this_step.type, 'lua'} queue[#queue+1] = {tag, recce.this_step.type, recce.op_fn_key[fn_key]} -- io.stderr:write('starting op: ', inspect(recce)) end local op_fn = recce[fn_key] local result = op_fn(recce, arg, new_values) if result >= -1 then return result end op_ix = op_ix + 3 end return -1 end ``` 6140eb2bf9dee8f905549d49d3ea6377 Determine the appropriate VM operations for this step, and perform them. Return codes are c774493032c8e76e2ac94bf262124d1c The mnemonic for these codes is that they represent the size of the list returned to Perl, with "trace" and "do not return" being special cases. ``` -- miranda: section+ VM operations function find_and_do_ops(recce) recce.trace_values_queue = {} while true do local new_values = marpa.sv.av_new() local ops = {} recce:step() if recce.this_step.type == 'MARPA_STEP_INACTIVE' then return 0, new_values end if recce.this_step.type == 'MARPA_STEP_RULE' then ops = recce.rule_semantics[recce.this_step.rule] if not ops then ops = recce.rule_semantics.default end goto DO_OPS end if recce.this_step.type == 'MARPA_STEP_TOKEN' then ops = recce.token_semantics[recce.this_step.symbol] if not ops then ops = recce.token_semantics.default end goto DO_OPS end if recce.this_step.type == 'MARPA_STEP_NULLING_SYMBOL' then ops = recce.nulling_semantics[recce.this_step.symbol] if not ops then ops = recce.nulling_semantics.default end goto DO_OPS end if true then return 1, new_values end ::DO_OPS:: if not ops then error(string.format('No semantics defined for %s', recce.this_step.type)) end local do_ops_result = do_ops(recce, ops, new_values) local stack = recce.lmw_v.stack -- truncate stack local above_top = recce.this_step.result + 1 for i = above_top,#stack do stack[i] = nil end if do_ops_result > 0 then return 3, new_values end if #recce.trace_values_queue > 0 then return -1, new_values end end end ``` 794c0d2d40add6139468882ae5dbcaf1 The following operations are used by the higher-level Perl code to set and discover various Lua values. 0024e3157be76a7c016404cb7d408f19 ``` -- miranda: section Utilities for Perl code function get_op_fn_key_by_name(recce, op_name_sv) local op_name = tostring(op_name_sv) return recce.op_fn_key[op_name] end ``` 8a24df87bd9b12aa7ec6c4cddddbb11e ``` -- miranda: section+ Utilities for Perl code function get_op_fn_name_by_key(recce, op_key_sv) local op_key = op_key_sv + 0 return recce.op_fn_key[op_key] end ``` ba87cf8544db8bb5e9bcdb3dd936c060 Register a constant, returning its key. ``` -- miranda: section+ Utilities for Perl code function constant_register(recce, constant_sv) local constants = recce:constants() local next_constant_key = marpa.sv.top_index(constants) + 1 constants[next_constant_key] = constant_sv return next_constant_key end ``` f1c0d0d498b68bbd08dfdaf213f231ff Register the semantic operations, `ops`, for the token whose id is `id`. ``` -- miranda: section+ Utilities for Perl code function token_register(...) local args = {...} local recce = args[1] local id = args[2]+0 local ops = {} for ix = 3, #args do ops[#ops+1] = args[ix]+0 end recce.token_semantics[id] = ops end ``` 51dda6c2d8175b580a563748f50efaa2 Register the semantic operations, `ops`, for the nulling symbol whose id is `id`. ``` -- miranda: section+ Utilities for Perl code function nulling_register(...) local args = {...} local recce = args[1] local id = args[2]+0 local ops = {} for ix = 3, #args do ops[#ops+1] = args[ix]+0 end recce.nulling_semantics[id] = ops end ``` e1bc5b0da11d1f83680c5c20e585e76d Register the semantic operations, `ops`, for the rule whose id is `id`. ``` -- miranda: section+ Utilities for Perl code function rule_register(...) local args = {...} local recce = args[1] local id = args[2]+0 local ops = {} for ix = 3, #args do ops[#ops+1] = args[ix]+0 end recce.rule_semantics[id] = ops end ``` 657ce8475e95a259230444aeea05d9c3 ``` -- miranda: section+ Utilities for Perl code function stack_top_index(recce) return recce.this_step.result end ``` f776cb87d6732a84fa565bcb393aefec ``` -- miranda: section+ Utilities for Perl code function stack_get(recce, ix) local stack = recce.lmw_v.stack return stack[ix+0] end ``` 5d2cf9e4a56c7aa8cd3b2600a5bbc0c5 ``` -- miranda: section+ Utilities for Perl code function stack_set(recce, ix, v) local stack = recce.lmw_v.stack stack[ix+0] = v end ``` 93bd16199227703f4bb77f151470ab35 The "valuator" portion of Kollos produces the value of a Kollos parse. f68f883f3bab97e21a4e28c792ff41fc Called when a valuator is set up. ``` -- miranda: section value_init() f1a520a8d23ecf3bd516d633f7984951 ``` e0b664f8a57aa6845fb38fa50f79792b A function to be called whenever a valuator is reset. It should free all memory associated with the valuation. ``` 8a57810aa29697569876565d51d713c5 ``` d82f5d1e82b8649109f4a3e37a8da9ce ``` -- miranda: section+ diagnostics function and_node_tag(recce, and_node_id) local bocage = recce.lmw_b local parent_or_node_id = bocage:_and_node_parent(and_node_id) local origin = bocage:_or_node_origin(parent_or_node_id) local origin_earleme = recce.lmw_g1r:earleme(origin) fbf0166447d88a48663a332d8475fe32 `show_bocage` returns a string which describes the bocage. 12f729bd0c7fc024cc955558255bb027 ``` 7225cb31284d54300be9a4223acfa505 ``` --[==[ miranda: exec libmarpa interface globals 3de7d2644a5cab9b69ff7dfbe97652ae ``` 4420a578dbc938521d8b0b4bd168a386 Here are the meta-programmed wrappers -- This is Lua code which writes the C code based on a "signature" for the wrapper This meta-programming does not attempt to work for all of the wrappers. It works only when 1. The number of arguments is fixed. 2. Their type is from a fixed list. 3. Converting the return value to int is a good thing to do. 4. Non-negative return values indicate success 5. Return values less than -1 indicate failure 6. Return values less than -1 set the error code 7. Return value of -1 is "soft" and returning nil is the right thing to do On those methods for which the wrapper requirements are "bent" a little bit: 3acedea147ab7d66bcd548db53d81f7c ``` -- miranda: section standard libmarpa wrappers --[==[ miranda: exec declare standard libmarpa wrappers signatures = { {"marpa_g_completion_symbol_activate", "Marpa_Symbol_ID", "sym_id", "int", "activate"}, {"marpa_g_error_clear"}, {"marpa_g_event_count"}, {"marpa_g_force_valued"}, {"marpa_g_has_cycle"}, {"marpa_g_highest_rule_id"}, {"marpa_g_highest_symbol_id"}, {"marpa_g_is_precomputed"}, {"marpa_g_nulled_symbol_activate", "Marpa_Symbol_ID", "sym_id", "int", "activate"}, {"marpa_g_prediction_symbol_activate", "Marpa_Symbol_ID", "sym_id", "int", "activate"}, {"marpa_g_rule_is_accessible", "Marpa_Rule_ID", "rule_id"}, {"marpa_g_rule_is_loop", "Marpa_Rule_ID", "rule_id"}, {"marpa_g_rule_is_nullable", "Marpa_Rule_ID", "rule_id"}, {"marpa_g_rule_is_nulling", "Marpa_Rule_ID", "rule_id"}, {"marpa_g_rule_is_productive", "Marpa_Rule_ID", "rule_id"}, {"marpa_g_rule_is_proper_separation", "Marpa_Rule_ID", "rule_id"}, {"marpa_g_rule_length", "Marpa_Rule_ID", "rule_id"}, {"marpa_g_rule_lhs", "Marpa_Rule_ID", "rule_id"}, {"marpa_g_rule_null_high", "Marpa_Rule_ID", "rule_id"}, {"marpa_g_rule_null_high_set", "Marpa_Rule_ID", "rule_id", "int", "flag"}, {"marpa_g_rule_rhs", "Marpa_Rule_ID", "rule_id", "int", "ix"}, {"marpa_g_sequence_min", "Marpa_Rule_ID", "rule_id"}, {"marpa_g_sequence_separator", "Marpa_Rule_ID", "rule_id"}, {"marpa_g_start_symbol"}, {"marpa_g_start_symbol_set", "Marpa_Symbol_ID", "id"}, {"marpa_g_symbol_is_accessible", "Marpa_Symbol_ID", "symbol_id"}, {"marpa_g_symbol_is_completion_event", "Marpa_Symbol_ID", "sym_id"}, {"marpa_g_symbol_is_completion_event_set", "Marpa_Symbol_ID", "sym_id", "int", "value"}, {"marpa_g_symbol_is_counted", "Marpa_Symbol_ID", "symbol_id"}, {"marpa_g_symbol_is_nullable", "Marpa_Symbol_ID", "symbol_id"}, {"marpa_g_symbol_is_nulled_event", "Marpa_Symbol_ID", "sym_id"}, {"marpa_g_symbol_is_nulled_event_set", "Marpa_Symbol_ID", "sym_id", "int", "value"}, {"marpa_g_symbol_is_nulling", "Marpa_Symbol_ID", "symbol_id"}, {"marpa_g_symbol_is_prediction_event", "Marpa_Symbol_ID", "sym_id"}, {"marpa_g_symbol_is_prediction_event_set", "Marpa_Symbol_ID", "sym_id", "int", "value"}, {"marpa_g_symbol_is_productive", "Marpa_Symbol_ID", "symbol_id"}, {"marpa_g_symbol_is_start", "Marpa_Symbol_ID", "symbol_id"}, {"marpa_g_symbol_is_terminal", "Marpa_Symbol_ID", "symbol_id"}, {"marpa_g_symbol_is_terminal_set", "Marpa_Symbol_ID", "symbol_id", "int", "boolean"}, {"marpa_g_symbol_is_valued", "Marpa_Symbol_ID", "symbol_id"}, {"marpa_g_symbol_is_valued_set", "Marpa_Symbol_ID", "symbol_id", "int", "boolean"}, {"marpa_g_symbol_new"}, {"marpa_g_zwa_new", "int", "default_value"}, {"marpa_g_zwa_place", "Marpa_Assertion_ID", "zwaid", "Marpa_Rule_ID", "xrl_id", "int", "rhs_ix"}, {"marpa_r_completion_symbol_activate", "Marpa_Symbol_ID", "sym_id", "int", "reactivate"}, {"marpa_r_alternative", "Marpa_Symbol_ID", "token", "int", "value", "int", "length"}, -- See above, {"marpa_r_current_earleme"}, {"marpa_r_earleme_complete"}, -- See note above, {"marpa_r_earleme", "Marpa_Earley_Set_ID", "ordinal"}, {"marpa_r_earley_item_warning_threshold"}, {"marpa_r_earley_item_warning_threshold_set", "int", "too_many_earley_items"}, {"marpa_r_earley_set_value", "Marpa_Earley_Set_ID", "ordinal"}, {"marpa_r_expected_symbol_event_set", "Marpa_Symbol_ID", "xsyid", "int", "value"}, {"marpa_r_furthest_earleme"}, {"marpa_r_is_exhausted"}, {"marpa_r_latest_earley_set"}, {"marpa_r_latest_earley_set_value_set", "int", "value"}, {"marpa_r_nulled_symbol_activate", "Marpa_Symbol_ID", "sym_id", "int", "reactivate"}, {"marpa_r_prediction_symbol_activate", "Marpa_Symbol_ID", "sym_id", "int", "reactivate"}, {"marpa_r_progress_report_finish"}, {"marpa_r_progress_report_start", "Marpa_Earley_Set_ID", "ordinal"}, {"marpa_r_start_input"}, {"marpa_r_terminal_is_expected", "Marpa_Symbol_ID", "xsyid"}, {"marpa_r_zwa_default", "Marpa_Assertion_ID", "zwaid"}, {"marpa_r_zwa_default_set", "Marpa_Assertion_ID", "zwaid", "int", "default_value"}, {"marpa_b_ambiguity_metric"}, {"marpa_b_is_null"}, {"marpa_o_ambiguity_metric"}, {"marpa_o_high_rank_only_set", "int", "flag"}, {"marpa_o_high_rank_only"}, {"marpa_o_is_null"}, {"marpa_o_rank"}, {"marpa_t_next"}, {"marpa_t_parse_count"}, {"_marpa_t_size" }, {"_marpa_t_nook_or_node", "Marpa_Nook_ID", "nook_id" }, {"_marpa_t_nook_choice", "Marpa_Nook_ID", "nook_id" }, {"_marpa_t_nook_parent", "Marpa_Nook_ID", "nook_id" }, {"_marpa_t_nook_is_cause", "Marpa_Nook_ID", "nook_id" }, {"_marpa_t_nook_cause_is_ready", "Marpa_Nook_ID", "nook_id" }, {"_marpa_t_nook_is_predecessor", "Marpa_Nook_ID", "nook_id" }, {"_marpa_t_nook_predecessor_is_ready", "Marpa_Nook_ID", "nook_id" }, {"marpa_v_valued_force"}, {"marpa_v_rule_is_valued_set", "Marpa_Rule_ID", "symbol_id", "int", "value"}, {"marpa_v_symbol_is_valued_set", "Marpa_Symbol_ID", "symbol_id", "int", "value"}, {"_marpa_v_nook"}, {"_marpa_v_trace", "int", "flag"}, {"_marpa_g_ahm_count"}, {"_marpa_g_ahm_irl", "Marpa_AHM_ID", "item_id"}, {"_marpa_g_ahm_position", "Marpa_AHM_ID", "item_id"}, {"_marpa_g_ahm_postdot", "Marpa_AHM_ID", "item_id"}, {"_marpa_g_irl_count"}, {"_marpa_g_irl_is_virtual_rhs", "Marpa_IRL_ID", "irl_id"}, {"_marpa_g_irl_length", "Marpa_IRL_ID", "irl_id"}, {"_marpa_g_irl_lhs", "Marpa_IRL_ID", "irl_id"}, {"_marpa_g_irl_rank", "Marpa_IRL_ID", "irl_id"}, {"_marpa_g_irl_rhs", "Marpa_IRL_ID", "irl_id", "int", "ix"}, {"_marpa_g_irl_semantic_equivalent", "Marpa_IRL_ID", "irl_id"}, {"_marpa_g_nsy_count"}, {"_marpa_g_nsy_is_lhs", "Marpa_NSY_ID", "nsy_id"}, {"_marpa_g_nsy_is_nulling", "Marpa_NSY_ID", "nsy_id"}, {"_marpa_g_nsy_is_semantic", "Marpa_NSY_ID", "nsy_id"}, {"_marpa_g_nsy_is_start", "Marpa_NSY_ID", "nsy_id"}, {"_marpa_g_nsy_lhs_xrl", "Marpa_NSY_ID", "nsy_id"}, {"_marpa_g_nsy_rank", "Marpa_NSY_ID", "nsy_id"}, {"_marpa_g_nsy_xrl_offset", "Marpa_NSY_ID", "nsy_id"}, {"_marpa_g_real_symbol_count", "Marpa_IRL_ID", "irl_id"}, {"_marpa_g_rule_is_keep_separation", "Marpa_Rule_ID", "rule_id"}, {"_marpa_g_rule_is_used", "Marpa_Rule_ID", "rule_id"}, {"_marpa_g_source_xrl", "Marpa_IRL_ID", "irl_id"}, {"_marpa_g_source_xsy", "Marpa_NSY_ID", "nsy_id"}, {"_marpa_g_virtual_end", "Marpa_IRL_ID", "irl_id"}, {"_marpa_g_virtual_start", "Marpa_IRL_ID", "irl_id"}, {"_marpa_g_xsy_nsy", "Marpa_Symbol_ID", "symid"}, {"_marpa_g_xsy_nulling_nsy", "Marpa_Symbol_ID", "symid"}, {"_marpa_r_earley_item_origin"}, {"_marpa_r_earley_item_trace", "Marpa_Earley_Item_ID", "item_id"}, {"_marpa_r_earley_set_size", "Marpa_Earley_Set_ID", "set_id"}, {"_marpa_r_earley_set_trace", "Marpa_Earley_Set_ID", "set_id"}, {"_marpa_r_first_completion_link_trace"}, {"_marpa_r_first_leo_link_trace"}, {"_marpa_r_first_postdot_item_trace"}, {"_marpa_r_first_token_link_trace"}, {"_marpa_r_is_use_leo"}, {"_marpa_r_is_use_leo_set", "int", "value"}, {"_marpa_r_leo_base_origin"}, {"_marpa_r_leo_base_state"}, {"_marpa_r_leo_predecessor_symbol"}, {"_marpa_r_next_completion_link_trace"}, {"_marpa_r_next_leo_link_trace"}, {"_marpa_r_next_postdot_item_trace"}, {"_marpa_r_next_token_link_trace"}, {"_marpa_r_postdot_item_symbol"}, {"_marpa_r_postdot_symbol_trace", "Marpa_Symbol_ID", "symid"}, {"_marpa_r_source_leo_transition_symbol"}, {"_marpa_r_source_middle"}, {"_marpa_r_source_predecessor_state"}, -- {"_marpa_r_source_token", "int", "*value_p"}, {"_marpa_r_trace_earley_set"}, {"_marpa_b_and_node_cause", "Marpa_And_Node_ID", "ordinal"}, {"_marpa_b_and_node_count"}, {"_marpa_b_and_node_middle", "Marpa_And_Node_ID", "and_node_id"}, {"_marpa_b_and_node_parent", "Marpa_And_Node_ID", "and_node_id"}, {"_marpa_b_and_node_predecessor", "Marpa_And_Node_ID", "ordinal"}, {"_marpa_b_and_node_symbol", "Marpa_And_Node_ID", "and_node_id"}, {"_marpa_b_or_node_and_count", "Marpa_Or_Node_ID", "or_node_id"}, {"_marpa_b_or_node_first_and", "Marpa_Or_Node_ID", "ordinal"}, {"_marpa_b_or_node_irl", "Marpa_Or_Node_ID", "ordinal"}, {"_marpa_b_or_node_is_semantic", "Marpa_Or_Node_ID", "or_node_id"}, {"_marpa_b_or_node_is_whole", "Marpa_Or_Node_ID", "or_node_id"}, {"_marpa_b_or_node_last_and", "Marpa_Or_Node_ID", "ordinal"}, {"_marpa_b_or_node_origin", "Marpa_Or_Node_ID", "ordinal"}, {"_marpa_b_or_node_position", "Marpa_Or_Node_ID", "ordinal"}, {"_marpa_b_or_node_set", "Marpa_Or_Node_ID", "ordinal"}, {"_marpa_b_top_or_node"}, {"_marpa_o_and_order_get", "Marpa_Or_Node_ID", "or_node_id", "int", "ix"}, {"_marpa_o_or_node_and_node_count", "Marpa_Or_Node_ID", "or_node_id"}, {"_marpa_o_or_node_and_node_id_by_ix", "Marpa_Or_Node_ID", "or_node_id", "int", "ix"}, } local result = {} for ix = 1,#signatures do result[#result+1] = wrap_libmarpa_method(signatures[ix]) end return table.concat(result) -- end of exec ]==] -- miranda: section register standard libmarpa wrappers --[==[ miranda: exec register standard libmarpa wrappers local result = {} for ix = 1, #signatures do local signature = signatures[ix] local function_name = signature[1] local unprefixed_name = function_name:gsub("^[_]?marpa_", "", 1) local class_letter = unprefixed_name:gsub("_.*$", "", 1) local class_name = libmarpa_class_name[class_letter] local class_table_name = 'class_' .. class_name -- for example: marpa_lua_getfield(L, kollos_table_stack_ix, "class_grammar") result[#result+1] = string.format(" marpa_lua_getfield(L, kollos_table_stack_ix, %q);\n", class_table_name) local wrapper_name = "wrap_" .. unprefixed_name; -- for example: marpa_lua_pushcfunction(L, wrap_g_highest_rule_id) result[#result+1] = string.format(" marpa_lua_pushcfunction(L, %s);\n", wrapper_name) local classless_name = function_name:gsub("^[_]?marpa_*_", "") local initial_underscore = function_name:match('^_') and '_' or '' local field_name = initial_underscore .. classless_name -- for example: marpa_lua_setfield(L, -2, "highest_rule_id") result[#result+1] = string.format(" marpa_lua_setfield(L, -2, %q);\n", field_name) result[#result+1] = string.format(" marpa_lua_pop(L, 1);\n", field_name) end return table.concat(result) ]==] -- miranda: section create kollos class tables --[==[ miranda: exec create kollos class tables local result = {} for class_letter, class in pairs(libmarpa_class_name) do local class_table_name = 'class_' .. class local functions_to_register = class .. '_methods' result[#result+1] = string.format(" marpa_luaL_newlib(L, %s);\n", functions_to_register) result[#result+1] = " marpa_lua_pushvalue(L, -1);\n" result[#result+1] = ' marpa_lua_setfield(L, -2, "__index");\n' result[#result+1] = string.format(" marpa_lua_setfield(L, kollos_table_stack_ix, %q);\n", class_table_name); end return table.concat(result) ]==] 05803539f778f51a5797abee9a2381f3 The standard constructors are generated indirectly, from a template. This saves a lot of repetition, which makes for easier reading in the long run. In the short run, however, you may want first to look at the bocage constructor. It is specified directly, which can be easier for a first reading. -- miranda: section object constructors --[==[ miranda: exec object constructors local result = {} local template = [[ |static int |wrap_!NAME!_new (lua_State * L) |{ | const int !BASE_NAME!_stack_ix = 1; | int !NAME!_stack_ix; | | if (0) | printf ("%s %s %d\n", __PRETTY_FUNCTION__, __FILE__, __LINE__); | if (1) | { | marpa_luaL_checktype(L, !BASE_NAME!_stack_ix, LUA_TTABLE); | } | | marpa_lua_newtable(L); | /* [ base_table, class_table ] */ | !NAME!_stack_ix = marpa_lua_gettop(L); | marpa_lua_getglobal (L, "kollos"); | marpa_lua_getfield (L, -1, "class_!NAME!"); | marpa_lua_setmetatable (L, !NAME!_stack_ix); | /* [ base_table, class_table ] */ | | { | !BASE_TYPE! *!BASE_NAME!_ud; | | !TYPE! *!NAME!_ud = | (!TYPE! *) marpa_lua_newuserdata (L, sizeof (!TYPE!)); | /* [ base_table, class_table, class_ud ] */ | marpa_lua_rawgetp (L, LUA_REGISTRYINDEX, &kollos_!LETTER!_ud_mt_key); | /* [ class_table, class_ud, class_ud_mt ] */ | marpa_lua_setmetatable (L, -2); | /* [ class_table, class_ud ] */ | | marpa_lua_setfield (L, !NAME!_stack_ix, "_libmarpa"); | marpa_lua_getfield (L, !BASE_NAME!_stack_ix, "_libmarpa_g"); | marpa_lua_setfield (L, !NAME!_stack_ix, "_libmarpa_g"); | marpa_lua_getfield (L, !BASE_NAME!_stack_ix, "_libmarpa"); | !BASE_NAME!_ud = (!BASE_TYPE! *) marpa_lua_touserdata (L, -1); | | *!NAME!_ud = marpa_!LETTER!_new (*!BASE_NAME!_ud); | if (!*!NAME!_ud) | { | return libmarpa_error_handle (L, !NAME!_stack_ix, "marpa_!LETTER!_new()"); | } | } | | if (0) | printf ("%s %s %d\n", __PRETTY_FUNCTION__, __FILE__, __LINE__); | marpa_lua_settop(L, !NAME!_stack_ix ); | /* [ base_table, class_table ] */ | return 1; |} ]] for class_ix = 1, (#libmarpa_class_sequence - 1) do local class_letter = libmarpa_class_sequence[class_ix+1] -- bocage constructor is special case if class_letter == 'b' then goto NEXT_CLASS end local class_name = libmarpa_class_name[class_letter] local class_type = libmarpa_class_type[class_letter] local base_class_letter = libmarpa_class_sequence[class_ix] local base_class_name = libmarpa_class_name[base_class_letter] local base_class_type = libmarpa_class_type[base_class_letter] local this_piece = pipe_dedent(template) :gsub("!BASE_NAME!", base_class_name) :gsub("!BASE_TYPE!", base_class_type) :gsub("!BASE_LETTER!", base_class_letter) :gsub("!NAME!", class_name) :gsub("!TYPE!", class_type) :gsub("!LETTER!", class_letter) result[#result+1] = this_piece ::NEXT_CLASS:: end return table.concat(result, "\n") ]==] The bocage constructor takes an extra argument, so it's a special case. It's close to the standard constructor. The standard constructors are generated indirectly, from a template. The template saves repetition, but is harder on a first reading. This bocage constructor is specified directly, so you may find it easer to read it first. 696b99945883b9971354315357ca2369 ``` The grammar constructor is a special case, because its argument is a special "configuration" argument. 3413eeffa76ac9455e11d6f5a646fb9e ``` 93c0c75a51511e500c0dae6c0c61b967 ``` -- miranda: section main -- miranda: insert legal preliminaries -- miranda: insert luacheck declarations -- miranda: insert enforce strict globals -- miranda: insert VM operations -- miranda: insert value_init() -- miranda: insert valuation_reset() -- miranda: insert diagnostics -- miranda: insert Utilities for Perl code 1d4f7117746e39d7c6b8c5915a3e863b ``` aa24849b8a6c63acc96f8d05194f8699 Licensing, etc. ``` d330c928d8c9e4928e85c0b149b0a9a8 ``` Luacheck declarations ``` b81c2f9eff64cac7580cbf7785a12c96 ``` Set "strict" globals, using code taken from strict.lua. ``` 77862327ef6c36b005fc7f61e69c0038 ``` 15a93b4b782f10f5b74aeaad0262d95d ``` -- miranda: section kollos_c -- miranda: language c -- miranda: insert preliminaries to the c library code -- miranda: insert private error code declarations -- miranda: insert define error codes -- miranda: insert private event code declarations -- miranda: insert define event codes -- miranda: insert private step code declarations -- miranda: insert define step codes 489d7af1fa46139c550a73c8f5841f88 ``` 2c97d996aac9a81338200d283c9c5ed9 ``` 852faf79d5428272f0ca848eb020ea4a `dummyup_grammar` is not Lua C API. This may be the basis of the actual constructor. Takes ownership of a Libmarpa grammar reference, so the caller must make sure that one is available. fadf73388c7f2ffc61facf7c2642d7c4 `lca_grammar_rule_new` wraps the Libmarpa method `marpa_g_rule_new()`. If the rule is 7 symbols or fewer, I put it on the stack. As an old kernel driver programmer, I was trained to avoid putting even small arrays on the stack, but one of this size should be safe on anything like close to a modern architecture. Perhaps I will eventually limit Libmarpa's rule RHS to 7 symbols, 7 because I can encode dot position in 3 bit. fcee9ea8d686a3f22de960fd1f240f8b `lca_grammar_sequence_new` wraps the Libmarpa method `marpa_g_sequence_new()`. If the rule is 7 symbols or fewer, I put it on the stack. As an old kernel driver programmer, I was trained to avoid putting even small arrays on the stack, but one of this size should be safe on anything like close to a modern architecture. Perhaps I will eventually limit Libmarpa's rule RHS to 7 symbols, 7 because I can encode dot position in 3 bit. e2f782eb1bb5589b535333df5537102a ``` e7ce018cd89a2db0dc4f39d0f359d06e ``` -- miranda: section define marpa_luaopen_kollos method static int marpa_luaopen_kollos(lua_State *L) { /* Create the main kollos object */ const int kollos_table_stack_ix = marpa_lua_gettop(L) + 1; 2a6626558b179ebdad868ca2fde485c5 ``` 16cba468890abcd47d29061aeec334e5 Given a Lua state, create a table, which can be used as a "sandbox" for protect the global environment from user code. The table is named `sandbox` and itself *is* kept in the global environment. This code only creates the sandbox, it does not set it as an environment -- it is assumed that that will be done later, after to-be-sandboxed Lua code is loaded, but before it is executed. Not Lua-callable, but leaves the stack as before. ``` -- miranda: section create sandbox table { const int base_of_stack = marpa_lua_gettop(L); marpa_lua_newtable (L); /* sandbox.__index = _G */ marpa_lua_pushglobaltable(L); marpa_lua_setfield(L, -2, "__index"); 4b591ba429f741af6923045aeec1867a ``` b6a8e743190e3cf38308b3e90f52b6c8 ``` -- miranda: section preliminaries to the c library code /* ** Permission is hereby granted, free of charge, to any person obtaining ** a copy of this software and associated documentation files (the ** "Software"), to deal in the Software without restriction, including ** without limitation the rights to use, copy, modify, merge, publish, ** distribute, sublicense, and/or sell copies of the Software, and to ** permit persons to whom the Software is furnished to do so, subject to ** the following conditions: ** ** The above copyright notice and this permission notice shall be ** included in all copies or substantial portions of the Software. ** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. ** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY ** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, ** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE ** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ** ** [ MIT license: http://www.opensource.org/licenses/mit-license.php ] */ bd950df5e94c26ae1756ced394737fc4 ``` b2f928f420e95c15fbf6fdb822fc81cc ``` -- miranda: section kollos_h -- miranda: language c -- miranda: insert preliminary comments of the c header file 63307eac5875bc8ac69a0b2da5e32644 ``` e6637690337bd22bf481dd86522a6199 ``` -- miranda: section preliminary comments of the c header file 15bec93404073dd1e5ce3a24f9ac1ebd ``` afb8dc824e40eceee466a48c546d8150 a4bbb4894824af0a448684b010503cfd ``` -- miranda: sequence-exec argument processing -- miranda: sequence-exec metacode utilities -- miranda: sequence-exec libmarpa interface globals -- miranda: sequence-exec declare standard libmarpa wrappers -- miranda: sequence-exec register standard libmarpa wrappers -- miranda: sequence-exec create kollos class tables -- miranda: sequence-exec object userdata gc methods ``` 96ba0007a41bc64e7333f4d2864d0998 A pipe symbol is used when inlining code to separate the code's indentation from the indentation used to display the code in this document. The `pipe_dedent` method removes the display indentation. ``` --[==[ miranda: exec metacode utilities function pipe_dedent(code) return code:gsub('\n *|', '\n'):gsub('^ *|', '', 1) end ]==] ``` c7a29b68db380940ce1706b6a063c975 ``` --[==[ miranda: exec metacode utilities local function c_safe_string (s) s = string.gsub(s, '"', '\\034') s = string.gsub(s, '\\', '\\092') return '"' .. s .. '"' end ]==] ``` 6a09f6e14230a185214506388e39c699 The arguments show where to find the files containing event and error codes. ``` -- assumes that, when called, out_file to set to output file --[==[ miranda: exec argument processing local error_file local event_file d20aee9d3482a5dcae0c0a8e232d8e3c ```