├── LICENSE ├── get_ast.lua ├── get_ast └── get_params.lua ├── get_formatter_ast.lua ├── get_formatter_ast └── get_params.lua ├── lcf-scm-1.rockspec ├── lua_get_ast.lua ├── lua_get_formatter_ast.lua ├── lua_reformat.lua ├── readme.md ├── reformat.lua ├── reformat ├── get_params.lua └── usage_text.lua └── workshop ├── base.lua ├── file ├── as_string.lua ├── convert.lua ├── exists.lua ├── get_size.lua ├── safe_open.lua └── text_file_as_string.lua ├── formats ├── lua │ ├── ast_transformer │ │ ├── align_nodes.lua │ │ ├── handlers │ │ │ ├── assignment.lua │ │ │ ├── else_part.lua │ │ │ ├── elseif_part.lua │ │ │ ├── expression.lua │ │ │ ├── function_call.lua │ │ │ ├── function_params.lua │ │ │ ├── generic_for_block.lua │ │ │ ├── if_block.lua │ │ │ ├── if_part.lua │ │ │ ├── interface.lua │ │ │ ├── key_val.lua │ │ │ ├── local_assignment.lua │ │ │ ├── local_named_function.lua │ │ │ ├── named_function.lua │ │ │ ├── numeric_for_block.lua │ │ │ ├── repeat_block.lua │ │ │ ├── type_function.lua │ │ │ ├── var_ref.lua │ │ │ └── while_block.lua │ │ ├── interface.lua │ │ ├── move_comments.lua │ │ ├── restruc_nodes.lua │ │ └── run.lua │ ├── formatter │ │ ├── get_result.lua │ │ ├── handlers │ │ │ ├── expression.lua │ │ │ ├── expressions │ │ │ │ ├── function_call.lua │ │ │ │ ├── string.lua │ │ │ │ ├── table.lua │ │ │ │ ├── type_function.lua │ │ │ │ └── var_ref.lua │ │ │ ├── interface.lua │ │ │ ├── statements.lua │ │ │ ├── statements │ │ │ │ ├── assignment.lua │ │ │ │ ├── blocks │ │ │ │ │ ├── do_block.lua │ │ │ │ │ ├── generic_for_block.lua │ │ │ │ │ ├── if_block.lua │ │ │ │ │ ├── local_named_function.lua │ │ │ │ │ ├── named_function.lua │ │ │ │ │ ├── numeric_for_block.lua │ │ │ │ │ ├── repeat_block.lua │ │ │ │ │ └── while_block.lua │ │ │ │ ├── break_statement.lua │ │ │ │ ├── comment.lua │ │ │ │ ├── goto_statement.lua │ │ │ │ ├── label_statement.lua │ │ │ │ ├── local_assignment.lua │ │ │ │ └── return_statement.lua │ │ │ └── wrappers │ │ │ │ ├── bracket_expr.lua │ │ │ │ ├── colon_name.lua │ │ │ │ ├── dot_list.lua │ │ │ │ ├── dot_name.lua │ │ │ │ ├── expr_list.lua │ │ │ │ ├── func_args.lua │ │ │ │ ├── function_params.lua │ │ │ │ ├── name_list.lua │ │ │ │ ├── name_parts.lua │ │ │ │ ├── par_expr.lua │ │ │ │ └── ref_list.lua │ │ ├── init.lua │ │ ├── interface.lua │ │ ├── process_block.lua │ │ ├── process_block_multiline.lua │ │ ├── process_block_oneline.lua │ │ ├── process_list.lua │ │ ├── process_list_variative.lua │ │ ├── process_node.lua │ │ ├── represent.lua │ │ ├── representation_is_allowed.lua │ │ ├── run.lua │ │ ├── state_keeper │ │ │ ├── enter_level.lua │ │ │ ├── get_child_state.lua │ │ │ ├── get_state.lua │ │ │ ├── init.lua │ │ │ ├── interface.lua │ │ │ ├── leave_level.lua │ │ │ ├── set_child_state.lua │ │ │ └── set_state.lua │ │ └── variate.lua │ ├── is_identifier.lua │ ├── keywords.lua │ ├── quote_string.lua │ ├── quote_string │ │ ├── custom_quotes.lua │ │ ├── dump.lua │ │ ├── intact.lua │ │ ├── linear.lua │ │ └── quote_char.lua │ ├── run_formatter.lua │ ├── syntax.lua │ ├── syntax │ │ ├── expression.lua │ │ ├── qualifiers │ │ │ └── var_or_call.lua │ │ ├── statements.lua │ │ ├── statements │ │ │ ├── assign_or_call.lua │ │ │ ├── break_statement.lua │ │ │ ├── do_block.lua │ │ │ ├── empty_statement.lua │ │ │ ├── function_body.lua │ │ │ ├── generic_for_block.lua │ │ │ ├── goto_statement.lua │ │ │ ├── if_block.lua │ │ │ ├── label_statement.lua │ │ │ ├── local_statement.lua │ │ │ ├── named_function.lua │ │ │ ├── numeric_for_block.lua │ │ │ ├── repeat_block.lua │ │ │ ├── return_statement.lua │ │ │ └── while_block.lua │ │ ├── type_boolean.lua │ │ ├── type_function.lua │ │ ├── type_nil.lua │ │ ├── type_number.lua │ │ ├── type_string.lua │ │ ├── type_table.lua │ │ ├── words │ │ │ ├── comment.lua │ │ │ ├── name.lua │ │ │ ├── opt_spc.lua │ │ │ ├── particles │ │ │ │ └── long_bracket.lua │ │ │ ├── syntel.lua │ │ │ ├── vararg.lua │ │ │ └── word.lua │ │ └── wrappers │ │ │ ├── bracket_expr.lua │ │ │ ├── colon_name.lua │ │ │ ├── dot_name.lua │ │ │ ├── expr_list.lua │ │ │ ├── name_list.lua │ │ │ └── par_expr.lua │ └── transform_ast.lua ├── lua_table │ ├── save.lua │ └── save │ │ ├── get_ast.lua │ │ ├── init.lua │ │ ├── install_node_handlers │ │ ├── minimal.lua │ │ └── readable.lua │ │ ├── interface.lua │ │ └── serialize_ast.lua ├── lua_table_code │ ├── load.lua │ ├── save.lua │ └── save │ │ ├── get_ast.lua │ │ ├── init.lua │ │ ├── install_node_handlers.lua │ │ ├── interface.lua │ │ └── serialize_ast.lua └── sh │ └── load.lua ├── frontend └── text │ └── print_msg_with_delta_time.lua ├── lua ├── code │ ├── ast_as_code.lua │ └── get_ast.lua ├── data_types.lua └── string │ └── quote.lua ├── mechs ├── command_line_processor │ ├── assert_type_is_correct.lua │ ├── classify_item.lua │ ├── get_key_name.lua │ ├── interface.lua │ ├── parse_args.lua │ └── run.lua ├── generic_file_converter │ ├── compile.lua │ ├── init.lua │ ├── interface.lua │ ├── run.lua │ └── say.lua ├── generic_loader.lua ├── geometry │ └── 1d │ │ └── segments │ │ ├── is_inside.lua │ │ └── subtract_inner.lua ├── graph │ ├── assembly_order.lua │ ├── dfs.lua │ └── dfs │ │ ├── dfs.lua │ │ ├── get_children.lua │ │ └── interface.lua ├── indents_table.lua ├── name_giver.lua ├── number │ └── representer │ │ ├── interface.lua │ │ ├── represent.lua │ │ └── units │ │ ├── binary_bytes.lua │ │ ├── binary_units.lua │ │ ├── frequency.lua │ │ ├── general_number.lua │ │ ├── general_time.lua │ │ └── interface.lua ├── parser │ ├── folder │ │ ├── fold.lua │ │ ├── get_struc.lua │ │ ├── init.lua │ │ └── interface.lua │ ├── get_struc.lua │ ├── handy.lua │ ├── on_match.lua │ ├── parse.lua │ └── populate.lua ├── processor │ ├── core │ │ ├── init.lua │ │ ├── interface.lua │ │ ├── match.lua │ │ └── on_match.lua │ ├── handy.lua │ ├── link.lua │ └── optimize.lua ├── streams │ ├── mergeable │ │ ├── block_read.lua │ │ ├── get_segment.lua │ │ ├── get_slot.lua │ │ ├── init.lua │ │ ├── interface.lua │ │ ├── set_next_position.lua │ │ ├── set_relative_position.lua │ │ └── string │ │ │ ├── get_length.lua │ │ │ ├── get_position.lua │ │ │ ├── init.lua │ │ │ ├── interface.lua │ │ │ ├── match_regexp.lua │ │ │ ├── match_string.lua │ │ │ ├── read.lua │ │ │ └── set_position.lua │ └── sequence │ │ ├── get_position.lua │ │ ├── init.lua │ │ ├── interface.lua │ │ ├── read.lua │ │ ├── set_position.lua │ │ └── write.lua └── text_block │ ├── dec_indent.lua │ ├── inc_indent.lua │ ├── init.lua │ ├── interface.lua │ ├── line │ ├── add.lua │ ├── get_line.lua │ ├── get_line_length.lua │ ├── get_text_length.lua │ ├── init.lua │ └── interface.lua │ └── text │ ├── add_curline.lua │ ├── add_textline.lua │ ├── get_block_width.lua │ ├── get_text.lua │ ├── get_text_width.lua │ ├── include.lua │ ├── new_line.lua │ ├── on_clean_line.lua │ ├── request_clean_line.lua │ ├── request_empty_line.lua │ └── store_textline.lua ├── number ├── represent_size.lua └── represent_time.lua ├── string ├── content_attributes.lua ├── lines │ └── get_next_line.lua ├── save_to_file.lua ├── trim.lua ├── trim_head.lua ├── trim_linefeed.lua └── trim_tail.lua ├── struc └── compile.lua ├── system ├── install_assert_functions.lua └── install_is_functions.lua └── table ├── as_string.lua ├── clone.lua ├── get_key_vals.lua ├── map_values.lua ├── merge.lua ├── new.lua ├── ordered_pass.lua ├── ordered_pass └── default_comparator.lua ├── patch.lua ├── replace.lua └── unfold.lua /get_ast.lua: -------------------------------------------------------------------------------- 1 | local get_params = request('get_ast.get_params') 2 | local convert = request('!.file.convert') 3 | local get_ast = request('!.lua.code.get_ast') 4 | 5 | return 6 | function(args) 7 | local f_in_name, f_out_name = get_params(args) 8 | if not f_in_name then 9 | return 10 | end 11 | convert( 12 | { 13 | f_in_name = f_in_name, 14 | f_out_name = f_out_name, 15 | parse = get_ast, 16 | } 17 | ) 18 | end 19 | -------------------------------------------------------------------------------- /get_ast/get_params.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Parse table with command line and return 3 | 4 | 5 | 6 | of fail and return nil. 7 | ]] 8 | 9 | local usage_text = 10 | [[ 11 | Get annotated syntax tree (AST) for Lua 5.3 code. 12 | 13 | lua.get_ast [] 14 | 15 | -- Martin, 2017-10-14 16 | ]] 17 | 18 | local cmdline_processor = 19 | new(request('!.mechs.command_line_processor.interface')) 20 | cmdline_processor.allowed_params = 21 | { 22 | {name = 'f_in_name', type = 'string'}, 23 | {name = 'f_out_name', type = 'string'}, 24 | } 25 | 26 | return 27 | function(args) 28 | assert_table(args) 29 | if not args[1] or (args[1] == '--help') then 30 | print(usage_text) 31 | return 32 | end 33 | 34 | local params = cmdline_processor:run(args) 35 | if not params.f_in_name then 36 | print(usage_text) 37 | return 38 | end 39 | 40 | local f_in_name, f_out_name 41 | f_in_name = params.f_in_name 42 | f_out_name = params.f_out_name or (f_in_name .. '.ast') 43 | 44 | return f_in_name, f_out_name 45 | end 46 | -------------------------------------------------------------------------------- /get_formatter_ast.lua: -------------------------------------------------------------------------------- 1 | local get_params = request('get_formatter_ast.get_params') 2 | local convert = request('!.file.convert') 3 | local get_ast = request('!.lua.code.get_ast') 4 | local transform_ast = request('!.formats.lua.transform_ast') 5 | 6 | local parse = 7 | function(s) 8 | local result 9 | result = get_ast(s) 10 | result = 11 | transform_ast( 12 | result, 13 | { 14 | keep_comments = true, 15 | keep_unparsed_tail = true, 16 | } 17 | ) 18 | return result 19 | end 20 | 21 | return 22 | function(args) 23 | local f_in_name, f_out_name = get_params(args) 24 | if not f_in_name then 25 | return 26 | end 27 | convert( 28 | { 29 | f_in_name = f_in_name, 30 | f_out_name = f_out_name, 31 | parse = parse, 32 | } 33 | ) 34 | end 35 | -------------------------------------------------------------------------------- /get_formatter_ast/get_params.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Parse table with command line and return 3 | 4 | 5 | 6 | or fail and return nil. 7 | ]] 8 | 9 | local usage_text = 10 | [[ 11 | Get transformed annotated syntax tree (AST) for Lua 5.3 code. 12 | 13 | lua.get_formatter_ast [] 14 | 15 | -- Martin, 2017-10-14 16 | ]] 17 | 18 | local cmdline_processor = 19 | new(request('!.mechs.command_line_processor.interface')) 20 | cmdline_processor.allowed_params = 21 | { 22 | {name = 'f_in_name', type = 'string'}, 23 | {name = 'f_out_name', type = 'string'}, 24 | } 25 | 26 | return 27 | function(args) 28 | assert_table(args) 29 | if not args[1] or (args[1] == '--help') then 30 | print(usage_text) 31 | return 32 | end 33 | 34 | local params = cmdline_processor:run(args) 35 | if not params.f_in_name then 36 | print(usage_text) 37 | return 38 | end 39 | 40 | local f_in_name, f_out_name 41 | f_in_name = params.f_in_name 42 | f_out_name = params.f_out_name or (f_in_name .. '.fast') 43 | 44 | return f_in_name, f_out_name 45 | end 46 | -------------------------------------------------------------------------------- /lua_get_ast.lua: -------------------------------------------------------------------------------- 1 | require('lcf.workshop.base') 2 | local f = request('lcf.get_ast') 3 | f(_G.arg) 4 | -------------------------------------------------------------------------------- /lua_get_formatter_ast.lua: -------------------------------------------------------------------------------- 1 | require('lcf.workshop.base') 2 | local f = request('lcf.get_formatter_ast') 3 | f(_G.arg) 4 | -------------------------------------------------------------------------------- /lua_reformat.lua: -------------------------------------------------------------------------------- 1 | require('lcf.workshop.base') 2 | local f = request('lcf.reformat') 3 | f(_G.arg) 4 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | ### Description 2 | 3 | Formats any valid Lua 5.3 code. 4 | 5 | Lines with code are wrapped to fit inside given margins. 6 | 7 | Files with invalid Lua syntax may lose the content after the syntax 8 | error! If this is a problem - verify the correctness of the file 9 | before running reformatter. For example via `$ luac -p `. 10 | 11 | Installation deploys three command-line scripts: 12 | 13 | * `lua.reformat` 14 | * `lua.get_ast` 15 | * `lua.get_formatter_ast` 16 | 17 | Last two for people who love tinkering. 18 | 19 | --- 20 | 21 | ### Requirements 22 | 23 | * [LuaRocks](https://luarocks.org/). For easy installation. 24 | * [Lua interpreter](https://lua.org). Version 5.3 is preferred. 25 | Another options described in a following table. 26 | 27 | | Lua version required | Syntax supported | Branch | Notes | 28 | | :---: | :---: | --- | --- | 29 | | 5.3 | 5.3 | [master] | May contain experimental features. | 30 | | 5.3 | 5.3 | [5.3] | | 31 | | 5.1 | 5.3 | [5.1] | | 32 | | 5.1 | 5.1 | [5.1-syntax_5.1] | | 33 | 34 | [master]: https://github.com/martin-eden/lua_code_formatter/tree/master 35 | [5.3]: https://github.com/martin-eden/lua_code_formatter/tree/5.3 36 | [5.1]: https://github.com/martin-eden/lua_code_formatter/tree/5.1 37 | [5.1-syntax_5.1]: https://github.com/martin-eden/lua_code_formatter/tree/5.1-syntax_5.1 38 | * OS: Linux. Possibly it can work on other OSs but I've not tested it 39 | there. 40 | 41 | ### Installation 42 | 43 | | Luarocks repo | Git repo, luarocks | 44 | | --- | --- | 45 | | `sudo luarocks install lcf` | `git clone https://github.com/martin-eden/lua_code_formatter` | 46 | | | `cd ./lua_code_formatter` | 47 | | | `sudo luarocks make lcf-scm-1.rockspec` | 48 | 49 | ### Deinstallation 50 | 51 | | Luarocks repo | Git repo, luarocks | 52 | | --- | --- | 53 | | `sudo luarocks remove lcf` | `sudo luarocks remove lcf` | 54 | | | `rm lua_code_formatter` | 55 | 56 | --- 57 | 58 | ### Usage 59 | 60 | #### From command-line 61 | 62 | `lua.reformat []` 63 | 64 | #### From Lua interpreter 65 | 66 | Suppose you have a string with Lua code and wish to get another string 67 | with formatted code. 68 | 69 | ```Lua 70 | do 71 | local lua_code_str = 'do return end' -- < fill it 72 | 73 | require('lcf.workshop.base') 74 | local get_ast = request('!.lua.code.get_ast') 75 | local get_formatted_code = request('!.lua.code.ast_as_code') 76 | 77 | return get_formatted_code(get_ast(lua_code_str)) 78 | end 79 | ``` 80 | 81 | ##### Passing formatting parameters 82 | 83 | You may override default parameters by passing a table with new values 84 | of changed parameters: 85 | 86 | ```lua 87 | get_formatted_code( 88 | get_ast(lua_code_str), 89 | { 90 | indent_chunk = ' ', 91 | right_margin = 96, 92 | max_text_width = math.huge, 93 | keep_unparsed_tail = true, 94 | keep_comments = true, 95 | } 96 | ) 97 | ``` 98 | 99 | | Parameter | Default | Description | Notes | 100 | | --- | :---: | --- | --- | 101 | | `indent_chunk` | ` ` ` ` | String used for building one indent. | You may try value `\|..` to see it's effect. | 102 | | `right_margin` | 96 | Maximum line length with indent. | Setting it makes sense for printing. | 103 | | `max_text_width` | +inf | Maximum line length without indent. | Setting it makes sense for viewing in editor. | 104 | | `keep_unparsed_tail` | true | Keep text after point where we failed to parse source. | Syntactically incorrect code may still lose parts even with this flag. For example `f() = a` is formatted as `f()`. (It's parsed as assignment but formatted as function call.) | 105 | | `keep_comments` | true | Keep comments. | Comment text is not changed so comments may last beyond right margin. | 106 | 107 | ##### Comments handling 108 | 109 | Comments are raised to statements level. 110 | 111 | So text 112 | ```lua 113 | function(a, --parameter "a" 114 | b) --parameter "b" 115 | end 116 | ``` 117 | is formatted as 118 | ```lua 119 | --parameter "a" 120 | function(a, b) 121 | --parameter "b" 122 | end 123 | ``` 124 | This is done to keep formatting routines simple. 125 | 126 | --- 127 | 128 | ### Changing formatting logic 129 | 130 | First, general workflow of this formatter: 131 | 132 | 1. Load `.lua` file as string. 133 | 2. Parse that string to tree in table. 134 | 3. Change nodes of that tree to make it easier to handle in formatting 135 | routines. 136 | 4. Run formatting routines for all nodes in tree. Formatting routines 137 | output to virtual printer. 138 | 5. Write contents of virtual printer to file. 139 | 140 | We are at point `4`. Start tinkering from something simple but usable 141 | as "repeat" block handling: 142 | `workshop/formats/lua/formatter/handlers/statements/blocks/repeat_block.lua` 143 | 144 | --- 145 | 146 | ### Contributors 147 | 148 | * Several people were asking to keep comments. This was done and 149 | `--keep-comments` option was added. 150 | * `Peter Melnichenko ♰` shown me how to write cross-platform 151 | `.rockspec` file. 152 | * `Oliver Jan Krylow` added cautions in `readme.md` that file with 153 | invalid syntax loses tail. I've mentioned `$ luac -p` workaround 154 | and added `--keep-unparsed-tail` option to detect and prevent 155 | this. 156 | * `keneanung` adoped formatter to Lua 5.1 syntax and pushed branch 157 | `5.1-syntax_5.1`. Main audience of this is LuaJIT users. 158 | 159 | ### Further development 160 | 161 | I feel this project is done. Original goal to reformat ugly code from 162 | World of Warcraft addons accomplished. 163 | 164 | * I'm planning to keep it compatible with current PuC-Lua version. 165 | * Maybe I'll add more documentation about inner mechanics. 166 | * Maybe I'll add more advanced formatting like empty lines before 167 | long `for` blocks. 168 | 169 | --- 170 | 171 | ### See also 172 | * [Command-line builder](https://github.com/martin-eden/lcf_params_gui) for this. 173 | * [My other repositories](https://github.com/martin-eden/contents). 174 | 175 | --- 176 | ``` 177 | 2016-08-16 178 | 2017-01-28 179 | 2017-09-26 180 | 2018-02-23 181 | ``` 182 | -------------------------------------------------------------------------------- /reformat.lua: -------------------------------------------------------------------------------- 1 | local get_params = request('reformat.get_params') 2 | local get_ast = request('!.lua.code.get_ast') 3 | local serialize_ast = request('!.lua.code.ast_as_code') 4 | local convert = request('!.file.convert') 5 | 6 | return 7 | function(args) 8 | local f_in_name, f_out_name, formatter_options = get_params(args) 9 | if not f_in_name then 10 | return 11 | end 12 | convert( 13 | { 14 | f_in_name = f_in_name, 15 | f_out_name = f_out_name, 16 | parse = get_ast, 17 | compile = 18 | function(struc) 19 | return serialize_ast(struc, formatter_options) 20 | end, 21 | } 22 | ) 23 | end 24 | -------------------------------------------------------------------------------- /reformat/get_params.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Parse given command line and return three values: 3 | 4 | 5 | 6 | Alternatively may print usage help and return nothing. 7 | ]] 8 | 9 | local usage_text = request('usage_text') 10 | 11 | local cmdline_processor = 12 | new(request('!.mechs.command_line_processor.interface')) 13 | cmdline_processor.allowed_params = 14 | { 15 | {name = 'f_in_name', type = 'string'}, 16 | {name = 'f_out_name', type = 'string'}, 17 | {name = 'indent', type = 'key_str'}, 18 | {name = 'right-margin', type = 'key_int'}, 19 | {name = 'max-text-width', type = 'key_int'}, 20 | {name = 'keep-comments', type = 'flag'}, 21 | {name = 'keep-unparsed-tail', type = 'flag'}, 22 | } 23 | 24 | --[[ 25 | Although original default parameters are stored in 26 | [lua.formatter.interface] I override it here. 27 | ]] 28 | local default_formatter_options = 29 | { 30 | indent_chunk = ' ', 31 | right_margin = 96, 32 | max_text_width = math.huge, 33 | keep_comments = true, 34 | keep_unparsed_tail = true, 35 | } 36 | 37 | local table_to_str = request('!.formats.lua_table.save') 38 | 39 | return 40 | function(args) 41 | assert_table(args) 42 | if not args[1] or (args[1] == '--help') then 43 | print(usage_text) 44 | print('Defaults:') 45 | print(table_to_str(default_formatter_options)) 46 | print('\n-- Martin, 2018-02') 47 | return 48 | end 49 | 50 | local params = cmdline_processor:run(args) 51 | 52 | local f_in_name = params.f_in_name 53 | if not f_in_name then 54 | print(usage_text) 55 | return 56 | end 57 | 58 | local f_out_name = params.f_out_name 59 | if not f_out_name then 60 | f_out_name = f_in_name .. '.formatted' 61 | end 62 | 63 | local formatter_options = 64 | new( 65 | default_formatter_options, 66 | { 67 | indent_chunk = params.indent, 68 | right_margin = params['right-margin'], 69 | max_text_width = params['max-text-width'], 70 | keep_comments = params['keep-comments'], 71 | keep_unparsed_tail = params['keep-unparsed-tail'], 72 | } 73 | ) 74 | 75 | return f_in_name, f_out_name, formatter_options 76 | end 77 | -------------------------------------------------------------------------------- /reformat/usage_text.lua: -------------------------------------------------------------------------------- 1 | return 2 | [[ 3 | Reformat Lua 5.3 code. 4 | 5 | lua.reformat [] 6 | ┬ ┬ ┬ 7 | │ │ │ 8 | └── [] ─────┘ 9 | 10 | : 11 | 12 | --indent= Use given string as indent chunk. 13 | 14 | --right-margin= Right margin, including indent part. 15 | --max-text-width= Maximum text length, excluding indent part. 16 | 17 | --keep-comments Keep comments. 18 | --~keep-comments Remove comments. 19 | 20 | --keep-unparsed-tail Keep unparsed file end. 21 | --~keep-unparsed-tail Remove unparsed file end. 22 | 23 | : 24 | 25 | If not given, file name is generated using prefix. 26 | ]] 27 | -------------------------------------------------------------------------------- /workshop/base.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Lua base libraries extensions. Used almost in any piece of my code. 3 | 4 | This module installs global function "request" which is based on 5 | "require" and makes relative module names possible. 6 | 7 | Also this function tracks module dependencies. This allows to 8 | get dependencies list for any module. Which is used in creating 9 | deploys without unused code. 10 | 11 | Price for this is exported global table "dependencies" and function 12 | "get_require_name". 13 | 14 | And lastly, global functions are added for convenience. Such 15 | functions are families of "is_", "assert_" and "new". 16 | 17 | "new" basically clones given table. But may override fields in 18 | clone with fields of second table argument. 19 | ]] 20 | 21 | -- Export request function: 22 | local split_name = 23 | function(qualified_name) 24 | local prefix_name_pattern = '^(.+%.)([^%.]+)$' -- a.b.c --> (a.b.) (c) 25 | local prefix, name = qualified_name:match(prefix_name_pattern) 26 | if not prefix then 27 | prefix = '' 28 | name = qualified_name 29 | if not name:find('^([^%.]+)$') then 30 | name = '' 31 | end 32 | end 33 | return prefix, name 34 | end 35 | 36 | local unite_prefixes = 37 | function(base_prefix, rel_prefix) 38 | local init_base_prefix, init_rel_prefix = base_prefix, rel_prefix 39 | local list_without_tail_pattern = '(.+%.)[^%.]-%.$' -- a.b.c. --> (a.b.) 40 | local list_without_head_pattern = '[^%.]+%.(.+)$' -- a.b.c. --> (b.c.) 41 | while rel_prefix:find('^%^%.') do 42 | if (base_prefix == '') then 43 | error( 44 | ([[Link "%s" is outside caller's prefix "%s".]]):format( 45 | init_rel_prefix, 46 | init_base_prefix 47 | ) 48 | ) 49 | end 50 | base_prefix = base_prefix:match(list_without_tail_pattern) or '' 51 | rel_prefix = rel_prefix:match(list_without_head_pattern) or '' 52 | end 53 | return base_prefix .. rel_prefix 54 | end 55 | 56 | local names = {} 57 | local deep = 1 58 | 59 | local get_caller_prefix = 60 | function() 61 | local result = '' 62 | if names[deep] then 63 | result = names[deep].prefix 64 | end 65 | return result 66 | end 67 | 68 | local get_caller_name = 69 | function() 70 | local result = 'anonymous' 71 | if names[deep] then 72 | result = names[deep].prefix .. names[deep].name 73 | end 74 | return result 75 | end 76 | 77 | local push = 78 | function(prefix, name) 79 | deep = deep + 1 80 | names[deep] = {prefix = prefix, name = name} 81 | end 82 | 83 | local pop = 84 | function() 85 | deep = deep - 1 86 | end 87 | 88 | local dependencies = {} 89 | local add_dependency = 90 | function(src_name, dest_name) 91 | dependencies[src_name] = dependencies[src_name] or {} 92 | dependencies[src_name][dest_name] = true 93 | end 94 | 95 | local base_prefix = split_name((...)) 96 | 97 | local get_require_name = 98 | function(qualified_name) 99 | local is_absolute_name = (qualified_name:sub(1, 2) == '!.') 100 | if is_absolute_name then 101 | qualified_name = qualified_name:sub(3) 102 | end 103 | local prefix, name = split_name(qualified_name) 104 | local caller_prefix = 105 | is_absolute_name and base_prefix or get_caller_prefix() 106 | prefix = unite_prefixes(caller_prefix, prefix) 107 | return prefix .. name, prefix, name 108 | end 109 | 110 | local request = 111 | function(qualified_name) 112 | local src_name = get_caller_name() 113 | 114 | local require_name, prefix, name = get_require_name(qualified_name) 115 | 116 | push(prefix, name) 117 | local dest_name = get_caller_name() 118 | add_dependency(src_name, dest_name) 119 | local results = table.pack(require(require_name)) 120 | pop() 121 | 122 | return table.unpack(results) 123 | end 124 | 125 | if not _G.request then 126 | _G.request = request 127 | _G.dependencies = dependencies 128 | _G.get_require_name = get_require_name 129 | push('', 'base') 130 | request('!.system.install_is_functions') 131 | request('!.system.install_assert_functions') 132 | _G.new = request('!.table.new') 133 | pop() 134 | end 135 | -------------------------------------------------------------------------------- /workshop/file/as_string.lua: -------------------------------------------------------------------------------- 1 | local safe_open = request('safe_open') 2 | 3 | return 4 | function(file_name) 5 | local f = safe_open(file_name, 'rb') 6 | local result = f:read('a') 7 | f:close() 8 | return result 9 | end 10 | -------------------------------------------------------------------------------- /workshop/file/convert.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Action-call of [generic_file_converter]. 3 | ]] 4 | 5 | local c_converter = request('!.mechs.generic_file_converter.interface') 6 | 7 | return 8 | function(params) 9 | local coverter = new(c_converter, params) 10 | coverter:init() 11 | coverter:run() 12 | end 13 | -------------------------------------------------------------------------------- /workshop/file/exists.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(file_name) 3 | local file_handle = io.open(file_name, 'r') 4 | local result = (file_handle ~= nil) 5 | if result then 6 | io.close(file_handle) 7 | end 8 | return result 9 | end 10 | -------------------------------------------------------------------------------- /workshop/file/get_size.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Return file size in bytes. 3 | 4 | Syntax: 5 | 6 | -> 7 | string integer 8 | ]] 9 | 10 | local safe_open = request('safe_open') 11 | 12 | return 13 | function(file_name) 14 | local f = safe_open(file_name, 'rb') 15 | local result = f:seek('end') 16 | f:close() 17 | return result 18 | end 19 | -------------------------------------------------------------------------------- /workshop/file/safe_open.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(file_name, mode) 3 | assert_string(file_name) 4 | mode = mode or 'rb' 5 | assert_string(mode) 6 | local f_object, err_msg = io.open(file_name, mode) 7 | if not f_object then 8 | error(err_msg, 2) 9 | end 10 | return f_object 11 | end 12 | -------------------------------------------------------------------------------- /workshop/file/text_file_as_string.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Fucken editors tend to save files in UTF-8 or UTF-16. 3 | In this case staring bytes are (EF BB BF), (FF FE) or (FE FF). 4 | They wrecks any further data parsing. 5 | 6 | This routine just strips UTF-8 sequence if found at start of file. 7 | UTF-16 sequences is not stripped as characters will be two-byte 8 | and parsing will fail anyway. 9 | ]] 10 | 11 | local file_as_string = request('as_string') 12 | 13 | local strip_prefixes = 14 | { 15 | '\xef\xbb\xbf', 16 | } 17 | 18 | return 19 | function(file_name) 20 | local result = file_as_string(file_name) 21 | for i = 1, #strip_prefixes do 22 | local prefix = strip_prefixes[i] 23 | if (result:sub(1, #prefix) == prefix) then 24 | result = result:sub(#prefix + 1) 25 | break 26 | end 27 | end 28 | return result 29 | end 30 | -------------------------------------------------------------------------------- /workshop/formats/lua/ast_transformer/align_nodes.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Change/remove some nodes of AST. 3 | 4 | Removed nodes: 5 | * string elements 6 | * 7 | 8 | Changed nodes: 9 | * <.value> added for nodes with single string element 10 | * - exact type resolved. Subnode may sink up. 11 | * - exact type resolved 12 | * - exact type resolved. Subnode sinks up. 13 | ]] 14 | 15 | local replace = request('!.table.replace') 16 | 17 | return 18 | function(self, node) 19 | local node_value 20 | local num_str_values = 0 21 | 22 | for i = #node, 1, -1 do 23 | local child_node = node[i] 24 | if is_string(child_node) then 25 | num_str_values = num_str_values + 1 26 | if (num_str_values == 1) then 27 | node_value = child_node 28 | end 29 | table.remove(node, i) 30 | elseif (child_node.type == 'whitespace') then 31 | table.remove(node, i) 32 | else 33 | self:align_nodes(child_node) 34 | end 35 | end 36 | 37 | if (num_str_values == 1) then 38 | node.value = node_value 39 | end 40 | 41 | if (node.type == 'assign_or_call') then 42 | if (node[1].type == 'function_call') then 43 | replace(node, node[1]) 44 | else 45 | node.type = 'assignment' 46 | end 47 | elseif (node.type == 'var_or_call') then 48 | if (node[#node].type == 'func_args') then 49 | node.type = 'function_call' 50 | else 51 | node.type = 'var_ref' 52 | end 53 | elseif (node.type == 'local_statement') then 54 | replace(node, node[1]) 55 | if (node.type == 'named_function') then 56 | node.type = 'local_named_function' 57 | else 58 | node.type = 'local_assignment' 59 | end 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /workshop/formats/lua/ast_transformer/handlers/assignment.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | rep 3 | 4 | 5 | ]] 6 | 7 | local replace = request('!.table.replace') 8 | 9 | return 10 | function(self, node) 11 | local dest_list = {type = 'ref_list'} 12 | for i = 1, (#node - 1) do 13 | dest_list[#dest_list + 1] = node[i] 14 | end 15 | 16 | local result = 17 | { 18 | type = 'assignment', 19 | dest_list = dest_list, 20 | val_list = node[#node], 21 | } 22 | 23 | replace(node, result) 24 | end 25 | -------------------------------------------------------------------------------- /workshop/formats/lua/ast_transformer/handlers/else_part.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | ]] 4 | return 5 | function(self, node) 6 | node.body = node[1] 7 | node[1] = nil 8 | end 9 | -------------------------------------------------------------------------------- /workshop/formats/lua/ast_transformer/handlers/elseif_part.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | 4 | ]] 5 | 6 | return 7 | function(self, node) 8 | node.condition = node[1] 9 | node[1] = nil 10 | 11 | node.body = node[2] 12 | node[2] = nil 13 | end 14 | -------------------------------------------------------------------------------- /workshop/formats/lua/ast_transformer/handlers/expression.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | rep 3 | 4 | opt_rep 5 | 6 | cho 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | opt 16 | ]] 17 | 18 | local replace = request('!.table.replace') 19 | 20 | return 21 | function(self, node) 22 | local result = {} 23 | 24 | local i = 1 25 | 26 | local n = #node 27 | 28 | local term = {} 29 | 30 | if (node[i].type == 'un_op') then 31 | local un_ops = {} 32 | while (node[i].type == 'un_op') and (i <= n) do 33 | un_ops[#un_ops + 1] = node[i].value 34 | node[i] = nil 35 | i = i + 1 36 | end 37 | term.un_ops = un_ops 38 | end 39 | 40 | term.operand = node[i] 41 | node[i] = nil 42 | i = i + 1 43 | 44 | if (i < n) and (node[i].type == 'bin_op') then 45 | term.bin_op = node[i].value 46 | node[i] = nil 47 | i = i + 1 48 | end 49 | 50 | result[#result + 1] = term 51 | 52 | if (i == n) and (node[i].type == 'expression') then 53 | -- Last node is already processed record. Due align_nodes() construction. 54 | for j = 1, #node[i] do 55 | result[#result + 1] = node[i][j] 56 | end 57 | node[i] = nil 58 | end 59 | 60 | result.type = node.type 61 | 62 | replace(node, result) 63 | end 64 | -------------------------------------------------------------------------------- /workshop/formats/lua/ast_transformer/handlers/function_call.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | function_call: 3 | 4 | name | par_expr 5 | (rep) 6 | (opt rep) bracket_expr | dot_name 7 | (opt) colon_name 8 | func_args 9 | 10 | Input structure is a list with possibly several . 11 | We fold inner , so returned structure at any level 12 | contains only one . 13 | 14 | result: 15 | 16 | name | par_expr 17 | inner_part 18 | 19 | inner_part: 20 | 21 | (opt rep) bracket_expr | dot_name 22 | (opt) inner_part 23 | (opt) colon_name 24 | func_args 25 | ]] 26 | 27 | -- local t2s = request('!.table.as_string') 28 | local replace = request('!.table.replace') 29 | 30 | return 31 | function(self, node) 32 | local result 33 | local name_part 34 | local seek_pos = 0 35 | 36 | while true do 37 | if (seek_pos < #node) then 38 | name_part = {result} 39 | seek_pos = seek_pos + 1 40 | else 41 | break 42 | end 43 | 44 | name_part.type = 'name_parts' 45 | while (node[seek_pos].type ~= 'func_args') do 46 | name_part[#name_part + 1] = node[seek_pos] 47 | seek_pos = seek_pos + 1 48 | end 49 | 50 | local args = node[seek_pos] 51 | 52 | result = 53 | { 54 | type = 'function_call', 55 | name_part = name_part, 56 | args = args, 57 | } 58 | end 59 | 60 | -- print('node\n', t2s(node)) 61 | -- print('result\n', t2s(result)) 62 | 63 | replace(node, result) 64 | end 65 | -------------------------------------------------------------------------------- /workshop/formats/lua/ast_transformer/handlers/function_params.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | opt 3 | opt 4 | ]] 5 | 6 | local replace = request('!.table.replace') 7 | 8 | return 9 | function(self, node) 10 | local result 11 | 12 | if node[1] and (node[1].type == 'name_list') then 13 | result = node[1] 14 | result[#result + 1] = node[2] 15 | else 16 | result = {node[1]} 17 | end 18 | result.type = node.type 19 | 20 | replace(node, result) 21 | end 22 | -------------------------------------------------------------------------------- /workshop/formats/lua/ast_transformer/handlers/generic_for_block.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | 4 | 5 | ]] 6 | 7 | return 8 | function(self, node) 9 | node.names = node[1] 10 | node[1] = nil 11 | 12 | node.expr_list = node[2] 13 | node[2] = nil 14 | 15 | node.body = node[3] 16 | node[3] = nil 17 | end 18 | -------------------------------------------------------------------------------- /workshop/formats/lua/ast_transformer/handlers/if_block.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | opt_rep 4 | opt 5 | ]] 6 | 7 | return 8 | function(self, node) 9 | node.if_part = node[1] 10 | node[1] = nil 11 | 12 | local seek_pos = 2 13 | 14 | local elseif_parts = {} 15 | while 16 | node[seek_pos] and 17 | (node[seek_pos].type == 'elseif_part') 18 | do 19 | elseif_parts[#elseif_parts + 1] = node[seek_pos] 20 | node[seek_pos] = nil 21 | seek_pos = seek_pos + 1 22 | end 23 | if next(elseif_parts) then 24 | node.elseif_parts = elseif_parts 25 | end 26 | 27 | if 28 | node[seek_pos] and 29 | (node[seek_pos].type == 'else_part') 30 | then 31 | node.else_part = node[seek_pos] 32 | node[seek_pos] = nil 33 | seek_pos = seek_pos + 1 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /workshop/formats/lua/ast_transformer/handlers/if_part.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | 4 | ]] 5 | 6 | return 7 | function(self, node) 8 | node.condition = node[1] 9 | node[1] = nil 10 | 11 | node.body = node[2] 12 | node[2] = nil 13 | end 14 | -------------------------------------------------------------------------------- /workshop/formats/lua/ast_transformer/handlers/interface.lua: -------------------------------------------------------------------------------- 1 | return 2 | { 3 | assignment = request('assignment'), 4 | else_part = request('else_part'), 5 | elseif_part = request('elseif_part'), 6 | expression = request('expression'), 7 | function_call = request('function_call'), 8 | function_params = request('function_params'), 9 | generic_for_block = request('generic_for_block'), 10 | if_block = request('if_block'), 11 | if_part = request('if_part'), 12 | key_val = request('key_val'), 13 | local_assignment = request('local_assignment'), 14 | local_named_function = request('local_named_function'), 15 | named_function = request('named_function'), 16 | numeric_for_block = request('numeric_for_block'), 17 | repeat_block = request('repeat_block'), 18 | type_function = request('type_function'), 19 | var_ref = request('var_ref'), 20 | while_block = request('while_block'), 21 | } 22 | -------------------------------------------------------------------------------- /workshop/formats/lua/ast_transformer/handlers/key_val.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self, node) 3 | if (#node == 2) then 4 | node.key = node[1] 5 | node.value = node[2] 6 | else 7 | node.value = node[1] 8 | end 9 | node[1] = nil 10 | node[2] = nil 11 | end 12 | -------------------------------------------------------------------------------- /workshop/formats/lua/ast_transformer/handlers/local_assignment.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self, node) 3 | node.name_list = node[1] 4 | node[1] = nil 5 | 6 | node.val_list = node[2] 7 | node[2] = nil 8 | end 9 | -------------------------------------------------------------------------------- /workshop/formats/lua/ast_transformer/handlers/local_named_function.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | 4 | opt 5 | ]] 6 | 7 | return 8 | function(self, node) 9 | node.dotted_name = node[1] 10 | node[1] = nil 11 | 12 | node.params = node[2] 13 | node[2] = nil 14 | 15 | node.body = node[3] 16 | node[3] = nil 17 | end 18 | -------------------------------------------------------------------------------- /workshop/formats/lua/ast_transformer/handlers/named_function.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | opt 4 | 5 | 6 | ]] 7 | 8 | return 9 | function(self, node) 10 | node.dotted_name = node[1] 11 | node[1] = nil 12 | 13 | local seek_pos = 2 14 | 15 | if (node[seek_pos].type == 'colon_name') then 16 | node.colon_name = node[seek_pos] 17 | node[seek_pos] = nil 18 | seek_pos = seek_pos + 1 19 | end 20 | 21 | node.params = node[seek_pos] 22 | node[seek_pos] = nil 23 | seek_pos = seek_pos + 1 24 | 25 | node.body = node[seek_pos] 26 | node[seek_pos] = nil 27 | seek_pos = seek_pos + 1 28 | end 29 | -------------------------------------------------------------------------------- /workshop/formats/lua/ast_transformer/handlers/numeric_for_block.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | 4 | 5 | opt 6 | 7 | ]] 8 | 9 | return 10 | function(self, node) 11 | node.index = node[1] 12 | node[1] = nil 13 | 14 | node.start_val = node[2] 15 | node[2] = nil 16 | 17 | node.end_val = node[3] 18 | node[3] = nil 19 | 20 | local seek_pos = 4 21 | if (node[seek_pos].type == 'expression') then 22 | node.increment = node[seek_pos] 23 | node[seek_pos] = nil 24 | seek_pos = seek_pos + 1 25 | end 26 | 27 | node.body = node[seek_pos] 28 | node[seek_pos] = nil 29 | seek_pos = seek_pos + 1 30 | end 31 | -------------------------------------------------------------------------------- /workshop/formats/lua/ast_transformer/handlers/repeat_block.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | 4 | ]] 5 | 6 | return 7 | function(self, node) 8 | node.body = node[1] 9 | node[1] = nil 10 | 11 | node.condition = node[2] 12 | node[2] = nil 13 | end 14 | -------------------------------------------------------------------------------- /workshop/formats/lua/ast_transformer/handlers/type_function.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | { 3 | type = 'type_function', 4 | { 5 | type = 'function_params', 6 | value = '()', 7 | }, 8 | { 9 | type = 'statements', 10 | }, 11 | } 12 | ]] 13 | return 14 | function(self, node) 15 | node.params = node[1] 16 | node[1] = nil 17 | 18 | node.body = node[2] 19 | node[2] = nil 20 | end 21 | -------------------------------------------------------------------------------- /workshop/formats/lua/ast_transformer/handlers/var_ref.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | var_ref: 3 | 4 | name | par_expr 5 | (opt rep) 6 | (opt rep) 7 | (opt) colon_name 8 | func_args 9 | bracket_expr | dot_name 10 | 11 | Input structure is quite similar to [function_call] but must not 12 | end in . We wrap subparts with as 13 | and fold them. 14 | 15 | In my opinion, this is the most complex code in preprocessor facility. 16 | 17 | result: 18 | 19 | name | par_expr 20 | (opt) inner_part 21 | bracket_expr | dot_name 22 | 23 | inner_part: 24 | 25 | (opt rep) bracket_expr | dot_name 26 | (opt) inner_part 27 | (opt) colon_name 28 | func_args 29 | ]] 30 | 31 | -- local t2s = request('!.table.as_string') 32 | local replace = request('!.table.replace') 33 | 34 | return 35 | function(self, node) 36 | local result 37 | local name_part 38 | local seek_pos = 0 39 | 40 | while true do 41 | if (seek_pos < #node) then 42 | name_part = {result} 43 | seek_pos = seek_pos + 1 44 | else 45 | break 46 | end 47 | 48 | while 49 | (seek_pos <= #node) and 50 | (node[seek_pos].type ~= 'func_args') 51 | do 52 | name_part[#name_part + 1] = node[seek_pos] 53 | seek_pos = seek_pos + 1 54 | end 55 | name_part.type = 'name_parts' 56 | 57 | local args 58 | if node[seek_pos] then 59 | args = node[seek_pos] 60 | end 61 | result = 62 | { 63 | name_part = name_part, 64 | args = args, 65 | } 66 | if args then 67 | result.type = 'function_call' 68 | else 69 | result.type = 'var_ref' 70 | end 71 | end 72 | 73 | -- print('node\n', t2s(node)) 74 | -- print('result\n', t2s(result)) 75 | 76 | replace(node, result) 77 | end 78 | -------------------------------------------------------------------------------- /workshop/formats/lua/ast_transformer/handlers/while_block.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | 4 | ]] 5 | 6 | return 7 | function(self, node) 8 | node.condition = node[1] 9 | node[1] = nil 10 | 11 | node.body = node[2] 12 | node[2] = nil 13 | end 14 | -------------------------------------------------------------------------------- /workshop/formats/lua/ast_transformer/interface.lua: -------------------------------------------------------------------------------- 1 | return 2 | { 3 | keep_comments = true, 4 | keep_unparsed_tail = true, 5 | 6 | data_struc = nil, 7 | run = request('run'), 8 | 9 | move_comments = request('move_comments'), 10 | handlers = request('handlers.interface'), 11 | align_nodes = request('align_nodes'), 12 | restruc_nodes = request('restruc_nodes'), 13 | } 14 | -------------------------------------------------------------------------------- /workshop/formats/lua/ast_transformer/move_comments.lua: -------------------------------------------------------------------------------- 1 | --[=[ 2 | 3 | (statements) 4 | | (call_assign) 5 | | | (var_link) 6 | | | | (name) 7 | | | | | (whitespace) 8 | | | | | | (comment) 9 | | | | | | | '--[[ Internal Tables ]]' 10 | | | | | (whitespace) 11 | | | | | | (comment) 12 | | | | | | | '--' 13 | | | | | (whitespace) 14 | | | | | | '\x0D\x0A' 15 | | | | | 'qcCompletedQuests' 16 | 17 | => 18 | 19 | (statements) 20 | | (comment) 21 | | | '--[[ Internal Tables ]]' 22 | | (comment) 23 | | | '--' 24 | | (call_assign) 25 | | | (var_link) 26 | | | | (name) 27 | | | | | (whitespace) 28 | | | | | (whitespace) 29 | | | | | (whitespace) 30 | | | | | | '\x0D\x0A' 31 | | | | | 'qcCompletedQuests' 32 | ]=] 33 | 34 | local move_comments 35 | move_comments = 36 | function(node, parent, insertion_point) 37 | if not is_table(node) then 38 | return 39 | end 40 | if (node.type == 'comment') then 41 | table.insert(parent, insertion_point, node) 42 | return 43 | end 44 | if (node.type == 'statements') then 45 | for i = #node, 1, -1 do 46 | move_comments(node[i], node, i) 47 | end 48 | else 49 | for i = #node, 1, -1 do 50 | move_comments(node[i], parent, insertion_point) 51 | end 52 | end 53 | end 54 | 55 | return 56 | function(self) 57 | move_comments(self.data_struc) 58 | end 59 | -------------------------------------------------------------------------------- /workshop/formats/lua/ast_transformer/restruc_nodes.lua: -------------------------------------------------------------------------------- 1 | local t2s = request('!.table.as_string') 2 | 3 | return 4 | function(self, node) 5 | assert_table(node) 6 | -- print(node.type) 7 | 8 | for i = 1, #node do 9 | self:restruc_nodes(node[i]) 10 | end 11 | 12 | local handler = self.handlers[node.type] 13 | if handler then 14 | -- print('', 'processing', node.type) 15 | handler(self, node) 16 | -- print(t2s(node)) 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /workshop/formats/lua/ast_transformer/run.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self) 3 | assert_table(self.data_struc) 4 | 5 | local result = self.data_struc 6 | 7 | if self.keep_comments then 8 | self:move_comments() 9 | end 10 | self:align_nodes(result) 11 | self:restruc_nodes(result) 12 | if not self.keep_unparsed_tail then 13 | result.unparsed_tail = nil 14 | end 15 | 16 | return result 17 | end 18 | -------------------------------------------------------------------------------- /workshop/formats/lua/formatter/get_result.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self) 3 | local result = {} 4 | 5 | table.insert(result, self.data_struc.shebang_str) 6 | table.insert(result, self.printer:get_text()) 7 | table.insert(result, self.data_struc.unparsed_tail) 8 | if not self.is_ok then 9 | table.insert(result, '') 10 | end 11 | 12 | result = table.concat(result, '\n') 13 | 14 | return result 15 | end 16 | -------------------------------------------------------------------------------- /workshop/formats/lua/formatter/handlers/expression.lua: -------------------------------------------------------------------------------- 1 | local represent_operand_oneline = 2 | function(self, node) 3 | local printer = self.printer 4 | if node.un_ops then 5 | local cur_un_op, prev_un_op 6 | for i = 1, #node.un_ops do 7 | cur_un_op = node.un_ops[i] 8 | if (prev_un_op == '-') and (cur_un_op == '-') then 9 | printer:add_curline(' -') 10 | elseif (cur_un_op == 'not') then 11 | printer:add_curline('not ') 12 | else 13 | printer:add_curline(cur_un_op) 14 | end 15 | prev_un_op = cur_un_op 16 | end 17 | end 18 | if not self:process_node(node.operand) then 19 | return 20 | end 21 | if node.bin_op then 22 | printer:add_curline(' ' .. node.bin_op .. ' ') 23 | end 24 | return true 25 | end 26 | 27 | local oneliner = 28 | function(self, node) 29 | for term_idx = 1, #node do 30 | if not represent_operand_oneline(self, node[term_idx]) then 31 | return 32 | end 33 | end 34 | return true 35 | end 36 | 37 | local line_wrap_ops = 38 | { 39 | ['and'] = true, 40 | ['or'] = true, 41 | ['+'] = true, 42 | ['-'] = true, 43 | ['*'] = true, 44 | ['/'] = true, 45 | ['//'] = true, 46 | ['..'] = true, 47 | } 48 | 49 | local represent_operand_multiline = 50 | function(self, node) 51 | if not represent_operand_oneline(self, node) then 52 | return 53 | end 54 | if line_wrap_ops[node.bin_op] then 55 | self.printer:request_clean_line() 56 | end 57 | return true 58 | end 59 | 60 | local multiliner = 61 | function(self, node) 62 | local printer = self.printer 63 | printer:request_clean_line() 64 | for term_idx = 1, #node do 65 | if not represent_operand_multiline(self, node[term_idx]) then 66 | return 67 | end 68 | end 69 | return true 70 | end 71 | 72 | return 73 | function(self, node) 74 | return self:variate(node, oneliner, multiliner) 75 | end 76 | -------------------------------------------------------------------------------- /workshop/formats/lua/formatter/handlers/expressions/function_call.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self, node) 3 | return 4 | self:process_node(node.name_part) and 5 | self:process_node(node.args) 6 | end 7 | -------------------------------------------------------------------------------- /workshop/formats/lua/formatter/handlers/expressions/string.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self, node) 3 | --[=[ 4 | Quite ugly handling indexing [[[s]]] case: convert to [ [[s]]]. 5 | 6 | Pasted from [lua_table.save.install_node_handlers.minimal. 7 | Should remove this doubling. 8 | ]=] 9 | if not self.printer:on_clean_line() then 10 | local text_line = self.printer.line_with_text:get_line() 11 | if 12 | (text_line:sub(-1) == '[') and 13 | (node.value:sub(1, 1) == '[') 14 | then 15 | self.printer:add_curline(' ') 16 | end 17 | end 18 | 19 | self.printer:add_curline(node.value) 20 | return true 21 | end 22 | -------------------------------------------------------------------------------- /workshop/formats/lua/formatter/handlers/expressions/table.lua: -------------------------------------------------------------------------------- 1 | local oneliner = 2 | function(self, node) 3 | local printer = self.printer 4 | printer:add_curline('{') 5 | for i = 1, #node do 6 | if (i > 1) then 7 | printer:add_curline(', ') 8 | end 9 | local key, value = node[i].key, node[i].value 10 | if key then 11 | if not self:process_node(key) then 12 | return 13 | end 14 | printer:add_curline(' = ') 15 | end 16 | if not self:process_node(value) then 17 | return 18 | end 19 | end 20 | printer:add_curline('}') 21 | return true 22 | end 23 | 24 | local multiliner = 25 | function(self, node) 26 | local printer = self.printer 27 | 28 | printer:request_clean_line() 29 | printer:add_curline('{') 30 | 31 | printer:inc_indent() 32 | for i = 1, #node do 33 | printer:request_clean_line() 34 | local key, value = node[i].key, node[i].value 35 | if key then 36 | if not self:process_node(key) then 37 | return 38 | end 39 | printer:add_curline(' = ') 40 | if not self:process_block(value) then 41 | return 42 | end 43 | printer:add_textline(',') 44 | else 45 | if not self:process_node(value) then 46 | return 47 | end 48 | printer:add_textline(',') 49 | end 50 | end 51 | printer:dec_indent() 52 | 53 | printer:request_clean_line() 54 | printer:add_curline('}') 55 | return true 56 | end 57 | 58 | return 59 | function(self, node) 60 | if (#node == 0) then 61 | self.printer:add_curline('{}') 62 | return true 63 | else 64 | return self:variate(node, oneliner, multiliner) 65 | end 66 | end 67 | -------------------------------------------------------------------------------- /workshop/formats/lua/formatter/handlers/expressions/type_function.lua: -------------------------------------------------------------------------------- 1 | local multiliner = 2 | function(self, node) 3 | local printer = self.printer 4 | 5 | printer:request_clean_line() 6 | printer:add_curline('function') 7 | if not self:process_node(node.params) then 8 | return 9 | end 10 | 11 | printer:request_clean_line() 12 | return self:process_block_multiline(nil, node.body, 'end') 13 | end 14 | 15 | return 16 | function(self, node) 17 | return self:variate(node, nil, multiliner) 18 | end 19 | -------------------------------------------------------------------------------- /workshop/formats/lua/formatter/handlers/expressions/var_ref.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self, node) 3 | return self:process_node(node.name_part) 4 | end 5 | -------------------------------------------------------------------------------- /workshop/formats/lua/formatter/handlers/interface.lua: -------------------------------------------------------------------------------- 1 | return 2 | { 3 | expression = request('expression'), 4 | 5 | function_call = request('expressions.function_call'), 6 | var_ref = request('expressions.var_ref'), 7 | 8 | string = request('expressions.string'), 9 | table = request('expressions.table'), 10 | type_function = request('expressions.type_function'), 11 | 12 | statements = request('statements'), 13 | 14 | assignment = request('statements.assignment'), 15 | break_statement = request('statements.break_statement'), 16 | comment = request('statements.comment'), 17 | goto_statement = request('statements.goto_statement'), 18 | label_statement = request('statements.label_statement'), 19 | local_assignment = request('statements.local_assignment'), 20 | return_statement = request('statements.return_statement'), 21 | 22 | do_block = request('statements.blocks.do_block'), 23 | generic_for_block = request('statements.blocks.generic_for_block'), 24 | if_block =request('statements.blocks.if_block'), 25 | local_named_function = request('statements.blocks.local_named_function'), 26 | named_function = request('statements.blocks.named_function'), 27 | numeric_for_block = request('statements.blocks.numeric_for_block'), 28 | repeat_block = request('statements.blocks.repeat_block'), 29 | while_block = request('statements.blocks.while_block'), 30 | 31 | bracket_expr = request('wrappers.bracket_expr'), 32 | colon_name = request('wrappers.colon_name'), 33 | dot_name = request('wrappers.dot_name'), 34 | dot_list = request('wrappers.dot_list'), 35 | expr_list = request('wrappers.expr_list'), 36 | func_args = request('wrappers.func_args'), 37 | function_params = request('wrappers.function_params'), 38 | name_list = request('wrappers.name_list'), 39 | name_parts = request('wrappers.name_parts'), 40 | par_expr = request('wrappers.par_expr'), 41 | ref_list = request('wrappers.ref_list'), 42 | } 43 | -------------------------------------------------------------------------------- /workshop/formats/lua/formatter/handlers/statements.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Handle sequence of statements. 3 | 4 | Each statement starts with new line, except ";". 5 | ]] 6 | 7 | local multiliner = 8 | function(self, node) 9 | local printer = self.printer 10 | for i = 1, #node do 11 | if (node[i].type ~= 'empty_statement') then 12 | self.printer:request_clean_line() 13 | end 14 | if not self:process_node(node[i]) then 15 | return 16 | end 17 | end 18 | return true 19 | end 20 | 21 | return 22 | function(self, node) 23 | return self:variate(node, nil, multiliner) 24 | end 25 | -------------------------------------------------------------------------------- /workshop/formats/lua/formatter/handlers/statements/assignment.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self, node) 3 | if not self:process_node(node.dest_list) then 4 | return 5 | end 6 | if node.val_list then 7 | self.printer:add_textline(' = ') 8 | return self:process_block(node.val_list) 9 | end 10 | return true 11 | end 12 | -------------------------------------------------------------------------------- /workshop/formats/lua/formatter/handlers/statements/blocks/do_block.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self, node) 3 | self.printer:request_clean_line() 4 | return self:process_block_multiline('do', node[1], 'end') 5 | end 6 | -------------------------------------------------------------------------------- /workshop/formats/lua/formatter/handlers/statements/blocks/generic_for_block.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Layout: 3 | 4 | for in do 5 | ]] 6 | local header_oneline = 7 | function(self, node) 8 | local printer = self.printer 9 | printer:add_curline('for ') 10 | if not self:process_node(node.names) then 11 | return 12 | end 13 | printer:add_curline(' in ') 14 | if not self:process_node(node.expr_list) then 15 | return 16 | end 17 | printer:add_curline(' do') 18 | return true 19 | end 20 | 21 | --[[ 22 | Layout: 23 | 24 | [...] in 25 | ]] 26 | local in_part_oneline = 27 | function(self, node) 28 | self.printer:add_curline(' in ') 29 | return self:process_node(node.expr_list) 30 | end 31 | 32 | --[[ 33 | Layout: 34 | 35 | [...] in 36 | 37 | ]] 38 | local in_part_multiline = 39 | function(self, node) 40 | self.printer:add_textline(' in') 41 | 42 | self.printer:request_clean_line() 43 | return self:process_block(node.expr_list) 44 | end 45 | 46 | --[[ 47 | Layout: 48 | 49 | for 50 | <[in_part_oneline() | in_part_multiline()]> 51 | do 52 | ]] 53 | local header_multiline = 54 | function(self, node) 55 | local printer = self.printer 56 | printer:add_curline('for') 57 | 58 | printer:request_clean_line() 59 | printer:inc_indent() 60 | if not self:process_node(node.names) then 61 | return 62 | end 63 | if not self:variate(node, in_part_oneline, in_part_multiline) then 64 | return 65 | end 66 | printer:dec_indent() 67 | 68 | printer:request_clean_line() 69 | printer:add_curline('do') 70 | return true 71 | end 72 | 73 | --[[ 74 | Layout: 75 | 76 | <[header_oneline() | header_multiline()]> 77 | 78 | end 79 | ]] 80 | local multiliner = 81 | function(self, node) 82 | local printer = self.printer 83 | 84 | printer:request_clean_line() 85 | if not self:variate(node, header_oneline, header_multiline) then 86 | return 87 | end 88 | 89 | printer:request_clean_line() 90 | if not self:process_block(node.body) then 91 | return 92 | end 93 | 94 | printer:request_clean_line() 95 | printer:add_curline('end') 96 | 97 | return true 98 | end 99 | 100 | return 101 | function(self, node) 102 | return self:variate(node, nil, multiliner) 103 | end 104 | -------------------------------------------------------------------------------- /workshop/formats/lua/formatter/handlers/statements/blocks/if_block.lua: -------------------------------------------------------------------------------- 1 | local oneline_if = 2 | function(self, node) 3 | return self:process_block_oneline('if ', node, ' then') 4 | end 5 | 6 | local multiline_if = 7 | function(self, node) 8 | return self:process_block_multiline('if', node, 'then') 9 | end 10 | 11 | local oneline_elseif = 12 | function(self, node) 13 | return self:process_block_oneline('elseif ', node, ' then') 14 | end 15 | 16 | local multiline_elseif = 17 | function(self, node) 18 | return self:process_block_multiline('elseif', node, 'then') 19 | end 20 | 21 | return 22 | function(self, node) 23 | local printer = self.printer 24 | 25 | printer:request_clean_line() 26 | if not self:variate(node.if_part.condition, oneline_if, multiline_if) then 27 | return 28 | end 29 | 30 | printer:request_clean_line() 31 | if not self:process_block(node.if_part.body) then 32 | return 33 | end 34 | 35 | if node.elseif_parts then 36 | for i = 1, #node.elseif_parts do 37 | printer:request_clean_line() 38 | if 39 | not self:variate( 40 | node.elseif_parts[i].condition, 41 | oneline_elseif, 42 | multiline_elseif 43 | ) 44 | then 45 | return 46 | end 47 | 48 | printer:request_clean_line() 49 | if not self:process_block(node.elseif_parts[i].body) then 50 | return 51 | end 52 | end 53 | end 54 | 55 | if node.else_part then 56 | printer:request_clean_line() 57 | if not self:process_block_multiline('else', node.else_part.body) then 58 | return 59 | end 60 | end 61 | 62 | printer:request_clean_line() 63 | self.printer:add_curline('end') 64 | return true 65 | end 66 | -------------------------------------------------------------------------------- /workshop/formats/lua/formatter/handlers/statements/blocks/local_named_function.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self, node) 3 | local printer = self.printer 4 | 5 | printer:request_empty_line() 6 | printer:add_curline('local function ') 7 | if not self:process_node(node.dotted_name) then 8 | return 9 | end 10 | if not self:process_node(node.params) then 11 | return 12 | end 13 | 14 | printer:request_clean_line() 15 | if not self:process_block_multiline(nil, node.body, 'end') then 16 | return 17 | end 18 | 19 | printer:request_empty_line() 20 | return true 21 | end 22 | -------------------------------------------------------------------------------- /workshop/formats/lua/formatter/handlers/statements/blocks/named_function.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Represent global gunction defined like 3 | 4 | function somename.bla.bla:bla(args) 5 | 6 | Layout ("--" means empty line): 7 | 8 | -- 9 | function 10 | 11 | end 12 | -- 13 | ]] 14 | 15 | return 16 | function(self, node) 17 | local printer = self.printer 18 | 19 | printer:request_empty_line() 20 | printer:add_curline('function ') 21 | if not self:process_node(node.dotted_name) then 22 | return 23 | end 24 | if node.colon_name then 25 | if not self:process_node(node.colon_name) then 26 | return 27 | end 28 | end 29 | if not self:process_node(node.params) then 30 | return 31 | end 32 | 33 | printer:request_clean_line() 34 | if not self:process_block_multiline(nil, node.body, 'end') then 35 | return 36 | end 37 | 38 | printer:request_empty_line() 39 | return true 40 | end 41 | -------------------------------------------------------------------------------- /workshop/formats/lua/formatter/handlers/statements/blocks/numeric_for_block.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self, node) 3 | self.printer:add_curline('for ') 4 | if not self:process_node(node.index) then 5 | return 6 | end 7 | self.printer:add_curline(' = ') 8 | if not self:process_node(node.start_val) then 9 | return 10 | end 11 | self.printer:add_curline(', ') 12 | if not self:process_node(node.end_val) then 13 | return 14 | end 15 | if node.increment then 16 | self.printer:add_curline(', ') 17 | if not self:process_node(node.increment) then 18 | return 19 | end 20 | end 21 | self.printer:add_curline(' ') 22 | return self:process_block_multiline('do', node.body, 'end') 23 | end 24 | -------------------------------------------------------------------------------- /workshop/formats/lua/formatter/handlers/statements/blocks/repeat_block.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self, node) 3 | if not self:process_block_multiline('repeat', node.body, 'until') then 4 | return 5 | end 6 | self.printer:add_curline(' ') 7 | return self:process_node(node.condition) 8 | end 9 | -------------------------------------------------------------------------------- /workshop/formats/lua/formatter/handlers/statements/blocks/while_block.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self, node) 3 | return 4 | self:process_block_oneline('while ', node.condition, ' do') and 5 | self:process_block_multiline(nil, node.body, 'end') 6 | end 7 | -------------------------------------------------------------------------------- /workshop/formats/lua/formatter/handlers/statements/break_statement.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self, node) 3 | self.printer:add_curline('break') 4 | return true 5 | end 6 | -------------------------------------------------------------------------------- /workshop/formats/lua/formatter/handlers/statements/comment.lua: -------------------------------------------------------------------------------- 1 | local trim_linefeed = request('!.string.trim_linefeed') 2 | 3 | return 4 | function(self, node) 5 | --[[ 6 | Line comments includes tail newline. Newline is automatically 7 | added after any statement. Comment considered statement. 8 | 9 | So quick fix is trim tail newline from comment. 10 | ]] 11 | self.printer:add_curline(trim_linefeed(node.value)) 12 | return true 13 | end 14 | -------------------------------------------------------------------------------- /workshop/formats/lua/formatter/handlers/statements/goto_statement.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self, node) 3 | self.printer:add_curline('goto ') 4 | return self:process_node(node[1]) 5 | end 6 | -------------------------------------------------------------------------------- /workshop/formats/lua/formatter/handlers/statements/label_statement.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self, node) 3 | self.printer:add_curline('::') 4 | if not self:process_node(node[1]) then 5 | return 6 | end 7 | self.printer:add_curline('::') 8 | return true 9 | end 10 | -------------------------------------------------------------------------------- /workshop/formats/lua/formatter/handlers/statements/local_assignment.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self, node) 3 | self.printer:add_curline('local ') 4 | if not self:process_block(node.name_list) then 5 | return 6 | end 7 | if node.val_list then 8 | self.printer:add_textline(' = ') 9 | return self:process_block(node.val_list) 10 | end 11 | return true 12 | end 13 | -------------------------------------------------------------------------------- /workshop/formats/lua/formatter/handlers/statements/return_statement.lua: -------------------------------------------------------------------------------- 1 | local oneliner = 2 | function(self, node) 3 | self.printer:add_curline('return ') 4 | return self:process_node(node[1]) 5 | end 6 | 7 | local multiliner = 8 | function(self, node) 9 | return self:process_block_multiline('return', node[1]) 10 | end 11 | 12 | return 13 | function(self, node) 14 | if not node[1] then 15 | self.printer:add_curline('return') 16 | return true 17 | else 18 | return self:variate(node, oneliner, multiliner) 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /workshop/formats/lua/formatter/handlers/wrappers/bracket_expr.lua: -------------------------------------------------------------------------------- 1 | local oneliner = 2 | function(self, node) 3 | self.printer:add_curline('[') 4 | if not self:process_node(node[1]) then 5 | return 6 | end 7 | self.printer:add_curline(']') 8 | return true 9 | end 10 | 11 | local multiliner = 12 | function(self, node) 13 | self.printer:add_curline('[') 14 | 15 | self.printer:request_clean_line() 16 | if not self:process_block(node[1]) then 17 | return 18 | end 19 | 20 | self.printer:request_clean_line() 21 | self.printer:add_curline(']') 22 | 23 | return true 24 | end 25 | 26 | return 27 | function(self, node) 28 | return self:variate(node, oneliner, multiliner) 29 | end 30 | -------------------------------------------------------------------------------- /workshop/formats/lua/formatter/handlers/wrappers/colon_name.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self, node) 3 | self.printer:add_curline(':') 4 | return self:process_node(node[1]) 5 | end 6 | -------------------------------------------------------------------------------- /workshop/formats/lua/formatter/handlers/wrappers/dot_list.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self, node) 3 | return self:process_list(node, '.') 4 | end 5 | -------------------------------------------------------------------------------- /workshop/formats/lua/formatter/handlers/wrappers/dot_name.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self, node) 3 | self.printer:add_curline('.') 4 | return self:process_node(node[1]) 5 | end 6 | -------------------------------------------------------------------------------- /workshop/formats/lua/formatter/handlers/wrappers/expr_list.lua: -------------------------------------------------------------------------------- 1 | local oneline_delimiter = ', ' 2 | 3 | local oneliner = 4 | function(self, node) 5 | return self:process_list(node, oneline_delimiter) 6 | end 7 | 8 | local multiline_delimiter = 9 | function(self) 10 | self.printer:add_textline(',') 11 | self.printer:request_clean_line() 12 | end 13 | 14 | local multiliner = 15 | function(self, node) 16 | self.printer:request_clean_line() 17 | return self:process_list_variative(node, oneline_delimiter, multiline_delimiter) 18 | end 19 | 20 | return 21 | function(self, node) 22 | return self:variate(node, oneliner, multiliner) 23 | end 24 | -------------------------------------------------------------------------------- /workshop/formats/lua/formatter/handlers/wrappers/func_args.lua: -------------------------------------------------------------------------------- 1 | return request('function_params') 2 | -------------------------------------------------------------------------------- /workshop/formats/lua/formatter/handlers/wrappers/function_params.lua: -------------------------------------------------------------------------------- 1 | local oneline_delimiter = ', ' 2 | 3 | local oneliner = 4 | function(self, node) 5 | self.printer:add_curline('(') 6 | if not self:process_list(node, oneline_delimiter) then 7 | return 8 | end 9 | self.printer:add_curline(')') 10 | return true 11 | end 12 | 13 | local multiline_delimiter = 14 | function(self) 15 | self.printer:add_textline(',') 16 | self.printer:request_clean_line() 17 | end 18 | 19 | local multiline_block_processor = 20 | function(self, node) 21 | return self:process_list_variative(node, oneline_delimiter, multiline_delimiter) 22 | end 23 | 24 | local multiliner = 25 | function(self, node) 26 | self.printer:add_curline('(') 27 | self.printer:request_clean_line() 28 | if not self:process_block(node, multiline_block_processor) then 29 | return 30 | end 31 | self.printer:request_clean_line() 32 | self.printer:add_curline(')') 33 | return true 34 | end 35 | 36 | return 37 | function(self, node) 38 | if (#node == 0) then 39 | self.printer:add_curline('()') 40 | return true 41 | else 42 | return self:variate(node, oneliner, multiliner) 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /workshop/formats/lua/formatter/handlers/wrappers/name_list.lua: -------------------------------------------------------------------------------- 1 | return request('expr_list') 2 | -------------------------------------------------------------------------------- /workshop/formats/lua/formatter/handlers/wrappers/name_parts.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self, node) 3 | return self:process_list(node) 4 | end 5 | -------------------------------------------------------------------------------- /workshop/formats/lua/formatter/handlers/wrappers/par_expr.lua: -------------------------------------------------------------------------------- 1 | local oneliner = 2 | function(self, node) 3 | self.printer:add_curline('(') 4 | if not self:process_node(node[1]) then 5 | return 6 | end 7 | self.printer:add_curline(')') 8 | return true 9 | end 10 | 11 | local multiliner = 12 | function(self, node) 13 | self.printer:add_curline('(') 14 | 15 | self.printer:request_clean_line() 16 | if not self:process_block(node[1]) then 17 | return 18 | end 19 | 20 | self.printer:request_clean_line() 21 | self.printer:add_curline(')') 22 | 23 | return true 24 | end 25 | 26 | return 27 | function(self, node) 28 | return self:variate(node, oneliner, multiliner) 29 | end 30 | -------------------------------------------------------------------------------- /workshop/formats/lua/formatter/handlers/wrappers/ref_list.lua: -------------------------------------------------------------------------------- 1 | return request('expr_list') 2 | -------------------------------------------------------------------------------- /workshop/formats/lua/formatter/init.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self) 3 | self.printer.indent_chunk = self.indent_chunk 4 | self.printer:init() 5 | self.state_keeper:init() 6 | 7 | assert_table(self.data_struc) 8 | end 9 | -------------------------------------------------------------------------------- /workshop/formats/lua/formatter/interface.lua: -------------------------------------------------------------------------------- 1 | return 2 | { 3 | right_margin = 80, 4 | max_text_width = 68, 5 | indent_chunk = ' ', 6 | 7 | keep_comments = true, 8 | keep_unparsed_tail = true, 9 | 10 | data_struc = nil, 11 | is_ok = true, 12 | 13 | init = request('init'), 14 | run = request('run'), 15 | get_result = request('get_result'), 16 | 17 | printer = request('!.mechs.text_block.interface'), 18 | state_keeper = request('state_keeper.interface'), 19 | 20 | handlers = request('handlers.interface'), 21 | 22 | process_list = request('process_list'), 23 | process_node = request('process_node'), 24 | 25 | process_block_oneline = request('process_block_oneline'), 26 | process_block_multiline = request('process_block_multiline'), 27 | process_block = request('process_block'), 28 | 29 | represent = request('represent'), 30 | variate = request('variate'), 31 | process_list_variative = request('process_list_variative'), 32 | 33 | representation_is_allowed = request('representation_is_allowed'), 34 | } 35 | 36 | --[[ 37 | limits length of line without indent, i.e. 38 | length of text in line. Setting it makes sense for windowed 39 | viewing in editor. 40 | limits length of line with indent. Setting it 41 | makes sense for printring. 42 | ]] 43 | -------------------------------------------------------------------------------- /workshop/formats/lua/formatter/process_block.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self, node, alternate_handler) 3 | local result 4 | self.printer:inc_indent() 5 | if alternate_handler then 6 | result = alternate_handler(self, node) 7 | else 8 | result = self:process_node(node) 9 | end 10 | self.printer:dec_indent() 11 | return result 12 | end 13 | -------------------------------------------------------------------------------- /workshop/formats/lua/formatter/process_block_multiline.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self, prefix, node, postfix, alternate_handler) 3 | local printer = self.printer 4 | 5 | if prefix then 6 | printer:add_curline(prefix) 7 | end 8 | 9 | printer:request_clean_line() 10 | local result = self:process_block(node, alternate_handler) 11 | 12 | if postfix then 13 | printer:request_clean_line() 14 | printer:add_curline(postfix) 15 | end 16 | 17 | return result 18 | end 19 | -------------------------------------------------------------------------------- /workshop/formats/lua/formatter/process_block_oneline.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self, prefix, node, postfix, alternate_handler) 3 | local printer = self.printer 4 | if prefix then 5 | printer:add_curline(prefix) 6 | end 7 | local result = self:process_block(node, alternate_handler) 8 | if postfix then 9 | printer:add_curline(postfix) 10 | end 11 | return result 12 | end 13 | -------------------------------------------------------------------------------- /workshop/formats/lua/formatter/process_list.lua: -------------------------------------------------------------------------------- 1 | local apply_delimiter = 2 | function(self, delimiter) 3 | if delimiter then 4 | if is_function(delimiter) then 5 | delimiter(self) 6 | else 7 | self.printer:add_curline(delimiter) 8 | end 9 | end 10 | end 11 | 12 | return 13 | function(self, node, delimiter, apply_tail_delimiter) 14 | if (#node > 0) then 15 | for i = 1, (#node - 1) do 16 | if not self:process_node(node[i]) then 17 | return 18 | end 19 | apply_delimiter(self, delimiter) 20 | end 21 | if not self:process_node(node[#node]) then 22 | return 23 | end 24 | if apply_tail_delimiter then 25 | apply_delimiter(self, delimiter) 26 | end 27 | end 28 | return true 29 | end 30 | -------------------------------------------------------------------------------- /workshop/formats/lua/formatter/process_list_variative.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self, node, oneline_delimiter, multiline_delimiter, apply_tail_delimiter) 3 | if (#node > 0) then 4 | local oneliner = 5 | function(self, node) 6 | return self:process_list(node, oneline_delimiter, apply_tail_delimiter) 7 | end 8 | local multiliner = 9 | function(self, node) 10 | return self:process_list(node, multiline_delimiter, apply_tail_delimiter) 11 | end 12 | return self:variate(node, oneliner, multiliner) 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /workshop/formats/lua/formatter/process_node.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self, node) 3 | if is_table(node) and node.type then 4 | local node_type = node.type or '?nil' 5 | local handler = self.handlers[node_type] 6 | if is_function(handler) then 7 | return handler(self, node) 8 | else 9 | if is_string(node.value) then 10 | self.printer:add_curline(node.value) 11 | return true 12 | else 13 | local msg = 14 | (''): 15 | format(node_type) 16 | self.printer:add_curline(msg) 17 | return 18 | end 19 | end 20 | else 21 | if is_nil(node) then 22 | --[[ 23 | Passed nil. It's ok. 24 | 25 | (For example may have .body = nil) 26 | ]] 27 | elseif is_table(node) then 28 | --[[ 29 | Passed some table without <.type>. It occurs for empty files 30 | or for files which we failed to parse. 31 | 32 | It's ok when given node is not sequence. 33 | ]] 34 | assert(not node[1]) 35 | else 36 | print('!', 'type(node)', type(node)) 37 | print('There are some uncovered cases in code formatter.') 38 | end 39 | return true 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /workshop/formats/lua/formatter/represent.lua: -------------------------------------------------------------------------------- 1 | local printer_class = request('!.mechs.text_block.interface') 2 | 3 | return 4 | function(self, representer, node) 5 | local original = self.printer 6 | 7 | local trial = new(printer_class) 8 | 9 | trial.indent_chunk = original.indent_chunk 10 | trial:init() 11 | 12 | trial.line_with_text.indent = original.line_with_text.indent 13 | if not original:on_clean_line() then 14 | trial.line_with_text.text = original.line_with_text.text 15 | end 16 | 17 | trial.next_line_indent = original.next_line_indent 18 | 19 | self.printer = trial 20 | local has_failed = not representer(self, node) 21 | self.printer = original 22 | 23 | return trial, has_failed 24 | end 25 | 26 | -------------------------------------------------------------------------------- /workshop/formats/lua/formatter/representation_is_allowed.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self, representation) 3 | local result = true 4 | 5 | local text_margin = representation:get_block_width() 6 | if self.right_margin then 7 | result = result and (text_margin <= self.right_margin) 8 | end 9 | 10 | local text_width = representation:get_text_width() 11 | if self.max_text_width then 12 | result = result and (text_width <= self.max_text_width) 13 | end 14 | 15 | return result 16 | end 17 | -------------------------------------------------------------------------------- /workshop/formats/lua/formatter/run.lua: -------------------------------------------------------------------------------- 1 | local transform_ast = request('!.formats.lua.transform_ast') 2 | 3 | return 4 | function(self) 5 | self.data_struc = 6 | transform_ast( 7 | self.data_struc, 8 | { 9 | keep_comments = self.keep_comments, 10 | keep_unparsed_tail = self.keep_unparsed_tail, 11 | } 12 | ) 13 | self.is_ok = self:process_node(self.data_struc) 14 | 15 | return self:get_result() 16 | end 17 | -------------------------------------------------------------------------------- /workshop/formats/lua/formatter/state_keeper/enter_level.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self) 3 | self.position = self.position + 1 4 | end 5 | -------------------------------------------------------------------------------- /workshop/formats/lua/formatter/state_keeper/get_child_state.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self) 3 | return self.states[self.position + 1] 4 | end 5 | -------------------------------------------------------------------------------- /workshop/formats/lua/formatter/state_keeper/get_state.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self) 3 | return self.states[self.position] 4 | end 5 | -------------------------------------------------------------------------------- /workshop/formats/lua/formatter/state_keeper/init.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self) 3 | self.states = {} 4 | self.position = 0 5 | end 6 | -------------------------------------------------------------------------------- /workshop/formats/lua/formatter/state_keeper/interface.lua: -------------------------------------------------------------------------------- 1 | return 2 | { 3 | states = nil, 4 | position = nil, 5 | init = request('init'), 6 | 7 | enter_level = request('enter_level'), 8 | leave_level = request('leave_level'), 9 | get_state = request('get_state'), 10 | set_state = request('set_state'), 11 | get_child_state = request('get_child_state'), 12 | set_child_state = request('set_child_state'), 13 | } 14 | -------------------------------------------------------------------------------- /workshop/formats/lua/formatter/state_keeper/leave_level.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self) 3 | self.position = self.position - 1 4 | end 5 | -------------------------------------------------------------------------------- /workshop/formats/lua/formatter/state_keeper/set_child_state.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self, new_state) 3 | self.states[self.position + 1] = new_state 4 | end 5 | -------------------------------------------------------------------------------- /workshop/formats/lua/formatter/state_keeper/set_state.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self, new_state) 3 | self.states[self.position] = new_state 4 | end 5 | -------------------------------------------------------------------------------- /workshop/formats/lua/formatter/variate.lua: -------------------------------------------------------------------------------- 1 | local default_state = {is_multiline_allowed = true, is_last_hope = true} 2 | 3 | return 4 | function(self, node, oneliner, multiliner) 5 | self.state_keeper:enter_level() 6 | local our_state = self.state_keeper:get_state() or default_state 7 | local is_multiline_allowed = our_state.is_multiline_allowed 8 | local is_last_hope = our_state.is_last_hope 9 | --[[ 10 | print( 11 | 'variate', 12 | node.type, 13 | oneliner and '1' or '.', 14 | multiliner and 'M' or '.', 15 | is_last_hope and 'H' or '.', 16 | is_multiline_allowed 17 | ) 18 | --]] 19 | 20 | local last_hope_handler 21 | if multiliner and is_multiline_allowed then 22 | last_hope_handler = multiliner 23 | else 24 | last_hope_handler = oneliner 25 | end 26 | 27 | local result_representation 28 | local has_succeeded = false 29 | 30 | local do_represent = 31 | function(representer, is_multiline) 32 | local representer_is_last_hope = 33 | is_last_hope and 34 | (representer == last_hope_handler) 35 | 36 | self.state_keeper:set_child_state( 37 | { 38 | is_multiline_allowed = is_multiline, 39 | is_last_hope = representer_is_last_hope, 40 | } 41 | ) 42 | 43 | local trial_representation, has_failed = 44 | self:represent(representer, node) 45 | if 46 | representer_is_last_hope or 47 | ( 48 | not has_failed and 49 | self:representation_is_allowed(trial_representation) 50 | ) 51 | then 52 | result_representation = trial_representation 53 | has_succeeded = true 54 | end 55 | end 56 | 57 | if oneliner then 58 | do_represent(oneliner, false) 59 | end 60 | if 61 | not result_representation and 62 | multiliner and 63 | is_multiline_allowed 64 | then 65 | do_represent(multiliner, true) 66 | end 67 | 68 | if result_representation then 69 | if not self.printer:on_clean_line() then 70 | --[[ 71 | Current line is already in result as it was added 72 | in represent(). So remove it before concat with result. 73 | ]] 74 | self.printer.line_with_text.text = '' 75 | end 76 | self.printer:include(result_representation, true) 77 | end 78 | 79 | self.state_keeper:leave_level() 80 | 81 | return has_succeeded 82 | end 83 | -------------------------------------------------------------------------------- /workshop/formats/lua/is_identifier.lua: -------------------------------------------------------------------------------- 1 | local keywords = request('keywords') 2 | 3 | return 4 | function(s) 5 | return 6 | is_string(s) and 7 | s:match('^[%a_][%w_]*$') and 8 | not keywords[s] 9 | end 10 | -------------------------------------------------------------------------------- /workshop/formats/lua/keywords.lua: -------------------------------------------------------------------------------- 1 | local map_values = request('!.table.map_values') 2 | 3 | return 4 | map_values( 5 | { 6 | 'nil', 7 | 'true', 8 | 'false', 9 | 'not', 10 | 'and', 11 | 'or', 12 | 'do', 13 | 'end', 14 | 'local', 15 | 'function', 16 | 'goto', 17 | 'if', 18 | 'then', 19 | 'elseif', 20 | 'else', 21 | 'while', 22 | 'repeat', 23 | 'until', 24 | 'for', 25 | 'in', 26 | 'break', 27 | 'return', 28 | } 29 | ) 30 | -------------------------------------------------------------------------------- /workshop/formats/lua/quote_string.lua: -------------------------------------------------------------------------------- 1 | local quote_escaped = request('quote_string.linear') 2 | local quote_long = request('quote_string.intact') 3 | local quote_dump = request('quote_string.dump') 4 | 5 | local content_funcs = request('!.string.content_attributes') 6 | local has_control_chars = content_funcs.has_control_chars 7 | local has_backslashes = content_funcs.has_backslashes 8 | local has_single_quotes = content_funcs.has_single_quotes 9 | local has_double_quotes = content_funcs.has_double_quotes 10 | local is_nonascii = content_funcs.is_nonascii 11 | 12 | local binary_entities_lengths = 13 | { 14 | [1] = true, 15 | [2] = true, 16 | [4] = true, 17 | [6] = true, 18 | [8] = true, 19 | [10] = true, 20 | [16] = true, 21 | } 22 | 23 | return 24 | function(s) 25 | assert_string(s) 26 | local quote_func 27 | if binary_entities_lengths[#s] and is_nonascii(s) then 28 | quote_func = quote_dump 29 | elseif has_control_chars(s) then 30 | quote_func = quote_escaped 31 | elseif 32 | has_backslashes(s) or 33 | ( 34 | has_single_quotes(s) and 35 | has_double_quotes(s) 36 | ) 37 | then 38 | quote_func = quote_long 39 | else 40 | quote_func = quote_escaped 41 | end 42 | local result = quote_func(s) 43 | return result 44 | end 45 | -------------------------------------------------------------------------------- /workshop/formats/lua/quote_string/custom_quotes.lua: -------------------------------------------------------------------------------- 1 | return 2 | { 3 | ['\x07'] = [[\a]], 4 | ['\x08'] = [[\b]], 5 | ['\x09'] = [[\t]], 6 | ['\x0a'] = [[\n]], 7 | ['\x0b'] = [[\v]], 8 | ['\x0c'] = [[\f]], 9 | ['\x0d'] = [[\r]], 10 | ['"'] = [[\"]], 11 | ["'"] = [[\']], 12 | ['\\'] = [[\\]], 13 | } 14 | -------------------------------------------------------------------------------- /workshop/formats/lua/quote_string/dump.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Quote given string as by substituting all characters to their 3 | hex values. 4 | 5 | Handy for representing binary numbers: 6 | '\xB2\x7F\x02\xEE' has more sense than '²\x7F\x02î' 7 | ]] 8 | 9 | local quote_char = request('quote_char') 10 | 11 | return 12 | function(s) 13 | assert_string(s) 14 | return "'" .. s:gsub('.', quote_char) .. "'" 15 | end 16 | -------------------------------------------------------------------------------- /workshop/formats/lua/quote_string/intact.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(s) 3 | assert_string(s) 4 | 5 | local min_needed_quotes = 0 6 | 7 | if (s:sub(-1) == ']') then 8 | -- case "abc]". We do not want "[[abc]]]" 9 | min_needed_quotes = 1 10 | end 11 | 12 | local postfix, eq_chunk 13 | while true do 14 | eq_chunk = ('='):rep(min_needed_quotes) 15 | postfix = ']' .. eq_chunk .. ']' 16 | if not s:find(postfix, 1, true) then 17 | break 18 | end 19 | min_needed_quotes = min_needed_quotes + 1 20 | end 21 | 22 | local prefix = '[' .. eq_chunk .. '[' 23 | 24 | -- Handling special case: heading newline dropped in long strings. 25 | if 26 | (s:sub(1, 2) == '\x0d\x0a') or 27 | (s:sub(1, 1) == '\x0a') 28 | then 29 | prefix = prefix .. '\n' 30 | end 31 | 32 | return prefix .. s .. postfix 33 | end 34 | -------------------------------------------------------------------------------- /workshop/formats/lua/quote_string/linear.lua: -------------------------------------------------------------------------------- 1 | local quote_char = request('quote_char') 2 | local custom_quotes = request('custom_quotes') 3 | 4 | --[[ 5 | [1] 6 | I do not want to remember mapping (to understand 7 | that "\f" in output means ASCII code 0x0C. Also I do not like when 8 | "\" maps to "\\", I prefer "\x5c". Without using 9 | table you get longer but easier to understand data representation. 10 | ]] 11 | return 12 | function(s) 13 | local result = s 14 | --(1) 15 | -- result = result:gsub([[\]], custom_quotes['\\']) 16 | result = result:gsub([[\]], quote_char) 17 | -- result = result:gsub('[%c]', custom_quotes) 18 | result = result:gsub('[%c]', quote_char) 19 | --/ 20 | 21 | local cnt_q1 = 0 22 | for i in result:gmatch("'") do 23 | cnt_q1 = cnt_q1 + 1 24 | end 25 | local cnt_q2 = 0 26 | for i in result:gmatch('"') do 27 | cnt_q2 = cnt_q2 + 1 28 | end 29 | if (cnt_q1 <= cnt_q2) then 30 | result = "'" .. result:gsub("'", custom_quotes["'"]) .. "'" 31 | else 32 | result = '"' .. result:gsub('"', custom_quotes['"']) .. '"' 33 | end 34 | return result 35 | end 36 | -------------------------------------------------------------------------------- /workshop/formats/lua/quote_string/quote_char.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(c) 3 | --this is going to be called as gsub()'s function, so no arg checks 4 | return ([[\x%02X]]):format(c:byte(1, 1)) 5 | end 6 | -------------------------------------------------------------------------------- /workshop/formats/lua/run_formatter.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Serialize Lua code AST to string with actual Lua code. 3 | 4 | It receives table with parsed syntax and returns string with 5 | formatted code. 6 | 7 | table -> str 8 | ]] 9 | 10 | local c_formatter = request('formatter.interface') 11 | 12 | return 13 | function(data_struc, options) 14 | local formatter = new(c_formatter, options) 15 | formatter.data_struc = data_struc 16 | formatter:init() 17 | return formatter:run() 18 | end 19 | -------------------------------------------------------------------------------- /workshop/formats/lua/syntax.lua: -------------------------------------------------------------------------------- 1 | -- These are core cycle nodes. Loaded separately to avoid cycles in require() 2 | 3 | local expression = request('syntax.expression') 4 | local statements = request('syntax.statements') 5 | 6 | local producers, consumers = {}, {} 7 | 8 | local link = request('!.mechs.processor.link') 9 | link(expression, producers, consumers) 10 | link(statements, producers, consumers) 11 | assert(not next(consumers)) 12 | 13 | local optimize = request('!.mechs.processor.optimize') 14 | optimize(statements) 15 | 16 | return statements 17 | -------------------------------------------------------------------------------- /workshop/formats/lua/syntax/expression.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | : 4 | 5 | ---+--------------+---+- ... -------------+---+----------------------------+--- 6 | | ------------ | +- -----------+ | -------------------------- | 7 | | V | | +- --------+ | V | | 8 | +-- ---+ +- --------+ +-- ---+ 9 | +- --------+ 10 | +- ---------+ 11 | +- ------+ 12 | +- ---+ 13 | ]] 14 | 15 | local handy = request('!.mechs.processor.handy') 16 | local opt_rep = handy.opt_rep 17 | local cho = handy.cho 18 | local match_regexp = request('!.mechs.parser.handy').match_regexp 19 | 20 | local word = request('words.word') 21 | local opt_spc = request('words.opt_spc') 22 | 23 | local un_op = 24 | { 25 | name = 'un_op', 26 | cho( 27 | {opt_spc, match_regexp('[%-%#%~]')}, 28 | word('not') 29 | ), 30 | } 31 | 32 | --[[ 33 | Order 34 | 35 | must be before because is a prefix of : 36 | 37 | 38 | 39 | // / 40 | ~= ~ 41 | << < 42 | <= < 43 | >> > 44 | >= > 45 | ]] 46 | 47 | local bin_op = 48 | { 49 | name = 'bin_op', 50 | opt_spc, 51 | cho( 52 | '//', 53 | '==', 54 | '~=', 55 | '<<', 56 | '<=', 57 | '>>', 58 | '>=', 59 | '..', 60 | match_regexp('[%+%-%*%/%^%%%&%~%|%<%>]'), 61 | word('and'), 62 | word('or') 63 | ), 64 | } 65 | 66 | local vararg = request('words.vararg') 67 | local type_nil = request('type_nil') 68 | local type_boolean = request('type_boolean') 69 | local type_number = request('type_number') 70 | local type_string = request('type_string') 71 | local type_table = request('type_table') 72 | local type_function = request('type_function') 73 | local var_or_call = request('qualifiers.var_or_call') 74 | 75 | return 76 | { 77 | name = 'expression', 78 | inner_name = 'expression', 79 | opt_rep(un_op), 80 | cho( 81 | vararg, 82 | type_nil, 83 | type_boolean, 84 | type_number, 85 | type_string, 86 | type_table, 87 | type_function, 88 | var_or_call 89 | ), 90 | opt_rep( 91 | bin_op, 92 | '>expression' 93 | ) 94 | } 95 | -------------------------------------------------------------------------------- /workshop/formats/lua/syntax/qualifiers/var_or_call.lua: -------------------------------------------------------------------------------- 1 | local handy = request('!.mechs.processor.handy') 2 | local cho = handy.cho 3 | local opt = handy.opt 4 | local rep = handy.rep 5 | local opt_rep = handy.opt_rep 6 | 7 | local name = request('^.words.name') 8 | local syntel = request('^.words.syntel') 9 | 10 | local par_expr = request('^.wrappers.par_expr') 11 | local bracket_expr = request('^.wrappers.bracket_expr') 12 | local expr_list = request('^.wrappers.expr_list') 13 | local dot_name = request('^.wrappers.dot_name') 14 | local colon_name = request('^.wrappers.colon_name') 15 | 16 | local type_string = request('^.type_string') 17 | local type_table = request('^.type_table') 18 | 19 | local func_args = 20 | { 21 | name = 'func_args', 22 | cho( 23 | {syntel('('), opt(expr_list), syntel(')')}, 24 | type_string, 25 | type_table 26 | ), 27 | } 28 | 29 | return 30 | { 31 | name = 'var_or_call', 32 | cho(name, par_expr), 33 | opt_rep( 34 | cho( 35 | bracket_expr, 36 | dot_name, 37 | {opt(colon_name), func_args} 38 | ) 39 | ), 40 | } 41 | -------------------------------------------------------------------------------- /workshop/formats/lua/syntax/statements.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | : 4 | 5 | ---+-------------------------------+---+----------------------+---+-------------+--- 6 | | +---------------------------- | +- -+ +- -+ 7 | | V / | 8 | +---+- ----+---+ 9 | +- ----+ 10 | +- ---+ 11 | +- ---+ 12 | +- ----+ 13 | +- ---+ 14 | +- ---+ 15 | +- ----------+ 16 | +- ----------+ 17 | +- -------+ 18 | +- -----+ 19 | +- -+ 20 | +- -+ 21 | ]] 22 | 23 | local handy = request('!.mechs.processor.handy') 24 | local opt = handy.opt 25 | local opt_rep = handy.opt_rep 26 | local cho = handy.cho 27 | 28 | local assign_or_call = request('statements.assign_or_call') 29 | local named_function = request('statements.named_function') 30 | 31 | local empty_statement = request('statements.empty_statement') 32 | local label_statement = request('statements.label_statement') 33 | local goto_statement = request('statements.goto_statement') 34 | local local_statement = request('statements.local_statement') 35 | local break_statement = request('statements.break_statement') 36 | 37 | local do_block = request('statements.do_block') 38 | local if_block = request('statements.if_block') 39 | local while_block = request('statements.while_block') 40 | local repeat_block = request('statements.repeat_block') 41 | local generic_for_block = request('statements.generic_for_block') 42 | local numeric_for_block = request('statements.numeric_for_block') 43 | 44 | local return_statement = request('statements.return_statement') 45 | local opt_spc = request('words.opt_spc') 46 | 47 | return 48 | { 49 | name = 'statements', 50 | inner_name = 'statements', 51 | opt_rep( 52 | cho( 53 | assign_or_call, 54 | named_function, 55 | 56 | empty_statement, 57 | label_statement, 58 | goto_statement, 59 | local_statement, 60 | break_statement, 61 | 62 | do_block, 63 | if_block, 64 | while_block, 65 | repeat_block, 66 | generic_for_block, 67 | numeric_for_block 68 | ) 69 | ), 70 | opt(return_statement), 71 | opt_spc, 72 | } 73 | 74 | --[[ 75 | 2013-07-08 76 | 2013-07-09 77 | 2013-07-10 78 | 2013-07-15 79 | 2013-12-23 80 | 2016-07-26 81 | 2016-07-27 82 | 2016-08-03 83 | 2016-08-04 84 | 2016-08-16 85 | 2016-09-21 86 | 2017-08-28 87 | 2018-01-23 88 | 2018-02-07 89 | ]] 90 | -------------------------------------------------------------------------------- /workshop/formats/lua/syntax/statements/assign_or_call.lua: -------------------------------------------------------------------------------- 1 | local handy = request('!.mechs.processor.handy') 2 | local opt = handy.opt 3 | local opt_rep = handy.opt_rep 4 | 5 | local syntel = request('^.words.syntel') 6 | local expr_list = request('^.wrappers.expr_list') 7 | local var_or_call = request('^.qualifiers.var_or_call') 8 | 9 | return 10 | { 11 | name = 'assign_or_call', 12 | var_or_call, 13 | opt( 14 | opt_rep(syntel(','), var_or_call), 15 | syntel('='), 16 | expr_list 17 | ), 18 | } 19 | -------------------------------------------------------------------------------- /workshop/formats/lua/syntax/statements/break_statement.lua: -------------------------------------------------------------------------------- 1 | local word = request('^.words.word') 2 | 3 | return 4 | { 5 | name = 'break_statement', 6 | word('break'), 7 | } 8 | -------------------------------------------------------------------------------- /workshop/formats/lua/syntax/statements/do_block.lua: -------------------------------------------------------------------------------- 1 | local word = request('^.words.word') 2 | 3 | return 4 | { 5 | name = 'do_block', 6 | word('do'), 7 | '>statements', 8 | word('end'), 9 | } 10 | -------------------------------------------------------------------------------- /workshop/formats/lua/syntax/statements/empty_statement.lua: -------------------------------------------------------------------------------- 1 | local syntel = request('^.words.syntel') 2 | 3 | return 4 | { 5 | name = 'empty_statement', 6 | syntel(';'), 7 | } 8 | -------------------------------------------------------------------------------- /workshop/formats/lua/syntax/statements/function_body.lua: -------------------------------------------------------------------------------- 1 | local handy = request('!.mechs.processor.handy') 2 | local opt = handy.opt 3 | local cho = handy.cho 4 | 5 | local vararg = request('^.words.vararg') 6 | local name_list = request('^.wrappers.name_list') 7 | local word = request('^.words.word') 8 | local syntel = request('^.words.syntel') 9 | 10 | local params_list = 11 | cho( 12 | vararg, 13 | { 14 | name_list, 15 | opt( 16 | syntel(','), 17 | vararg 18 | ), 19 | } 20 | ) 21 | 22 | return 23 | { 24 | { 25 | name = 'function_params', 26 | syntel('('), 27 | opt(params_list), 28 | syntel(')'), 29 | }, 30 | { 31 | '>statements', 32 | word('end'), 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /workshop/formats/lua/syntax/statements/generic_for_block.lua: -------------------------------------------------------------------------------- 1 | local word = request('^.words.word') 2 | local name_list = request('^.wrappers.name_list') 3 | local expr_list = request('^.wrappers.expr_list') 4 | 5 | return 6 | { 7 | name = 'generic_for_block', 8 | word('for'), 9 | name_list, 10 | word('in'), 11 | expr_list, 12 | word('do'), 13 | '>statements', 14 | word('end'), 15 | } 16 | -------------------------------------------------------------------------------- /workshop/formats/lua/syntax/statements/goto_statement.lua: -------------------------------------------------------------------------------- 1 | local word = request('^.words.word') 2 | local name = request('^.words.name') 3 | 4 | return 5 | { 6 | name = 'goto_statement', 7 | word('goto'), 8 | name, 9 | } 10 | -------------------------------------------------------------------------------- /workshop/formats/lua/syntax/statements/if_block.lua: -------------------------------------------------------------------------------- 1 | local handy = request('!.mechs.processor.handy') 2 | local opt = handy.opt 3 | local opt_rep = handy.opt_rep 4 | 5 | local word = request('^.words.word') 6 | 7 | return 8 | { 9 | name = 'if_block', 10 | { 11 | name = 'if_part', 12 | word('if'), 13 | '>expression', 14 | word('then'), 15 | '>statements', 16 | }, 17 | opt_rep( 18 | { 19 | name = 'elseif_part', 20 | word('elseif'), 21 | '>expression', 22 | word('then'), 23 | '>statements', 24 | } 25 | ), 26 | opt( 27 | { 28 | name = 'else_part', 29 | word('else'), 30 | '>statements', 31 | } 32 | ), 33 | word('end'), 34 | } 35 | -------------------------------------------------------------------------------- /workshop/formats/lua/syntax/statements/label_statement.lua: -------------------------------------------------------------------------------- 1 | local name = request('^.words.name') 2 | local syntel = request('^.words.syntel') 3 | 4 | return 5 | { 6 | name = 'label_statement', 7 | syntel('::'), 8 | name, 9 | syntel('::'), 10 | } 11 | -------------------------------------------------------------------------------- /workshop/formats/lua/syntax/statements/local_statement.lua: -------------------------------------------------------------------------------- 1 | local handy = request('!.mechs.processor.handy') 2 | local opt = handy.opt 3 | local cho = handy.cho 4 | 5 | local word = request('^.words.word') 6 | local syntel = request('^.words.syntel') 7 | local name_list = request('^.wrappers.name_list') 8 | local expr_list = request('^.wrappers.expr_list') 9 | local named_function = request('named_function') 10 | 11 | return 12 | { 13 | name = 'local_statement', 14 | word('local'), 15 | cho( 16 | { 17 | name = 'local_assignment', 18 | name_list, 19 | opt( 20 | syntel('='), 21 | expr_list 22 | ) 23 | }, 24 | named_function 25 | ), 26 | } 27 | -------------------------------------------------------------------------------- /workshop/formats/lua/syntax/statements/named_function.lua: -------------------------------------------------------------------------------- 1 | local handy = request('!.mechs.processor.handy') 2 | local list = handy.list 3 | local opt = handy.opt 4 | 5 | local word = request('^.words.word') 6 | local syntel = request('^.words.syntel') 7 | local name = request('^.words.name') 8 | local colon_name = request('^.wrappers.colon_name') 9 | local function_body = request('function_body') 10 | 11 | return 12 | { 13 | name = 'named_function', 14 | word('function'), 15 | { 16 | name = 'dot_list', 17 | list(name, syntel('.')), 18 | }, 19 | opt(colon_name), 20 | function_body, 21 | } 22 | -------------------------------------------------------------------------------- /workshop/formats/lua/syntax/statements/numeric_for_block.lua: -------------------------------------------------------------------------------- 1 | local handy = request('!.mechs.processor.handy') 2 | local opt = handy.opt 3 | 4 | local word = request('^.words.word') 5 | local syntel = request('^.words.syntel') 6 | local name = request('^.words.name') 7 | 8 | return 9 | { 10 | name = 'numeric_for_block', 11 | word('for'), 12 | name, 13 | syntel('='), 14 | '>expression', 15 | syntel(','), 16 | '>expression', 17 | opt( 18 | syntel(','), 19 | '>expression' 20 | ), 21 | word('do'), 22 | '>statements', 23 | word('end'), 24 | } 25 | -------------------------------------------------------------------------------- /workshop/formats/lua/syntax/statements/repeat_block.lua: -------------------------------------------------------------------------------- 1 | local handy = request('!.mechs.processor.handy') 2 | 3 | local word = request('^.words.word') 4 | 5 | return 6 | { 7 | name = 'repeat_block', 8 | word('repeat'), 9 | '>statements', 10 | word('until'), 11 | '>expression', 12 | } 13 | -------------------------------------------------------------------------------- /workshop/formats/lua/syntax/statements/return_statement.lua: -------------------------------------------------------------------------------- 1 | local handy = request('!.mechs.processor.handy') 2 | local opt = handy.opt 3 | 4 | local word = request('^.words.word') 5 | local expr_list = request('^.wrappers.expr_list') 6 | local empty_statement = request('empty_statement') 7 | 8 | return 9 | { 10 | name = 'return_statement', 11 | word('return'), 12 | opt(expr_list), 13 | opt(empty_statement), 14 | } 15 | -------------------------------------------------------------------------------- /workshop/formats/lua/syntax/statements/while_block.lua: -------------------------------------------------------------------------------- 1 | local handy = request('!.mechs.processor.handy') 2 | 3 | local word = request('^.words.word') 4 | 5 | return 6 | { 7 | name = 'while_block', 8 | word('while'), 9 | '>expression', 10 | word('do'), 11 | '>statements', 12 | word('end'), 13 | } 14 | -------------------------------------------------------------------------------- /workshop/formats/lua/syntax/type_boolean.lua: -------------------------------------------------------------------------------- 1 | local handy = request('!.mechs.processor.handy') 2 | local cho = handy.cho 3 | 4 | local word = request('words.word') 5 | 6 | return 7 | { 8 | name = 'boolean', 9 | cho(word('false'), word('true')), 10 | } 11 | -------------------------------------------------------------------------------- /workshop/formats/lua/syntax/type_function.lua: -------------------------------------------------------------------------------- 1 | local handy = request('!.mechs.processor.handy') 2 | 3 | local word = request('words.word') 4 | local function_body = request('statements.function_body') 5 | 6 | return 7 | { 8 | name = 'type_function', 9 | word('function'), 10 | function_body, 11 | } 12 | -------------------------------------------------------------------------------- /workshop/formats/lua/syntax/type_nil.lua: -------------------------------------------------------------------------------- 1 | local word = request('words.word') 2 | 3 | return 4 | { 5 | name = 'nil', 6 | word('nil'), 7 | } 8 | -------------------------------------------------------------------------------- /workshop/formats/lua/syntax/type_number.lua: -------------------------------------------------------------------------------- 1 | local handy = request('!.mechs.processor.handy') 2 | local opt = handy.opt 3 | local cho = handy.cho 4 | local match_regexp = request('!.mechs.parser.handy').match_regexp 5 | 6 | local opt_spc = request('words.opt_spc') 7 | 8 | local int_10 = match_regexp('%d+') 9 | local dec_number = 10 | { 11 | cho( 12 | {'.', int_10}, 13 | {int_10, opt('.', opt(int_10))} 14 | ), 15 | opt(match_regexp('[eE][%+%-]?%d+')) 16 | } 17 | 18 | local int_16 = match_regexp('%x+') 19 | local hex_number = 20 | { 21 | match_regexp('0[xX]'), 22 | cho( 23 | {'.', int_16}, 24 | {int_16, opt('.', opt(int_16))} 25 | ), 26 | opt(match_regexp('[pP][%+%-]?%d+')) 27 | } 28 | 29 | --[[ 30 | Order 31 | 32 | must be checked first. Or it's "0" from prefix "0x" 33 | will be treated as decimal "0". 34 | ]] 35 | 36 | return 37 | { 38 | name = 'number', 39 | opt_spc, 40 | cho( 41 | hex_number, 42 | dec_number 43 | ) 44 | } 45 | -------------------------------------------------------------------------------- /workshop/formats/lua/syntax/type_string.lua: -------------------------------------------------------------------------------- 1 | local handy = request('!.mechs.processor.handy') 2 | local opt_rep = handy.opt_rep 3 | local cho = handy.cho 4 | local match_regexp = request('!.mechs.parser.handy').match_regexp 5 | 6 | local opt_spc = request('words.opt_spc') 7 | 8 | local long_bracket = request('words.particles.long_bracket') 9 | 10 | return 11 | { 12 | name = 'string', 13 | opt_spc, 14 | cho( 15 | long_bracket, 16 | { 17 | '"', 18 | opt_rep( 19 | cho( 20 | match_regexp([[[^%\%"]+]]), 21 | match_regexp([[%\.]]) 22 | ) 23 | ), 24 | '"' 25 | }, 26 | { 27 | "'", 28 | opt_rep( 29 | cho( 30 | match_regexp([[[^%\%']+]]), 31 | match_regexp([[%\.]]) 32 | ) 33 | ), 34 | "'" 35 | } 36 | ) 37 | } 38 | -------------------------------------------------------------------------------- /workshop/formats/lua/syntax/type_table.lua: -------------------------------------------------------------------------------- 1 | local handy = request('!.mechs.processor.handy') 2 | local opt = handy.opt 3 | local cho = handy.cho 4 | local list = handy.list 5 | 6 | local name = request('words.name') 7 | local bracket_expr = request('wrappers.bracket_expr') 8 | local syntel = request('words.syntel') 9 | 10 | local key_val = 11 | { 12 | name = 'key_val', 13 | opt( 14 | cho( 15 | bracket_expr, 16 | name 17 | ), 18 | syntel('=') 19 | ), 20 | '>expression', 21 | } 22 | 23 | local rec_sep = 24 | { 25 | cho(syntel(','), syntel(';')) 26 | } 27 | 28 | return 29 | { 30 | name = 'table', 31 | syntel('{'), 32 | opt( 33 | list(key_val, rec_sep), 34 | opt(rec_sep) 35 | ), 36 | syntel('}'), 37 | } 38 | -------------------------------------------------------------------------------- /workshop/formats/lua/syntax/words/comment.lua: -------------------------------------------------------------------------------- 1 | local handy = request('!.mechs.processor.handy') 2 | local cho = handy.cho 3 | local match_regexp = request('!.mechs.parser.handy').match_regexp 4 | 5 | local long_bracket = request('particles.long_bracket') 6 | 7 | --[[ 8 | Comment grammar is part of . So no spaces before "--". 9 | ]] 10 | 11 | return 12 | { 13 | name = 'comment', 14 | '--', 15 | cho( 16 | long_bracket, 17 | match_regexp('[^\n\r]*[\n\r]?') 18 | ) 19 | } 20 | -------------------------------------------------------------------------------- /workshop/formats/lua/syntax/words/name.lua: -------------------------------------------------------------------------------- 1 | local match_regexp = request('!.mechs.parser.handy').match_regexp 2 | local opt_spc = request('opt_spc') 3 | 4 | local keywords = request('!.formats.lua.keywords') 5 | local name_pattern = '^[_A-Za-z][_A-Za-z0-9]*' 6 | 7 | local is_name = 8 | function(in_stream, out_stream) 9 | local init_pos = in_stream:get_position() 10 | if in_stream:match_regexp(name_pattern) then 11 | local s = in_stream:get_segment(init_pos, in_stream:get_position() - init_pos) 12 | if keywords[s] then 13 | in_stream:set_position(init_pos) 14 | return false 15 | else 16 | return true 17 | end 18 | end 19 | end 20 | 21 | return 22 | { 23 | name = 'name', 24 | opt_spc, is_name, 25 | } 26 | -------------------------------------------------------------------------------- /workshop/formats/lua/syntax/words/opt_spc.lua: -------------------------------------------------------------------------------- 1 | local handy = request('!.mechs.processor.handy') 2 | local opt_rep = handy.opt_rep 3 | local cho = handy.cho 4 | local match_regexp = request('!.mechs.parser.handy').match_regexp 5 | 6 | local comment = request('comment') 7 | 8 | return 9 | { 10 | name = 'whitespace', 11 | opt_rep( 12 | cho( 13 | match_regexp('[ \t\n\r]+'), 14 | comment 15 | ) 16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /workshop/formats/lua/syntax/words/particles/long_bracket.lua: -------------------------------------------------------------------------------- 1 | local match_regexp = request('!.mechs.parser.handy').match_regexp 2 | 3 | --[[ 4 | In human words: idea is to capture "="'s that may be present between 5 | "[[" in opening quote. And then use this capture in closing quote. 6 | 7 | "%[(%=*)%[" means "[" "]" 10 | ]] 11 | return match_regexp('%[(%=*)%[.-%]%1%]') 12 | -------------------------------------------------------------------------------- /workshop/formats/lua/syntax/words/syntel.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Shortcut structure to prefix punctiation characters with optional 3 | spaces. 4 | 5 | So instead of "opt_spc, ','" you may write "syntel(',')". 6 | ]] 7 | 8 | local opt_spc = request('opt_spc') 9 | 10 | return 11 | function(str) 12 | return {opt_spc, str} 13 | end 14 | -------------------------------------------------------------------------------- /workshop/formats/lua/syntax/words/vararg.lua: -------------------------------------------------------------------------------- 1 | local syntel = request('^.words.syntel') 2 | 3 | return 4 | { 5 | name = 'vararg', 6 | syntel('...'), 7 | } 8 | -------------------------------------------------------------------------------- /workshop/formats/lua/syntax/words/word.lua: -------------------------------------------------------------------------------- 1 | local opt_spc = request('opt_spc') 2 | local match_regexp = request('!.mechs.parser.handy').match_regexp 3 | 4 | local not_name_body_letter = '%f[^_A-Za-z0-9]' 5 | 6 | return 7 | function(str) 8 | return 9 | { 10 | opt_spc, 11 | match_regexp(str .. not_name_body_letter), 12 | } 13 | end 14 | -------------------------------------------------------------------------------- /workshop/formats/lua/syntax/wrappers/bracket_expr.lua: -------------------------------------------------------------------------------- 1 | local syntel = request('^.words.syntel') 2 | 3 | return 4 | { 5 | name = 'bracket_expr', 6 | syntel('['), 7 | '>expression', 8 | syntel(']'), 9 | } 10 | -------------------------------------------------------------------------------- /workshop/formats/lua/syntax/wrappers/colon_name.lua: -------------------------------------------------------------------------------- 1 | local syntel = request('^.words.syntel') 2 | local name = request('^.words.name') 3 | 4 | return 5 | { 6 | name = 'colon_name', 7 | syntel(':'), 8 | name, 9 | } 10 | -------------------------------------------------------------------------------- /workshop/formats/lua/syntax/wrappers/dot_name.lua: -------------------------------------------------------------------------------- 1 | local syntel = request('^.words.syntel') 2 | local name = request('^.words.name') 3 | 4 | return 5 | { 6 | name = 'dot_name', 7 | syntel('.'), 8 | name, 9 | } 10 | -------------------------------------------------------------------------------- /workshop/formats/lua/syntax/wrappers/expr_list.lua: -------------------------------------------------------------------------------- 1 | local handy = request('!.mechs.processor.handy') 2 | local list = handy.list 3 | 4 | local syntel = request('^.words.syntel') 5 | 6 | return 7 | { 8 | name = 'expr_list', 9 | list('>expression', syntel(',')), 10 | } 11 | -------------------------------------------------------------------------------- /workshop/formats/lua/syntax/wrappers/name_list.lua: -------------------------------------------------------------------------------- 1 | local handy = request('!.mechs.processor.handy') 2 | local list = handy.list 3 | 4 | local name = request('^.words.name') 5 | local syntel = request('^.words.syntel') 6 | 7 | return 8 | { 9 | name = 'name_list', 10 | list(name, syntel(',')) 11 | } 12 | -------------------------------------------------------------------------------- /workshop/formats/lua/syntax/wrappers/par_expr.lua: -------------------------------------------------------------------------------- 1 | local syntel = request('^.words.syntel') 2 | 3 | return 4 | { 5 | name = 'par_expr', 6 | syntel('('), 7 | '>expression', 8 | syntel(')'), 9 | } 10 | -------------------------------------------------------------------------------- /workshop/formats/lua/transform_ast.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | This is AST transformer. 3 | 4 | It reformulates structure produced by parser to something simpler 5 | to manage. 6 | 7 | For example numeric "for" loop represented as sequence 8 | 9 | "for", , "=", , ",", , [",", ], "do", ... 10 | 11 | It transformed to specific record: 12 | 13 | : 14 | index = , 15 | start_val = , 16 | end_val = , 17 | increment = , 18 | ... 19 | ]] 20 | 21 | local c_transformer = request('ast_transformer.interface') 22 | 23 | return 24 | function(data_struc, options) 25 | local transformer = new(c_transformer, options) 26 | transformer.data_struc = data_struc 27 | return transformer:run() 28 | end 29 | -------------------------------------------------------------------------------- /workshop/formats/lua_table/save.lua: -------------------------------------------------------------------------------- 1 | -- Serialize lua table as string with lua table definition. 2 | 3 | -- Not suitable for tables with cross-links in keys or values. 4 | 5 | local c_table_serializer = request('save.interface') 6 | local compile = request('!.struc.compile') 7 | 8 | return 9 | function(t, options) 10 | assert_table(t) 11 | local table_serializer = new(c_table_serializer, options) 12 | table_serializer:init() 13 | local ast = table_serializer:get_ast(t) 14 | local result = table_serializer:serialize_ast(ast) 15 | return result 16 | end 17 | -------------------------------------------------------------------------------- /workshop/formats/lua_table/save/get_ast.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self, data) 3 | local result 4 | local data_type = type(data) 5 | if (data_type == 'table') then 6 | if self.value_names[data] then 7 | result = 8 | { 9 | type = 'name', 10 | value = self.value_names[data], 11 | } 12 | else 13 | result = {} 14 | result.type = 'table' 15 | for key, value in self.table_iterator(data) do 16 | local key_slot = self:get_ast(key) 17 | local value_slot = self:get_ast(value) 18 | result[#result + 1] = 19 | { 20 | key = key_slot, 21 | value = value_slot, 22 | } 23 | end 24 | end 25 | else 26 | result = 27 | { 28 | type = data_type, 29 | value = data, 30 | } 31 | end 32 | return result 33 | end 34 | -------------------------------------------------------------------------------- /workshop/formats/lua_table/save/init.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self) 3 | self.text_block = new(self.c_text_block) 4 | self.text_block:init() 5 | self.install_node_handlers(self.node_handlers, self.text_block) 6 | end 7 | -------------------------------------------------------------------------------- /workshop/formats/lua_table/save/install_node_handlers/minimal.lua: -------------------------------------------------------------------------------- 1 | local text_block 2 | 3 | local add = 4 | function(s) 5 | text_block:add_curline(s) 6 | end 7 | 8 | local node_handlers = {} 9 | 10 | local raw_compile = request('!.struc.compile') 11 | 12 | local compile = 13 | function(t) 14 | add(raw_compile(t, node_handlers)) 15 | end 16 | 17 | local is_identifier = request('!.formats.lua.is_identifier') 18 | local compact_sequences = true 19 | 20 | node_handlers.table = 21 | function(node) 22 | if (#node == 0) then 23 | add('{}') 24 | return 25 | end 26 | local last_integer_key = 0 27 | add('{') 28 | for i = 1, #node do 29 | if (i > 1) then 30 | add(',') 31 | end 32 | local key, value = node[i].key, node[i].value 33 | -- skip key part? 34 | if 35 | compact_sequences and 36 | (key.type == 'number') and 37 | is_integer(key.value) and 38 | (key.value == last_integer_key + 1) 39 | then 40 | last_integer_key = key.value 41 | else 42 | -- may mention key without brackets? 43 | if 44 | (key.type == 'string') and 45 | is_identifier(key.value) 46 | then 47 | add(key.value) 48 | else 49 | add('[') 50 | compile(key) 51 | add(']') 52 | end 53 | add('=') 54 | end 55 | compile(value) 56 | end 57 | add('}') 58 | end 59 | 60 | do 61 | local serialize_tostring = 62 | function(node) 63 | add(tostring(node.value)) 64 | end 65 | 66 | local tostring_datatypes = {'number', 'boolean', 'nil'} 67 | 68 | for i = 1, #tostring_datatypes do 69 | node_handlers[tostring_datatypes[i]] = serialize_tostring 70 | end 71 | end 72 | 73 | do 74 | local quote = request('!.lua.string.quote') 75 | 76 | local serialize_quoted = 77 | function(node) 78 | local quoted_string = quote(tostring(node.value)) 79 | -- Quite ugly handling indexing [[[s]]] case: convert to [ [[s]]] 80 | if not text_block:on_clean_line() then 81 | local text_line = text_block.line_with_text:get_line() 82 | if 83 | (text_line:sub(-1) == '[') and 84 | (quoted_string:sub(1, 1) == '[') 85 | then 86 | add(' ') 87 | end 88 | end 89 | add(quoted_string) 90 | end 91 | 92 | local quoted_datatypes = {'string', 'function', 'thread', 'userdata'} 93 | 94 | for i = 1, #quoted_datatypes do 95 | node_handlers[quoted_datatypes[i]] = serialize_quoted 96 | end 97 | end 98 | 99 | node_handlers.name = 100 | function(node) 101 | compile(node.value) 102 | end 103 | 104 | local merge = request('!.table.merge') 105 | 106 | return 107 | function(a_node_handlers, a_text_block, options) 108 | node_handlers = merge(a_node_handlers, node_handlers) 109 | text_block = a_text_block 110 | if options and is_boolean(options.compact_sequences) then 111 | compact_sequences = options.compact_sequences 112 | end 113 | end 114 | -------------------------------------------------------------------------------- /workshop/formats/lua_table/save/install_node_handlers/readable.lua: -------------------------------------------------------------------------------- 1 | local text_block 2 | 3 | local add = 4 | function(s) 5 | text_block:add_curline(s) 6 | end 7 | 8 | local request_clean_line = 9 | function() 10 | text_block:request_clean_line() 11 | end 12 | 13 | local inc_indent = 14 | function() 15 | text_block:inc_indent() 16 | end 17 | 18 | local dec_indent = 19 | function() 20 | text_block:dec_indent() 21 | end 22 | 23 | local node_handlers = {} 24 | 25 | local raw_compile = request('!.struc.compile') 26 | 27 | local compile = 28 | function(t) 29 | add(raw_compile(t, node_handlers)) 30 | end 31 | 32 | local is_identifier = request('!.formats.lua.is_identifier') 33 | local compact_sequences = true 34 | 35 | node_handlers.table = 36 | function(node) 37 | if (#node == 0) then 38 | add('{}') 39 | return 40 | end 41 | local last_integer_key = 0 42 | add('{') 43 | inc_indent() 44 | for i = 1, #node do 45 | local key, value = node[i].key, node[i].value 46 | request_clean_line() 47 | if 48 | compact_sequences and 49 | (key.type == 'number') and 50 | is_integer(key.value) and 51 | (key.value == last_integer_key + 1) 52 | then 53 | last_integer_key = key.value 54 | else 55 | if 56 | (key.type == 'string') and 57 | is_identifier(key.value) 58 | then 59 | add(key.value) 60 | else 61 | add('[') 62 | compile(key) 63 | add(']') 64 | end 65 | add(' = ') 66 | end 67 | compile(value) 68 | add(',') 69 | end 70 | dec_indent() 71 | request_clean_line() 72 | add('}') 73 | end 74 | 75 | local merge = request('!.table.merge') 76 | local install_minimal_handlers = request('minimal') 77 | 78 | return 79 | function(a_node_handlers, a_text_block, options) 80 | install_minimal_handlers(a_node_handlers, a_text_block, options) 81 | node_handlers = merge(a_node_handlers, node_handlers) 82 | text_block = a_text_block 83 | if options and is_boolean(options.compact_sequences) then 84 | compact_sequences = options.compact_sequences 85 | end 86 | end 87 | -------------------------------------------------------------------------------- /workshop/formats/lua_table/save/interface.lua: -------------------------------------------------------------------------------- 1 | return 2 | { 3 | init = request('init'), 4 | 5 | get_ast = request('get_ast'), 6 | serialize_ast = request('serialize_ast'), 7 | 8 | node_handlers = {}, 9 | c_text_block = request('!.mechs.text_block.interface'), 10 | text_block = nil, 11 | value_names = {}, 12 | table_iterator = request('!.table.ordered_pass'), 13 | install_node_handlers = request('install_node_handlers.readable'), 14 | } 15 | -------------------------------------------------------------------------------- /workshop/formats/lua_table/save/serialize_ast.lua: -------------------------------------------------------------------------------- 1 | local compile = request('!.struc.compile') 2 | 3 | return 4 | function(self, ast) 5 | compile(ast, self.node_handlers) 6 | return self.text_block:get_text() 7 | end 8 | -------------------------------------------------------------------------------- /workshop/formats/lua_table_code/load.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(s) 3 | local f = load(s) 4 | local result 5 | if f then 6 | result = f() 7 | end 8 | return result 9 | end 10 | -------------------------------------------------------------------------------- /workshop/formats/lua_table_code/save.lua: -------------------------------------------------------------------------------- 1 | -- Serialize table to string with Lua code which recreates table 2 | 3 | local c_table_serializer = request('save.interface') 4 | 5 | return 6 | function(t, options) 7 | assert_table(t) 8 | local table_serializer = new(c_table_serializer, options) 9 | table_serializer:init() 10 | local ast = table_serializer:get_ast(t) 11 | local result = table_serializer:serialize_ast(ast) 12 | return result 13 | end 14 | -------------------------------------------------------------------------------- /workshop/formats/lua_table_code/save/get_ast.lua: -------------------------------------------------------------------------------- 1 | local get_num_refs = 2 | function(node_rec) 3 | local result = 0 4 | if node_rec.refs then 5 | local node = node_rec.node 6 | for parent, parent_keys in pairs(node_rec.refs) do 7 | if (parent == node) then 8 | result = result + 1 9 | end 10 | for key in pairs(parent_keys) do 11 | if (parent[key] == node) then 12 | result = result + 1 13 | end 14 | if (key == node) then 15 | result = result + 1 16 | end 17 | end 18 | end 19 | end 20 | return result 21 | end 22 | 23 | local may_print_inline = 24 | function(node_rec) 25 | return 26 | not node_rec or 27 | ( 28 | (get_num_refs(node_rec) <= 1) and 29 | not node_rec.part_of_cycle 30 | ) 31 | end 32 | 33 | local get_assembly_order = request('!.mechs.graph.assembly_order') 34 | local c_name_giver = request('!.mechs.name_giver') 35 | 36 | return 37 | function(self, data) 38 | local name_giver = new(c_name_giver) 39 | local table_serializer = self.table_serializer 40 | local table_iterator = table_serializer.table_iterator 41 | 42 | local node_recs, nodes_ordered = 43 | get_assembly_order( 44 | data, 45 | {also_visit_keys = true, table_iterator = table_iterator} 46 | ) 47 | 48 | local result = {} 49 | local processed_tables = {} 50 | 51 | for i = 1, #nodes_ordered do 52 | local node = nodes_ordered[i] 53 | local node_rec = node_recs[node] 54 | if 55 | not may_print_inline(node_rec) or 56 | (node == data) 57 | then 58 | local table_rec 59 | if node_rec.part_of_cycle then 60 | table_rec = {type = 'table'} 61 | for k, v in table_serializer.table_iterator(node) do 62 | local key_is_ok = not is_table(k) or processed_tables[k] 63 | local value_is_ok = not is_table(v) or processed_tables[v] 64 | if key_is_ok and value_is_ok then 65 | table_rec[#table_rec + 1] = 66 | { 67 | key = table_serializer:get_ast(k), 68 | value = table_serializer:get_ast(v), 69 | } 70 | end 71 | end 72 | else 73 | table_rec = table_serializer:get_ast(node) 74 | end 75 | local node_name = name_giver:give_name(node) 76 | result[#result + 1] = 77 | { 78 | type = 'local_definition', 79 | name = node_name, 80 | value = table_rec, 81 | } 82 | table_serializer.value_names[node] = node_name 83 | end 84 | processed_tables[node] = true 85 | 86 | if node_rec.part_of_cycle then 87 | -- Add links to a table we just processed: 88 | for parent, parent_keys in pairs(node_rec.refs) do 89 | if processed_tables[parent] then 90 | for parent_key in pairs(parent_keys) do 91 | local key_slot 92 | local key_name = table_serializer.value_names[parent_key] 93 | if key_name then 94 | key_slot = {type = 'name', value = key_name} 95 | else 96 | key_slot = {type = type(parent_key), value = parent_key} 97 | end 98 | result[#result + 1] = 99 | { 100 | type = 'assignment', 101 | name = table_serializer.value_names[parent], 102 | index = 103 | { 104 | type = 'index', 105 | value = key_slot, 106 | }, 107 | value = table_serializer.value_names[node], 108 | } 109 | end 110 | end 111 | end 112 | end 113 | end 114 | 115 | result[#result + 1] = 116 | { 117 | type = 'return_statement', 118 | value = table_serializer.value_names[data], 119 | } 120 | 121 | return result 122 | end 123 | -------------------------------------------------------------------------------- /workshop/formats/lua_table_code/save/init.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self) 3 | self.table_serializer = new(self.c_table_serializer) 4 | self.table_serializer:init() 5 | self.install_node_handlers( 6 | self.table_serializer.node_handlers, 7 | self.table_serializer.text_block 8 | ) 9 | end 10 | -------------------------------------------------------------------------------- /workshop/formats/lua_table_code/save/install_node_handlers.lua: -------------------------------------------------------------------------------- 1 | local text_block 2 | 3 | local node_handlers = {} 4 | 5 | local add = 6 | function(s) 7 | text_block:add_curline(s) 8 | end 9 | 10 | local request_clean_line = 11 | function() 12 | text_block:request_clean_line() 13 | end 14 | 15 | local raw_compile = request('!.struc.compile') 16 | 17 | local compile = 18 | function(t) 19 | add(raw_compile(t, node_handlers)) 20 | end 21 | 22 | node_handlers.local_definition = 23 | function(node) 24 | request_clean_line() 25 | add('local ') 26 | compile(node.name) 27 | add(' = ') 28 | compile(node.value) 29 | end 30 | 31 | local is_identifier = request('!.formats.lua.is_identifier') 32 | 33 | node_handlers.index = 34 | function(node) 35 | if 36 | (node.value.type == 'string') and 37 | is_identifier(node.value.value) 38 | then 39 | add('.') 40 | add(node.value.value) 41 | else 42 | add('[') 43 | compile(node.value) 44 | add(']') 45 | end 46 | end 47 | 48 | node_handlers.assignment = 49 | function(node) 50 | request_clean_line() 51 | add(node.name) 52 | compile(node.index) 53 | add(' = ') 54 | compile(node.value) 55 | end 56 | 57 | node_handlers.return_statement = 58 | function(node) 59 | request_clean_line() 60 | add('return ') 61 | compile(node.value) 62 | end 63 | 64 | local merge = request('!.table.merge') 65 | 66 | return 67 | function(a_node_handlers, a_text_block) 68 | node_handlers = merge(a_node_handlers, node_handlers) 69 | text_block = a_text_block 70 | end 71 | -------------------------------------------------------------------------------- /workshop/formats/lua_table_code/save/interface.lua: -------------------------------------------------------------------------------- 1 | return 2 | { 3 | init = request('init'), 4 | 5 | get_ast = request('get_ast'), 6 | serialize_ast = request('serialize_ast'), 7 | 8 | c_table_serializer = request('!.formats.lua_table.save.interface'), 9 | table_serializer = nil, 10 | install_node_handlers = request('install_node_handlers'), 11 | } 12 | -------------------------------------------------------------------------------- /workshop/formats/lua_table_code/save/serialize_ast.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self, ast) 3 | return self.table_serializer:serialize_ast(ast) 4 | end 5 | -------------------------------------------------------------------------------- /workshop/formats/sh/load.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Shebang script files splitter. 3 | 4 | Input: 5 | string with file data 6 | 7 | Output: 8 | {tool = or , data = } 9 | ]] 10 | 11 | local get_next_line = request('!.string.lines.get_next_line') 12 | 13 | return 14 | function(s) 15 | local result = 16 | { 17 | tool = nil, 18 | data = '', 19 | } 20 | 21 | local new_pos, line = get_next_line(s) 22 | if line and (line:sub(1, 2) == '#!') then 23 | result.tool = line:match('#!%s*(.*)') 24 | result.data = s:sub(new_pos) 25 | else 26 | result.data = s 27 | end 28 | 29 | return result 30 | end 31 | -------------------------------------------------------------------------------- /workshop/frontend/text/print_msg_with_delta_time.lua: -------------------------------------------------------------------------------- 1 | local represent_time = request('!.number.represent_time') 2 | 3 | local last_time 4 | 5 | return 6 | function(s) 7 | local cur_time = os.clock() 8 | if last_time then 9 | local time_passed = represent_time(cur_time - last_time) 10 | io.stdout:write((' [%s]\n'):format(time_passed)) 11 | end 12 | io.stdout:write(s) 13 | io.stdout:flush() 14 | last_time = cur_time 15 | end 16 | -------------------------------------------------------------------------------- /workshop/lua/code/ast_as_code.lua: -------------------------------------------------------------------------------- 1 | return request('!.formats.lua.run_formatter') 2 | -------------------------------------------------------------------------------- /workshop/lua/code/get_ast.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Get AST for string with Lua code. 3 | 4 | str -> table 5 | 6 | Handles possible "#!" at start of shell files. 7 | ]] 8 | 9 | local parse = request('!.mechs.generic_loader') 10 | local syntax = request('!.formats.lua.syntax') 11 | local parse_sh = request('!.formats.sh.load') 12 | 13 | return 14 | function(s) 15 | assert_string(s) 16 | 17 | local sh = parse_sh(s) 18 | local shebang_str 19 | if sh.tool then 20 | shebang_str = '#! ' .. sh.tool 21 | end 22 | 23 | local result, unparsed_tail = parse(sh.data, syntax) 24 | 25 | result.shebang_str = shebang_str 26 | result.unparsed_tail = unparsed_tail 27 | 28 | return result 29 | end 30 | -------------------------------------------------------------------------------- /workshop/lua/data_types.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Return table with string names of all Lua datatypes. 3 | 4 | Used in generating assert_ functions. 5 | 6 | -> table 7 | ]] 8 | 9 | return 10 | { 11 | 'nil', 12 | 'boolean', 13 | 'number', 14 | 'string', 15 | 'function', 16 | 'thread', 17 | 'userdata', 18 | 'table', 19 | } 20 | -------------------------------------------------------------------------------- /workshop/lua/string/quote.lua: -------------------------------------------------------------------------------- 1 | return request('!.formats.lua.quote_string') 2 | -------------------------------------------------------------------------------- /workshop/mechs/command_line_processor/assert_type_is_correct.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self, name, item_type) 3 | for _, param_rec in ipairs(self.allowed_params) do 4 | if (param_rec.name == name) then 5 | if (param_rec.type ~= item_type) then 6 | local msg = 7 | ('Actual type for parameter "%s" is "%s", not "%s".'): 8 | format(name, param_rec.type, item_type) 9 | error(msg) 10 | end 11 | return true 12 | end 13 | end 14 | error(('No record for parameter "%s".'):format(name)) 15 | end 16 | -------------------------------------------------------------------------------- /workshop/mechs/command_line_processor/classify_item.lua: -------------------------------------------------------------------------------- 1 | local flag_pattern = '^%-%-(~?)([%w%-]+)$' -- --~keep-comments 2 | local key_int_pattern = '^%-%-([%w%-]+)=(%d+)$' -- --right-margin=40 3 | local key_str_pattern = '^%-%-([%w%-]+)=(.+)$' -- --indent-chunk= | 4 | 5 | return 6 | function(self, item) 7 | assert_string(item) 8 | 9 | local is_not, key, value 10 | 11 | is_not, key = item:match(flag_pattern) 12 | if key then 13 | return 'flag', key, (is_not == '') 14 | end 15 | 16 | key, value = item:match(key_int_pattern) 17 | if key then 18 | value = tonumber(value) 19 | assert_integer(value) 20 | return 'key_int', key, value 21 | end 22 | 23 | key, value = item:match(key_str_pattern) 24 | if key then 25 | return 'key_str', key, value 26 | end 27 | 28 | return 'string', nil, item 29 | end 30 | -------------------------------------------------------------------------------- /workshop/mechs/command_line_processor/get_key_name.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self, idx) 3 | local num_found = 0 4 | for _, param_rec in ipairs(self.allowed_params) do 5 | if (param_rec.type == 'string') then 6 | num_found = num_found + 1 7 | if (num_found == idx) then 8 | return param_rec.name 9 | end 10 | end 11 | end 12 | local msg = 13 | ('Description for string parameter #%d not provided.'):format(idx) 14 | error(msg) 15 | end 16 | -------------------------------------------------------------------------------- /workshop/mechs/command_line_processor/interface.lua: -------------------------------------------------------------------------------- 1 | return 2 | { 3 | -- core 4 | allowed_params = 5 | { 6 | {name = 'f_in_name', type = 'string'}, 7 | {name = 'f_out_name', type = 'string'}, 8 | }, 9 | run = request('run'), 10 | -- implementation 11 | parse_args = request('parse_args'), 12 | classify_item = request('classify_item'), 13 | get_key_name = request('get_key_name'), 14 | assert_type_is_correct = request('assert_type_is_correct'), 15 | } 16 | -------------------------------------------------------------------------------- /workshop/mechs/command_line_processor/parse_args.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Parse command-line parameters placed in <.args>. 3 | Match their types with parameters from <.allowed_params>. 4 | Raise error on mismatch. 5 | Return key-value table. 6 | ]] 7 | 8 | return 9 | function(self, args) 10 | local result = {} 11 | local num_strings = 0 12 | 13 | for _, item in ipairs(args) do 14 | local item_type, key, value = self:classify_item(item) 15 | if (item_type == 'string') then 16 | num_strings = num_strings + 1 17 | key = self:get_key_name(num_strings) 18 | end 19 | self:assert_type_is_correct(key, item_type) 20 | result[key] = 21 | { 22 | type = item_type, 23 | value = value, 24 | } 25 | end 26 | 27 | return result 28 | end 29 | -------------------------------------------------------------------------------- /workshop/mechs/command_line_processor/run.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Process sequence with command-line arguments. 3 | ]] 4 | 5 | return 6 | function(self, args) 7 | local options = self:parse_args(args) 8 | --[[ 9 | is map of records {type: <>, value: <>}. 10 | should be map of values. 11 | ]] 12 | local result = {} 13 | for key, rec in pairs(options) do 14 | result[key] = rec.value 15 | end 16 | return result 17 | end 18 | -------------------------------------------------------------------------------- /workshop/mechs/generic_file_converter/compile.lua: -------------------------------------------------------------------------------- 1 | local table_to_str = request('!.formats.lua_table_code.save') 2 | 3 | return 4 | function(t) 5 | if is_table(t) then 6 | return table_to_str(t) 7 | else 8 | return t 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /workshop/mechs/generic_file_converter/init.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Pre-flight checks. 3 | Raises error if fails. 4 | ]] 5 | 6 | local exists = request('!.file.exists') 7 | local safe_open = request('!.file.safe_open') 8 | 9 | return 10 | function(self) 11 | local f_in_name = self.f_in_name 12 | local f_out_name = self.f_out_name 13 | 14 | assert_string(f_in_name) 15 | if not exists(f_in_name) then 16 | error(('File "%s" not found.'):format(f_in_name)) 17 | end 18 | 19 | assert_string(f_out_name) 20 | if (f_in_name == f_out_name) then 21 | error(('Input and output file names must differ.')) 22 | end 23 | 24 | -- Test that is writable: 25 | local is_ok, f_out = pcall(safe_open, f_out_name, 'w') 26 | if not is_ok then 27 | local msg = 28 | ([[Can't open output file "%s" for writing: "%s".]]): 29 | format(f_out_name, f_out) 30 | error(msg) 31 | end 32 | f_out:close() 33 | end 34 | -------------------------------------------------------------------------------- /workshop/mechs/generic_file_converter/interface.lua: -------------------------------------------------------------------------------- 1 | return 2 | { 3 | -- core 4 | f_in_name = '', 5 | f_out_name = '', 6 | init = request('init'), 7 | run = request('run'), 8 | -- adjustable 9 | load = request('!.file.text_file_as_string'), 10 | parse = request('!.formats.lua_table_code.load'), 11 | compile = request('compile'), 12 | save = request('!.string.save_to_file'), 13 | -- implementation 14 | say = request('say'), 15 | } 16 | -------------------------------------------------------------------------------- /workshop/mechs/generic_file_converter/run.lua: -------------------------------------------------------------------------------- 1 | local file_size = request('!.file.get_size') 2 | local represent_size = request('!.number.represent_size') 3 | 4 | return 5 | function(self) 6 | self:say( 7 | ('Loading "%s" [%s].'):format( 8 | self.f_in_name, 9 | represent_size(file_size(self.f_in_name)) 10 | ) 11 | ) 12 | local data = self.load(self.f_in_name) 13 | if data then 14 | self:say('Parsing.') 15 | local parse_result = self.parse(data) 16 | if parse_result then 17 | self:say('Compiling.') 18 | local compile_result = self.compile(parse_result) 19 | if compile_result then 20 | assert_string(compile_result) 21 | self:say( 22 | ('Saving "%s" [%s].'):format( 23 | self.f_out_name, 24 | represent_size(#compile_result) 25 | ) 26 | ) 27 | self.save(self.f_out_name, compile_result) 28 | else 29 | self:say('Compile failed.') 30 | end 31 | else 32 | self:say('Parse failed.') 33 | end 34 | end 35 | self:say('') 36 | end 37 | -------------------------------------------------------------------------------- /workshop/mechs/generic_file_converter/say.lua: -------------------------------------------------------------------------------- 1 | local print_msg_with_delta_time = request('!.frontend.text.print_msg_with_delta_time') 2 | 3 | return 4 | function(self, s) 5 | print_msg_with_delta_time(s) 6 | end 7 | -------------------------------------------------------------------------------- /workshop/mechs/generic_loader.lua: -------------------------------------------------------------------------------- 1 | local parse = request('parser.parse') 2 | 3 | -- local c_stream = request('!.mechs.streams.mergeable.file.interface') 4 | local c_stream = request('!.mechs.streams.mergeable.string.interface') 5 | 6 | -- local profiler = request('!.system.profiler') 7 | -- local to_str = request('!.formats.lua_table_code.save') 8 | 9 | return 10 | function(data, syntax, ...) 11 | -- profiler.start() 12 | -- print(to_str(syntax)) 13 | 14 | local input_stream = new(c_stream) 15 | input_stream:init(data) 16 | 17 | local result 18 | 19 | local is_parsed, data_struc = parse(input_stream, syntax) 20 | if is_parsed then 21 | result = data_struc 22 | for i = 1, select('#', ...) do 23 | local struc_transformer = select(i, ...) 24 | result = struc_transformer(result) 25 | end 26 | end 27 | 28 | local unparsed_tail 29 | local elems_remained = 30 | input_stream:get_length() - input_stream:get_position() + 1 31 | if (elems_remained > 0) then 32 | unparsed_tail = input_stream:read(elems_remained) 33 | end 34 | 35 | -- profiler.stop() 36 | 37 | return result, unparsed_tail 38 | end 39 | -------------------------------------------------------------------------------- /workshop/mechs/geometry/1d/segments/is_inside.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(seg_outer, seg_inner) 3 | local seg_inner_finish = seg_inner.start + seg_inner.len 4 | local seg_outer_finish = seg_outer.start + seg_outer.len 5 | return 6 | (seg_inner.start >= seg_outer.start) and 7 | (seg_inner_finish <= seg_outer_finish) 8 | end 9 | -------------------------------------------------------------------------------- /workshop/mechs/geometry/1d/segments/subtract_inner.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Subtract inner segment. 3 | 4 | Returs two possible and segments. Which may be nil. 5 | ]] 6 | 7 | return 8 | function(big, small) 9 | local left_seg 10 | if (small.start > big.start) then 11 | left_seg = 12 | { 13 | start = big.start, 14 | len = small.start - big.start, 15 | } 16 | end 17 | local right_seg 18 | if (small.start + small.len < big.start + big.len) then 19 | right_seg = 20 | { 21 | start = small.start + small.len, 22 | len = (big.start + big.len) - (small.start + small.len), 23 | } 24 | end 25 | return left_seg, right_seg 26 | end 27 | -------------------------------------------------------------------------------- /workshop/mechs/graph/assembly_order.lua: -------------------------------------------------------------------------------- 1 | local dfs = request('dfs') 2 | 3 | return 4 | function(graph, options) 5 | options = options or {} 6 | local assembly_order_seq = {} 7 | options.handle_leave = 8 | function(node, node_rec, deep) 9 | assembly_order_seq[#assembly_order_seq + 1] = node 10 | end 11 | local node_recs = dfs(graph, options) 12 | return node_recs, assembly_order_seq 13 | end 14 | -------------------------------------------------------------------------------- /workshop/mechs/graph/dfs.lua: -------------------------------------------------------------------------------- 1 | -- DFS-pass manager 2 | 3 | local dfs_class = request('dfs.interface') 4 | 5 | return 6 | function(graph, options) 7 | local dfs = new(dfs_class, options) 8 | dfs:run(graph) 9 | return dfs.nodes_status 10 | end 11 | -------------------------------------------------------------------------------- /workshop/mechs/graph/dfs/dfs.lua: -------------------------------------------------------------------------------- 1 | -- DFS-pass of given graph 2 | 3 | return 4 | function(self, graph) 5 | self.nodes_status = {} 6 | 7 | local handle_discovery = self.handle_discovery 8 | local handle_leave = self.handle_leave 9 | local nodes_status = self.nodes_status 10 | local get_children = self.get_children 11 | 12 | local init_node_rec = 13 | function(node) 14 | nodes_status[node] = nodes_status[node] or {node = node} 15 | end 16 | 17 | local time = 0 18 | 19 | local dfs_visit 20 | 21 | local process = 22 | function(parent, parent_key, node, deep) 23 | init_node_rec(node) 24 | local node_rec = nodes_status[node] 25 | node_rec.refs = node_rec.refs or {} 26 | node_rec.refs[parent] = node_rec.refs[parent] or {} 27 | node_rec.refs[parent][parent_key] = true 28 | if not node_rec.color then 29 | node_rec.parent = parent 30 | node_rec.parent_key = parent_key 31 | dfs_visit(node, deep + 1) 32 | elseif (node_rec.color == 'gray') then 33 | node_rec.part_of_cycle = true 34 | nodes_status[parent].part_of_cycle = true 35 | end 36 | end 37 | 38 | dfs_visit = 39 | function(node, deep) 40 | time = time + 1 41 | local node_rec = nodes_status[node] 42 | node_rec.discovery_time = time 43 | node_rec.color = 'gray' 44 | handle_discovery(node, node_rec, deep) 45 | for _, child in ipairs(self:get_children(node)) do 46 | process(node, child.key, child.value, deep) 47 | end 48 | time = time + 1 49 | node_rec.color = 'black' 50 | node_rec.finish_time = time 51 | handle_leave(node, node_rec, deep) 52 | end 53 | 54 | init_node_rec(graph) 55 | dfs_visit(graph, 0) 56 | end 57 | -------------------------------------------------------------------------------- /workshop/mechs/graph/dfs/get_children.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Return list of children for given node. 3 | 4 | Each record in list should have fields and . 5 | - key to get . 6 | 7 | This method assumed to be overriden for custom formats. 8 | ]] 9 | 10 | local get_key_vals = request('!.table.get_key_vals') 11 | local compare = request('!.table.ordered_pass.default_comparator') 12 | 13 | return 14 | function(self, node) 15 | local result = {} 16 | local key_vals = get_key_vals(node) 17 | local also_visit_keys = self.also_visit_keys 18 | for _, rec in ipairs(key_vals) do 19 | if is_table(rec.value) then 20 | result[#result + 1] = rec 21 | end 22 | if also_visit_keys and is_table(rec.key) then 23 | result[#result + 1] = {key = rec.key, value = rec.key} 24 | end 25 | end 26 | table.sort(result, compare) 27 | return result 28 | end 29 | -------------------------------------------------------------------------------- /workshop/mechs/graph/dfs/interface.lua: -------------------------------------------------------------------------------- 1 | local empty_func = function() end 2 | 3 | return 4 | { 5 | get_children = request('get_children'), 6 | handle_discovery = empty_func, 7 | handle_leave = empty_func, 8 | also_visit_keys = false, 9 | table_iterator = request('!.table.ordered_pass'), 10 | 11 | run = request('dfs'), 12 | 13 | nodes_status = {}, 14 | } 15 | -------------------------------------------------------------------------------- /workshop/mechs/indents_table.lua: -------------------------------------------------------------------------------- 1 | local init = 2 | function(self) 3 | setmetatable( 4 | self.indents, 5 | { 6 | __index = 7 | function(t, key) 8 | if is_integer(key) then 9 | local value = self.indent_chunk:rep(key) 10 | t[key] = value 11 | return value 12 | end 13 | end, 14 | } 15 | ) 16 | end 17 | 18 | return 19 | { 20 | indents = {}, 21 | 22 | init = init, 23 | indent_chunk = ' ', 24 | } 25 | -------------------------------------------------------------------------------- /workshop/mechs/name_giver.lua: -------------------------------------------------------------------------------- 1 | return 2 | { 3 | names = {}, 4 | counters = 5 | { 6 | ['function'] = 0, 7 | ['thread'] = 0, 8 | ['userdata'] = 0, 9 | ['table'] = 0, 10 | }, 11 | templates = 12 | { 13 | ['function'] = 'f_%d', 14 | ['thread'] = 'th_%d', 15 | ['userdata'] = 'u_%d', 16 | ['table'] = 't_%d', 17 | }, 18 | give_name = 19 | function(self, obj) 20 | if not self.names[obj] then 21 | local obj_type = type(obj) 22 | if not self.counters[obj_type] then 23 | error( 24 | ('Argument type "%s" not supported for counting.'): 25 | format(obj_type), 26 | 2 27 | ) 28 | end 29 | self.counters[obj_type] = self.counters[obj_type] + 1 30 | self.names[obj] = 31 | (self.templates[obj_type]):format(self.counters[obj_type]) 32 | end 33 | return self.names[obj] 34 | end, 35 | } 36 | -------------------------------------------------------------------------------- /workshop/mechs/number/representer/interface.lua: -------------------------------------------------------------------------------- 1 | return 2 | { 3 | type = 'binary_bytes', 4 | digits_display = 3, 5 | init = function(self) end, 6 | represent = request('represent'), 7 | 8 | units = request('units.interface'), 9 | max_part_digits = 16, 10 | } 11 | -------------------------------------------------------------------------------- /workshop/mechs/number/representer/represent.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self, n) 3 | assert_number(n) 4 | if not self.units[self.type] then 5 | error(('Have no settings for type "%s".'):format(self.type), 2) 6 | end 7 | local pos = 0 8 | local powers = self.units[self.type] 9 | local is_neg = (n < 0) 10 | n = math.abs(n) 11 | 12 | ::redo:: 13 | local int_part, frac_part = math.modf(n) 14 | -- print(self.type, int_part, frac_part, pos, n) 15 | if powers[pos - 1] and (int_part == 0) then 16 | n = n * powers[pos][1] 17 | pos = pos - 1 18 | goto redo 19 | end 20 | if powers[pos + 1] and (int_part >= powers[pos + 1][1]) then 21 | pos = pos + 1 22 | n = n / powers[pos][1] 23 | goto redo 24 | end 25 | 26 | local unit_name = powers[pos][2] 27 | local s_int = ('%d'):format(int_part) 28 | 29 | local digits_remained = self.digits_display - #s_int 30 | digits_remained = math.min(digits_remained, self.max_part_digits) 31 | 32 | local has_eps = (frac_part > 0) 33 | local s_frac = '' 34 | 35 | if (digits_remained > 0) then 36 | frac_part = math.floor(frac_part * 10 ^ digits_remained) 37 | -- Do not add zero fraction part: ".00". 38 | if (frac_part > 0) then 39 | s_frac = ('%0' .. digits_remained .. 'd'):format(frac_part) 40 | s_frac = '.' .. (s_frac):sub(1, digits_remained) 41 | end 42 | end 43 | 44 | local result 45 | 46 | if (int_part == 0) and (frac_part == 0) and has_eps then 47 | result = 'EPS' 48 | else 49 | result = s_int .. s_frac 50 | end 51 | 52 | if is_neg then 53 | result = '-' .. result 54 | end 55 | 56 | result = result .. unit_name 57 | 58 | return result 59 | end 60 | -------------------------------------------------------------------------------- /workshop/mechs/number/representer/units/binary_bytes.lua: -------------------------------------------------------------------------------- 1 | return 2 | { 3 | [0] = {1024, 'B'}, 4 | [1] = {1024, 'KiB'}, 5 | [2] = {1024, 'MiB'}, 6 | [3] = {1024, 'GiB'}, 7 | [4] = {1024, 'TiB'}, 8 | [5] = {1024, 'PiB'}, 9 | [6] = {1024, 'EiB'}, 10 | [7] = {1024, 'ZiB'}, 11 | [8] = {1024, 'YiB'}, 12 | } 13 | -------------------------------------------------------------------------------- /workshop/mechs/number/representer/units/binary_units.lua: -------------------------------------------------------------------------------- 1 | return 2 | { 3 | [0] = {1024, ''}, 4 | [1] = {1024, 'Ki'}, 5 | [2] = {1024, 'Mi'}, 6 | [3] = {1024, 'Gi'}, 7 | [4] = {1024, 'Ti'}, 8 | [5] = {1024, 'Pi'}, 9 | [6] = {1024, 'Ei'}, 10 | [7] = {1024, 'Zi'}, 11 | [8] = {1024, 'Yi'}, 12 | } 13 | -------------------------------------------------------------------------------- /workshop/mechs/number/representer/units/frequency.lua: -------------------------------------------------------------------------------- 1 | return 2 | { 3 | [-8] = {1000, 'yHz'}, 4 | [-7] = {1000, 'zHz'}, 5 | [-6] = {1000, 'aHz'}, 6 | [-5] = {1000, 'fHz'}, 7 | [-4] = {1000, 'pHz'}, 8 | [-3] = {1000, 'nHz'}, 9 | [-2] = {1000, 'mcrHz'}, 10 | [-1] = {1000, 'mHz'}, 11 | [0] = {1000, 'Hz'}, 12 | [1] = {1000, 'kHz'}, 13 | [2] = {1000, 'MHz'}, 14 | [3] = {1000, 'GHz'}, 15 | [4] = {1000, 'THz'}, 16 | [5] = {1000, 'PHz'}, 17 | [6] = {1000, 'EHz'}, 18 | [7] = {1000, 'ZHz'}, 19 | [8] = {1000, 'YHz'}, 20 | } 21 | -------------------------------------------------------------------------------- /workshop/mechs/number/representer/units/general_number.lua: -------------------------------------------------------------------------------- 1 | return 2 | { 3 | [-8] = {1000, 'y'}, 4 | [-7] = {1000, 'z'}, 5 | [-6] = {1000, 'a'}, 6 | [-5] = {1000, 'f'}, 7 | [-4] = {1000, 'p'}, 8 | [-3] = {1000, 'n'}, 9 | [-2] = {1000, 'mcr'}, 10 | [-1] = {1000, 'm'}, 11 | [0] = {1000, ''}, 12 | [1] = {1000, 'k'}, 13 | [2] = {1000, 'M'}, 14 | [3] = {1000, 'G'}, 15 | [4] = {1000, 'T'}, 16 | [5] = {1000, 'P'}, 17 | [6] = {1000, 'E'}, 18 | [7] = {1000, 'Z'}, 19 | [8] = {1000, 'Y'}, 20 | } 21 | -------------------------------------------------------------------------------- /workshop/mechs/number/representer/units/general_time.lua: -------------------------------------------------------------------------------- 1 | return 2 | { 3 | [-8] = {1000, 'ys'}, 4 | [-7] = {1000, 'zs'}, 5 | [-6] = {1000, 'as'}, 6 | [-5] = {1000, 'fs'}, 7 | [-4] = {1000, '㎰'}, 8 | [-3] = {1000, '㎱'}, 9 | [-2] = {1000, '㎲'}, 10 | [-1] = {1000, '㎳'}, 11 | [0] = {1000, 's'}, 12 | [1] = {3600, 'hr'}, 13 | [2] = {24, 'day'}, 14 | [3] = {365.2425, 'y'}, 15 | [4] = {1000, 'Ky'}, 16 | [5] = {1000, 'My'}, 17 | [6] = {1000, 'Gy'}, 18 | [7] = {1000, 'Ty'}, 19 | [8] = {1000, 'Py'}, 20 | } 21 | -------------------------------------------------------------------------------- /workshop/mechs/number/representer/units/interface.lua: -------------------------------------------------------------------------------- 1 | return 2 | { 3 | binary_bytes = request('binary_bytes'), 4 | binary_units = request('binary_units'), 5 | frequency = request('frequency'), 6 | general_number = request('general_number'), 7 | general_time = request('general_time'), 8 | } 9 | -------------------------------------------------------------------------------- /workshop/mechs/parser/folder/fold.lua: -------------------------------------------------------------------------------- 1 | local is_inside = request('!.mechs.geometry.1d.segments.is_inside') 2 | 3 | return 4 | function(self) 5 | if self.is_folded then 6 | return 7 | end 8 | local struc = self.struc 9 | if (#struc >= 2) then 10 | local src_idx = 2 11 | local dest_idx = 1 12 | local result = {struc[1]} 13 | repeat 14 | if not is_inside(struc[src_idx], result[dest_idx]) then 15 | dest_idx = dest_idx + 1 16 | result[dest_idx] = struc[src_idx] 17 | else 18 | local start_idx = dest_idx 19 | repeat 20 | start_idx = start_idx - 1 21 | until 22 | not result[start_idx] or 23 | not is_inside(struc[src_idx], result[start_idx]) 24 | 25 | start_idx = start_idx + 1 26 | local dest_table = 27 | { 28 | start = struc[src_idx].start, 29 | len = struc[src_idx].len, 30 | type = struc[src_idx].type, 31 | } 32 | local copy_idx = start_idx 33 | for i = start_idx, dest_idx do 34 | dest_table[#dest_table + 1] = result[i] 35 | end 36 | result[start_idx] = dest_table 37 | dest_idx = start_idx 38 | end 39 | src_idx = src_idx + 1 40 | until (src_idx > #struc) 41 | dest_idx = dest_idx + 1 42 | while result[dest_idx] do 43 | result[dest_idx] = nil 44 | dest_idx = dest_idx + 1 45 | end 46 | struc = result 47 | end 48 | --[[ 49 | In practice fold() is called at end of processing. So 50 | no additional data is going to be add to . Also 51 | often data is grouped to one root record. We'll unfold 52 | it here, breaking possibility to add additional data 53 | to but freeing dependent code of undolding it 54 | itself. 55 | ]] 56 | if (#struc == 1) then 57 | self.struc = struc[1] 58 | else 59 | self.struc = struc 60 | end 61 | self.is_folded = true 62 | end 63 | -------------------------------------------------------------------------------- /workshop/mechs/parser/folder/get_struc.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self) 3 | return self.struc 4 | end 5 | -------------------------------------------------------------------------------- /workshop/mechs/parser/folder/init.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self, struc) 3 | assert_table(struc) 4 | self.struc = struc 5 | self.is_folded = false 6 | end 7 | -------------------------------------------------------------------------------- /workshop/mechs/parser/folder/interface.lua: -------------------------------------------------------------------------------- 1 | return 2 | { 3 | init = request('init'), 4 | fold = request('fold'), 5 | get_struc = request('get_struc'), 6 | 7 | struc = nil, 8 | is_folded = nil, 9 | } 10 | -------------------------------------------------------------------------------- /workshop/mechs/parser/get_struc.lua: -------------------------------------------------------------------------------- 1 | local c_folder = request('folder.interface') 2 | 3 | return 4 | function(marks) 5 | local folder = new(c_folder) 6 | folder:init(marks) 7 | folder:fold() 8 | return folder:get_struc() 9 | end 10 | -------------------------------------------------------------------------------- /workshop/mechs/parser/handy.lua: -------------------------------------------------------------------------------- 1 | local spawn_match_function = 2 | function(pattern) 3 | assert_string(pattern) 4 | assert(pattern ~= '') 5 | -- Place "begin-of-text" anchor: 6 | if (pattern:sub(1, 1) ~= '^') then 7 | pattern = '^' .. pattern 8 | end 9 | return 10 | function(stream) 11 | return stream:match_regexp(pattern) 12 | end 13 | end 14 | 15 | local any_char = 16 | function(stream) 17 | return stream:block_read(1) 18 | end 19 | 20 | return 21 | { 22 | match_regexp = spawn_match_function, 23 | any_char = any_char, 24 | } 25 | -------------------------------------------------------------------------------- /workshop/mechs/parser/on_match.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(input_stream, output_stream, rule_name, init_in_stream_pos) 3 | local len = input_stream:get_position() - init_in_stream_pos 4 | if (len > 0) then 5 | output_stream:write( 6 | { 7 | start = init_in_stream_pos, 8 | len = len, 9 | type = rule_name, 10 | } 11 | ) 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /workshop/mechs/parser/parse.lua: -------------------------------------------------------------------------------- 1 | local c_sequence = request('!.mechs.streams.sequence.interface') 2 | local c_processor = request('!.mechs.processor.core.interface') 3 | local on_match = request('on_match') 4 | local get_struc = request('get_struc') 5 | local populate = request('populate') 6 | 7 | return 8 | function(in_stream, struc) 9 | assert_table(in_stream) 10 | assert(is_table(struc) or is_string(struc) or is_function(struc)) 11 | 12 | local out_stream = new(c_sequence) 13 | out_stream:init({}) 14 | 15 | local processor = new(c_processor) 16 | processor.input_stream = in_stream 17 | processor.output_stream = out_stream 18 | processor.on_match = on_match 19 | 20 | processor:init() 21 | local is_parsed = processor:match(struc) 22 | local data_struc = out_stream.seq 23 | data_struc = get_struc(out_stream.seq) 24 | populate(data_struc, in_stream) 25 | 26 | return is_parsed, data_struc 27 | end 28 | -------------------------------------------------------------------------------- /workshop/mechs/parser/populate.lua: -------------------------------------------------------------------------------- 1 | local subtract_seg = request('!.mechs.geometry.1d.segments.subtract_inner') 2 | 3 | local process = 4 | function(data_struc, in_stream) 5 | 6 | local get_segment = 7 | function(node) 8 | return in_stream:get_segment(node.start, node.len) 9 | end 10 | 11 | local populate 12 | populate = 13 | function(node) 14 | if not is_table(node) then 15 | return 16 | end 17 | local outer_seg = node 18 | local i = 1 19 | while (i <= #node) and outer_seg do 20 | local l_seg, r_seg = subtract_seg(outer_seg, node[i]) 21 | local seg_val 22 | if l_seg then 23 | seg_val = get_segment(l_seg) 24 | table.insert(node, i, seg_val) 25 | i = i + 1 26 | end 27 | outer_seg = r_seg 28 | i = i + 1 29 | end 30 | if outer_seg then 31 | seg_val = get_segment(outer_seg) 32 | table.insert(node, i, seg_val) 33 | end 34 | for i = 1, #node do 35 | populate(node[i]) 36 | end 37 | node.start = nil 38 | node.len = nil 39 | end 40 | 41 | if next(data_struc) then 42 | populate(data_struc) 43 | end 44 | end 45 | 46 | return process 47 | 48 | --[[ 49 | * For all records we remove data offsets related to input stream. 50 | 51 | This is because 52 | * we assume that input stream is no longer accessible in 53 | further flow 54 | * the intention of parsing is remove more low-level layer, 55 | not just annotate it 56 | ]] 57 | -------------------------------------------------------------------------------- /workshop/mechs/processor/core/init.lua: -------------------------------------------------------------------------------- 1 | local assert_is_stream = 2 | function(t) 3 | assert_table(t) 4 | assert_function(t.get_position) 5 | assert_function(t.set_position) 6 | end 7 | 8 | return 9 | function(self) 10 | assert_is_stream(self.input_stream) 11 | assert_is_stream(self.output_stream) 12 | end 13 | -------------------------------------------------------------------------------- /workshop/mechs/processor/core/interface.lua: -------------------------------------------------------------------------------- 1 | return 2 | { 3 | input_stream = nil, 4 | output_stream = nil, 5 | 6 | on_match = request('on_match'), 7 | 8 | init = request('init'), 9 | match = request('match'), 10 | } 11 | -------------------------------------------------------------------------------- /workshop/mechs/processor/core/match.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self, rule) 3 | local rule_type = type(rule) 4 | if (rule_type == 'string') then 5 | return self.input_stream:match_string(rule) 6 | elseif (rule_type == 'function') then 7 | return rule(self.input_stream, self.output_stream) 8 | elseif (rule_type == 'table') then 9 | local op_neg = (rule.op == 'neg') 10 | local op_opt = (rule.op == 'opt') 11 | local f_rep = rule.f_rep 12 | if f_rep and op_neg then 13 | f_rep = nil 14 | end 15 | 16 | local rule_name = rule.name 17 | 18 | local result 19 | local repeat_result 20 | local mode_choice = rule.mode_choice 21 | local mode_seq = not mode_choice 22 | 23 | local input_stream = self.input_stream 24 | local output_stream = self.output_stream 25 | 26 | ::restart:: 27 | local round_in_stream_pos = input_stream:get_position() 28 | local round_out_stream_pos = output_stream:get_position() 29 | 30 | if mode_seq then 31 | result = true 32 | for i = 1, #rule do 33 | if not self:match(rule[i]) then 34 | result = false 35 | break 36 | end 37 | end 38 | else 39 | -- mode_choice 40 | result = false 41 | for i = 1, #rule do 42 | if self:match(rule[i]) then 43 | result = true 44 | break 45 | end 46 | end 47 | end 48 | 49 | if not result then 50 | input_stream:set_position(round_in_stream_pos) 51 | output_stream:set_position(round_out_stream_pos) 52 | elseif rule_name then 53 | self.on_match(input_stream, output_stream, rule_name, round_in_stream_pos) 54 | end 55 | 56 | if f_rep then 57 | if result then 58 | repeat_result = true 59 | goto restart 60 | else 61 | result = repeat_result 62 | end 63 | end 64 | if op_opt then 65 | result = true 66 | elseif op_neg then 67 | result = not result 68 | end 69 | 70 | return result 71 | end 72 | end 73 | -------------------------------------------------------------------------------- /workshop/mechs/processor/core/on_match.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(input_stream, output_stream, rule, init_input_stream_position) 3 | error('Virtual method. Should be overriden.') 4 | end 5 | -------------------------------------------------------------------------------- /workshop/mechs/processor/handy.lua: -------------------------------------------------------------------------------- 1 | -- Handy functions for describing syntax 2 | 3 | --[[ 4 | This module exports syntax-sugar functions for brief and 5 | obvious defining common syntax constructions. 6 | ]] 7 | 8 | local cho = 9 | function(...) 10 | local node = {...} 11 | node.mode_choice = true 12 | return node 13 | end 14 | 15 | local set_repeat = 16 | function(...) 17 | local node = {...} 18 | node.f_rep = true 19 | return node 20 | end 21 | 22 | local set_opt_rep = 23 | function(...) 24 | local node = {...} 25 | node.op = 'opt' 26 | node.f_rep = true 27 | return node 28 | end 29 | 30 | local set_operand = 31 | function(operand) 32 | return 33 | function(...) 34 | local node = {...} 35 | node.op = operand 36 | return node 37 | end 38 | end 39 | 40 | local make_list_record = 41 | function(...) 42 | local body = {...} 43 | assert(#body >= 2) 44 | local delimiter = body[#body] 45 | body[#body] = nil 46 | return {body, set_opt_rep({delimiter, body})} 47 | end 48 | 49 | local interleave = 50 | function(t, delimiter) 51 | assert_table(t) 52 | for i = #t, 2, -1 do 53 | table.insert(t, i, delimiter) 54 | end 55 | return t 56 | end 57 | 58 | return 59 | { 60 | cho = cho, 61 | is_not = set_operand('neg'), 62 | opt = set_operand('opt'), 63 | rep = set_repeat, 64 | opt_rep = set_opt_rep, 65 | list = make_list_record, 66 | -- interleave = interleave, 67 | } 68 | -------------------------------------------------------------------------------- /workshop/mechs/processor/link.lua: -------------------------------------------------------------------------------- 1 | -- Generate final structure for parser 2 | 3 | --[[ 4 | This module takes almost-ready structure and finalizes it 5 | by resolving links. 6 | ]] 7 | 8 | local pass_graph = request('!.mechs.graph.dfs') 9 | 10 | local is_producer = 11 | function(node) 12 | return node.inner_name 13 | end 14 | 15 | local ref_pattern = '^%>(%a[%a%d_]*)$' -- Sample: >dig_base_10 16 | 17 | local is_consumer = 18 | function(node) 19 | local result 20 | for i = 1, #node do 21 | if is_string(node[i]) then 22 | local ref_name = node[i]:match(ref_pattern) 23 | if ref_name then 24 | result = true 25 | break 26 | end 27 | end 28 | end 29 | return result 30 | end 31 | 32 | local add_producer = 33 | function(node, producers) 34 | local name = node.inner_name 35 | if producers[name] then 36 | -- print(('Already have producer with name "%s".'):format(name)) 37 | else 38 | producers[name] = node 39 | end 40 | end 41 | 42 | local add_consumer = 43 | function(node, consumers) 44 | if consumers[node] then 45 | -- print(('Already have consumer "%s.".'):format(name)) 46 | else 47 | consumers[node] = node 48 | end 49 | end 50 | 51 | local resolve_links = 52 | function(node, producers) 53 | local has_unresolved_links 54 | for i = 1, #node do 55 | if is_string(node[i]) then 56 | local ref_name = node[i]:match(ref_pattern) 57 | if ref_name then 58 | if producers[ref_name] then 59 | node[i] = producers[ref_name] 60 | else 61 | has_unresolved_links = true 62 | end 63 | end 64 | end 65 | end 66 | return not has_unresolved_links 67 | end 68 | 69 | return 70 | function(node, producers, consumers) 71 | local producers = producers or {} 72 | local consumers = consumers or {} 73 | pass_graph( 74 | node, 75 | { 76 | handle_discovery = 77 | function(node, node_rec, deep) 78 | if is_producer(node) then 79 | add_producer(node, producers) 80 | end 81 | if is_consumer(node) then 82 | add_consumer(node, consumers) 83 | end 84 | end, 85 | } 86 | ) 87 | for k, v in pairs(consumers) do 88 | if resolve_links(v, producers) then 89 | consumers[k] = nil 90 | end 91 | end 92 | 93 | pass_graph( 94 | node, 95 | { 96 | handle_leave = 97 | function(node, node_rec, deep) 98 | if node.inner_name then 99 | node.inner_name = nil 100 | end 101 | end, 102 | } 103 | ) 104 | end 105 | -------------------------------------------------------------------------------- /workshop/mechs/processor/optimize.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | We're inlining lone children into parents trying to get 4 | "saturated" record with following structure: 5 | 6 | --+---------+--+---------+--+----------+-- 7 | +-| opt |-+ `-| rep |-' `-| name |-' 8 | `-| neg |-' 9 | 10 | ]] 11 | 12 | local to_str = request('!.formats.lua_table_code.save') 13 | local assembly_order = request('!.mechs.graph.assembly_order') 14 | 15 | local get_num_refs = 16 | function(node_rec) 17 | local result = 0 18 | if node_rec.refs then 19 | for parent, parent_keys in pairs(node_rec.refs) do 20 | for field in pairs(parent_keys) do 21 | result = result + 1 22 | end 23 | end 24 | end 25 | return result 26 | end 27 | 28 | local get_first_parent = 29 | function(node_rec) 30 | local parent, child_key 31 | if node_rec.refs then 32 | parent, child_key = next(node_rec.refs) 33 | child_key = next(child_key) 34 | end 35 | return parent, child_key 36 | end 37 | 38 | local not_both = 39 | function(a, b) 40 | return not (a and b) 41 | end 42 | 43 | local modes_are_compatible = 44 | function(mode_a, mode_b) 45 | return (not mode_a and not mode_b) or (mode_a == mode_b) 46 | end 47 | 48 | return 49 | function(grammar) 50 | -- print('Source grammar:') 51 | -- print(to_str(grammar)) 52 | 53 | local embed = 54 | function(parent, child_key, node) 55 | parent.name = parent.name or node.name 56 | parent.op = parent.op or node.op 57 | parent.f_rep = parent.f_rep or node.f_rep 58 | table.remove(parent, child_key) 59 | for j = #node, 1, -1 do 60 | table.insert(parent, child_key, node[j]) 61 | end 62 | end 63 | 64 | :: restart :: 65 | local node_recs, node_order = assembly_order(grammar, {table_iterator = pairs}) 66 | for i = 1, #node_order do 67 | local node = node_order[i] 68 | -- If is rule node: 69 | if (#node > 0) then 70 | local node_rec = node_recs[node] 71 | if (get_num_refs(node_rec) <= 1) then 72 | local parent, child_key = get_first_parent(node_rec) 73 | if parent then 74 | if 75 | (#parent == 1) and 76 | not_both(parent.name, node.name) and 77 | modes_are_compatible(parent.mode_choice, node.mode_choice) and 78 | ( 79 | (not parent.op) or 80 | (not node.op) or 81 | ((parent.op == 'opt') and (node.op == 'opt')) 82 | ) 83 | then 84 | embed(parent, child_key, node) 85 | elseif 86 | not node.name and 87 | modes_are_compatible(parent.mode_choice, node.mode_choice) and 88 | (not parent.f_rep and not node.f_rep) and 89 | (not parent.op and not node.op) 90 | then 91 | embed(parent, child_key, node) 92 | goto restart 93 | end 94 | end 95 | end 96 | end 97 | end 98 | 99 | -- print('Optimized grammar:') 100 | -- print(to_str(grammar)) 101 | end 102 | -------------------------------------------------------------------------------- /workshop/mechs/streams/mergeable/block_read.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self, num_positions) 3 | local original_position = self:get_position() 4 | local result, num_read = self:read(num_positions) 5 | if (num_read < num_positions) then 6 | self:set_position(original_position) 7 | return 8 | end 9 | return result 10 | end 11 | -------------------------------------------------------------------------------- /workshop/mechs/streams/mergeable/get_segment.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self, start, len) 3 | local init_pos = self:get_position() 4 | self:set_position(start) 5 | local result = self:block_read(len) 6 | self:set_position(init_pos) 7 | return result 8 | end 9 | -------------------------------------------------------------------------------- /workshop/mechs/streams/mergeable/get_slot.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self) 3 | local init_pos = self:get_position() 4 | local result = self:block_read(1) 5 | self:set_position(init_pos) 6 | return result 7 | end 8 | -------------------------------------------------------------------------------- /workshop/mechs/streams/mergeable/init.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self) 3 | end 4 | -------------------------------------------------------------------------------- /workshop/mechs/streams/mergeable/interface.lua: -------------------------------------------------------------------------------- 1 | return 2 | { 3 | init = request('init'), 4 | -- core for implementors: 5 | get_position = nil, 6 | set_position = nil, 7 | read = nil, 8 | -- optional for implementors: 9 | get_length = nil, 10 | -- just handy: 11 | block_read = request('block_read'), 12 | get_segment = request('get_segment'), 13 | set_relative_position = request('set_relative_position'), 14 | set_next_position = request('set_next_position'), 15 | get_slot = request('get_slot'), 16 | } 17 | -------------------------------------------------------------------------------- /workshop/mechs/streams/mergeable/set_next_position.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self) 3 | self:set_position(self:get_position() + 1) 4 | end 5 | -------------------------------------------------------------------------------- /workshop/mechs/streams/mergeable/set_relative_position.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self, position_offset) 3 | self:set_position(self:get_position() + position_offset) 4 | end 5 | -------------------------------------------------------------------------------- /workshop/mechs/streams/mergeable/string/get_length.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self) 3 | return #self.s 4 | end 5 | -------------------------------------------------------------------------------- /workshop/mechs/streams/mergeable/string/get_position.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self) 3 | return self.position 4 | end 5 | -------------------------------------------------------------------------------- /workshop/mechs/streams/mergeable/string/init.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self, s) 3 | assert_string(s) 4 | self.s = s 5 | self.position = 1 6 | self:original_init() 7 | end 8 | -------------------------------------------------------------------------------- /workshop/mechs/streams/mergeable/string/interface.lua: -------------------------------------------------------------------------------- 1 | local merge = request('!.table.merge') 2 | local result = new(request('^.interface')) 3 | local original_init = result.init 4 | return 5 | merge( 6 | result, 7 | { 8 | init = request('init'), 9 | get_position = request('get_position'), 10 | set_position = request('set_position'), 11 | read = request('read'), 12 | get_length = request('get_length'), 13 | -- extensions: 14 | match_string = request('match_string'), 15 | match_regexp = request('match_regexp'), 16 | -- internal: 17 | s = nil, 18 | position = nil, 19 | original_init = original_init, 20 | } 21 | ) 22 | -------------------------------------------------------------------------------- /workshop/mechs/streams/mergeable/string/match_regexp.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Try to find regexp pattern from current position in stream. 3 | If found, 4 | set position past last character in found pattern, 5 | return capture 6 | else 7 | return nothing 8 | ]] 9 | 10 | return 11 | function(self, pattern) 12 | -- print(('match_regexp [%d, "%s"]'):format(self:get_position(), pattern)) 13 | local start, finish = self.s:find(pattern, self:get_position()) 14 | if start then 15 | self:set_position(finish + 1) 16 | return self.s:sub(start, finish) 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /workshop/mechs/streams/mergeable/string/match_string.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self, str) 3 | -- print(('match [%d, "%s"]'):format(self:get_position(), str)) 4 | local segment = self.s:sub(self.position, self.position + #str - 1) 5 | if (segment == str) then 6 | self.position = self.position + #str 7 | return true 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /workshop/mechs/streams/mergeable/string/read.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self, num_positions) 3 | local result = self.s:sub(self.position, self.position + num_positions - 1) 4 | -- print(('raw_read [%d, %d, "%s"]'):format(self:get_position(), num_positions, result)) 5 | local num_read = #result 6 | self:set_relative_position(num_read) 7 | return result, num_read 8 | end 9 | -------------------------------------------------------------------------------- /workshop/mechs/streams/mergeable/string/set_position.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self, new_position) 3 | if (new_position == self.position) then 4 | return 5 | end 6 | if (new_position < 1) then 7 | new_position = 1 8 | elseif (new_position > #self.s + 1) then 9 | new_position = #self.s + 1 10 | end 11 | self.position = new_position 12 | end 13 | -------------------------------------------------------------------------------- /workshop/mechs/streams/sequence/get_position.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self) 3 | return self.position 4 | end 5 | -------------------------------------------------------------------------------- /workshop/mechs/streams/sequence/init.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self, seq) 3 | assert_table(seq) 4 | self.seq = seq 5 | self.position = 1 6 | end 7 | -------------------------------------------------------------------------------- /workshop/mechs/streams/sequence/interface.lua: -------------------------------------------------------------------------------- 1 | return 2 | { 3 | init = request('init'), 4 | get_position = request('get_position'), 5 | set_position = request('set_position'), 6 | read = request('read'), 7 | write = request('write'), 8 | -- internal: 9 | seq = nil, 10 | position = nil, 11 | } 12 | -------------------------------------------------------------------------------- /workshop/mechs/streams/sequence/read.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self) 3 | local result = self.seq[self.position] 4 | self:set_position(self.position + 1) 5 | return result 6 | end 7 | -------------------------------------------------------------------------------- /workshop/mechs/streams/sequence/set_position.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self, new_position) 3 | if (new_position == self.position) then 4 | return 5 | end 6 | if (new_position < 1) then 7 | new_position = 1 8 | end 9 | if (new_position > self.position) then 10 | new_position = self.position 11 | end 12 | for i = new_position, self.position do 13 | self.seq[i] = nil 14 | end 15 | self.position = new_position 16 | end 17 | -------------------------------------------------------------------------------- /workshop/mechs/streams/sequence/write.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self, value) 3 | self.seq[self.position] = value 4 | self.position = self.position + 1 5 | end 6 | -------------------------------------------------------------------------------- /workshop/mechs/text_block/dec_indent.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self) 3 | self.next_line_indent = self.next_line_indent - 1 4 | end 5 | -------------------------------------------------------------------------------- /workshop/mechs/text_block/inc_indent.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self) 3 | self.next_line_indent = self.next_line_indent + 1 4 | end 5 | -------------------------------------------------------------------------------- /workshop/mechs/text_block/init.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self) 3 | self.processed_text = {} 4 | 5 | self.line_with_text.indents_obj.indent_chunk = self.indent_chunk 6 | self.line_with_text:init() 7 | self.line_with_text.indent = self.next_line_indent 8 | 9 | self.num_line_feeds = 0 10 | end 11 | -------------------------------------------------------------------------------- /workshop/mechs/text_block/interface.lua: -------------------------------------------------------------------------------- 1 | return 2 | { 3 | -- text: 4 | line_with_text = request('line.interface'), 5 | processed_text = {}, 6 | num_line_feeds = 0, 7 | 8 | store_textline = request('text.store_textline'), 9 | add_textline = request('text.add_textline'), 10 | add_curline = request('text.add_curline'), 11 | 12 | new_line = request('text.new_line'), 13 | request_clean_line = request('text.request_clean_line'), 14 | request_empty_line = request('text.request_empty_line'), 15 | 16 | on_clean_line = request('text.on_clean_line'), 17 | 18 | include = request('text.include'), 19 | 20 | get_text = request('text.get_text'), 21 | 22 | -- indents: 23 | indent_chunk = ' ', 24 | next_line_indent = 0, 25 | inc_indent = request('inc_indent'), 26 | dec_indent = request('dec_indent'), 27 | 28 | -- text length: 29 | max_text_width = 0, 30 | max_block_width = 0, 31 | get_text_width = request('text.get_text_width'), 32 | get_block_width = request('text.get_block_width'), 33 | 34 | init = request('init'), 35 | } 36 | -------------------------------------------------------------------------------- /workshop/mechs/text_block/line/add.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self, s) 3 | self.text = self.text .. s 4 | end 5 | -------------------------------------------------------------------------------- /workshop/mechs/text_block/line/get_line.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self) 3 | if (self.text == '') then 4 | return '' 5 | else 6 | return self.indents_obj.indents[self.indent] .. self.text 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /workshop/mechs/text_block/line/get_line_length.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self) 3 | return self.indent * self.chunk_length + self:get_text_length() 4 | end 5 | -------------------------------------------------------------------------------- /workshop/mechs/text_block/line/get_text_length.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self) 3 | return utf8.len(self.text) or #self.text 4 | end 5 | -------------------------------------------------------------------------------- /workshop/mechs/text_block/line/init.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self) 3 | self.indents_obj:init() 4 | self.chunk_length = utf8.len(self.indents_obj.indent_chunk) 5 | end 6 | -------------------------------------------------------------------------------- /workshop/mechs/text_block/line/interface.lua: -------------------------------------------------------------------------------- 1 | return 2 | { 3 | text = '', 4 | indent = 0, 5 | indents_obj = request('^.^.indents_table'), 6 | chunk_length = 0, 7 | 8 | init = request('init'), 9 | 10 | get_line_length = request('get_line_length'), 11 | get_text_length = request('get_text_length'), 12 | get_line = request('get_line'), 13 | add = request('add'), 14 | } 15 | -------------------------------------------------------------------------------- /workshop/mechs/text_block/text/add_curline.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self, s) 3 | if (self.num_line_feeds > 0) and (s ~= '') then 4 | --[[ 5 | We're going to add some text to currently empty line. 6 | So will point to this text. Save previous 7 | text from this object. 8 | ]] 9 | self:store_textline() 10 | end 11 | if (self.line_with_text.text == '') then 12 | self.line_with_text.indent = self.next_line_indent 13 | end 14 | self.line_with_text:add(s) 15 | end 16 | -------------------------------------------------------------------------------- /workshop/mechs/text_block/text/add_textline.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self, s) 3 | self.line_with_text:add(s) 4 | end 5 | -------------------------------------------------------------------------------- /workshop/mechs/text_block/text/get_block_width.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self) 3 | return 4 | math.max(self.max_block_width, self.line_with_text:get_line_length()) 5 | end 6 | -------------------------------------------------------------------------------- /workshop/mechs/text_block/text/get_text.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self) 3 | self:store_textline() 4 | local result = table.concat(self.processed_text) 5 | return result 6 | end 7 | -------------------------------------------------------------------------------- /workshop/mechs/text_block/text/get_text_width.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self) 3 | return 4 | math.max(self.max_text_width, self.line_with_text:get_text_length()) 5 | end 6 | -------------------------------------------------------------------------------- /workshop/mechs/text_block/text/include.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self, block, do_glue_border_lines) 3 | if not do_glue_border_lines then 4 | self:new_line() 5 | end 6 | self:store_textline() 7 | 8 | table.move( 9 | block.processed_text, 10 | 1, 11 | #block.processed_text, 12 | #self.processed_text + 1, 13 | self.processed_text 14 | ) 15 | 16 | self.line_with_text = block.line_with_text 17 | end 18 | -------------------------------------------------------------------------------- /workshop/mechs/text_block/text/new_line.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self) 3 | self.num_line_feeds = self.num_line_feeds + 1 4 | end 5 | -------------------------------------------------------------------------------- /workshop/mechs/text_block/text/on_clean_line.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self) 3 | return 4 | (self.num_line_feeds > 0) or 5 | ( 6 | (self.num_line_feeds == 0) and 7 | (self.line_with_text.text == '') 8 | ) 9 | end 10 | -------------------------------------------------------------------------------- /workshop/mechs/text_block/text/request_clean_line.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self) 3 | if not self:on_clean_line() then 4 | self:new_line() 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /workshop/mechs/text_block/text/request_empty_line.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(self) 3 | if not self:on_clean_line() then 4 | self:new_line() 5 | end 6 | if (self.num_line_feeds == 1) then 7 | self:new_line() 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /workshop/mechs/text_block/text/store_textline.lua: -------------------------------------------------------------------------------- 1 | local trim = request('!.string.trim') 2 | 3 | return 4 | function(self) 5 | local line_with_text = self.line_with_text 6 | 7 | line_with_text.text = trim(line_with_text.text) 8 | 9 | self.max_block_width = self:get_block_width() 10 | self.max_text_width = self:get_text_width() 11 | 12 | self.processed_text[#self.processed_text + 1] = line_with_text:get_line() 13 | for i = 1, self.num_line_feeds do 14 | self.processed_text[#self.processed_text + 1] = '\n' 15 | end 16 | self.num_line_feeds = 0 17 | 18 | line_with_text.text = '' 19 | line_with_text.indent = self.next_line_indent 20 | end 21 | -------------------------------------------------------------------------------- /workshop/number/represent_size.lua: -------------------------------------------------------------------------------- 1 | local representer = request('!.mechs.number.representer.interface') 2 | 3 | representer = 4 | new( 5 | representer, 6 | { 7 | type = 'binary_bytes', 8 | digits_display = 3, 9 | } 10 | ) 11 | representer:init() 12 | 13 | return 14 | function(n) 15 | return representer:represent(n) 16 | end 17 | -------------------------------------------------------------------------------- /workshop/number/represent_time.lua: -------------------------------------------------------------------------------- 1 | local representer = request('!.mechs.number.representer.interface') 2 | 3 | representer = 4 | new( 5 | representer, 6 | { 7 | type = 'general_time', 8 | digits_display = 3, 9 | } 10 | ) 11 | representer:init() 12 | 13 | return 14 | function(n) 15 | return representer:represent(n) 16 | end 17 | -------------------------------------------------------------------------------- /workshop/string/content_attributes.lua: -------------------------------------------------------------------------------- 1 | local has_control_chars = 2 | function(s) 3 | return s:find('%c') and true 4 | end 5 | 6 | local has_backslashes = 7 | function(s) 8 | return s:find([[%\]]) and true 9 | end 10 | 11 | local has_single_quotes = 12 | function(s) 13 | return s:find([[%']]) and true 14 | end 15 | 16 | local has_double_quotes = 17 | function(s) 18 | return s:find([[%"]]) and true 19 | end 20 | 21 | local is_nonascii = 22 | function(s) 23 | return s:find('[^%w%s_%p]') 24 | end 25 | 26 | return 27 | { 28 | has_control_chars = has_control_chars, 29 | has_backslashes = has_backslashes, 30 | has_single_quotes = has_single_quotes, 31 | has_double_quotes = has_double_quotes, 32 | is_nonascii = is_nonascii, 33 | } 34 | -------------------------------------------------------------------------------- /workshop/string/lines/get_next_line.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Get line from given string and position. Return it with next line 3 | position. 4 | 5 | Input 6 | 7 | 8 | 9 | 10 | Output 11 | 12 | 13 | 14 | 15 | Notes 16 | 17 | * next() function format used. 18 | * Special cases handled: 19 | * Last line may not have tail newline. It handled as line 20 | anyway. 21 | * Source line may be empty. (1, ) is returned. 22 | ]] 23 | 24 | return 25 | function(s, start_pos) 26 | start_pos = start_pos or 1 27 | local start, finish, result = s:find('(.-)\n', start_pos) 28 | local new_start 29 | if not start then 30 | if (start_pos <= #s) then 31 | result = s:sub(start_pos) 32 | new_start = #s + 1 33 | end 34 | else 35 | new_start = finish + 1 36 | end 37 | return new_start, result 38 | end 39 | -------------------------------------------------------------------------------- /workshop/string/save_to_file.lua: -------------------------------------------------------------------------------- 1 | local safe_open = request('!.file.safe_open') 2 | 3 | return 4 | function(f_out_name, s) 5 | assert_string(s) 6 | local f_out = safe_open(f_out_name, 'wb') 7 | f_out:write(s) 8 | f_out:close() 9 | end 10 | -------------------------------------------------------------------------------- /workshop/string/trim.lua: -------------------------------------------------------------------------------- 1 | local trim_head = request('trim_head') 2 | local trim_tail = request('trim_tail') 3 | 4 | return 5 | function(s) 6 | return trim_head(trim_tail(s)) 7 | end 8 | -------------------------------------------------------------------------------- /workshop/string/trim_head.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(s) 3 | local result 4 | if (s:sub(1, 1) == ' ') then 5 | local start_pos = 2 6 | while (s:sub(start_pos, start_pos) == ' ') do 7 | start_pos = start_pos + 1 8 | end 9 | result = s:sub(start_pos) 10 | else 11 | result = s 12 | end 13 | return result 14 | end 15 | -------------------------------------------------------------------------------- /workshop/string/trim_linefeed.lua: -------------------------------------------------------------------------------- 1 | local newline_chars = 2 | { 3 | ['\x0d'] = true, 4 | ['\x0a'] = true, 5 | } 6 | 7 | return 8 | function(s) 9 | assert_string(s) 10 | local finish_pos = #s 11 | while newline_chars[s:sub(finish_pos, finish_pos)] do 12 | finish_pos = finish_pos - 1 13 | end 14 | local result = s:sub(1, finish_pos) 15 | return result 16 | end 17 | -------------------------------------------------------------------------------- /workshop/string/trim_tail.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(s) 3 | local result 4 | if (s:sub(-1, -1) == ' ') then 5 | local finish_pos = #s - 1 6 | while (s:sub(finish_pos, finish_pos) == ' ') do 7 | finish_pos = finish_pos - 1 8 | end 9 | result = s:sub(1, finish_pos) 10 | else 11 | result = s 12 | end 13 | return result 14 | end 15 | -------------------------------------------------------------------------------- /workshop/struc/compile.lua: -------------------------------------------------------------------------------- 1 | local unfold = request('!.table.unfold') 2 | 3 | return 4 | function(t, node_handlers) 5 | if is_string(t) then 6 | return t 7 | end 8 | 9 | assert_table(t) 10 | node_handlers = node_handlers or {} 11 | assert_table(node_handlers) 12 | 13 | local result = {} 14 | 15 | local compile 16 | compile = 17 | function(node) 18 | if is_string(node) then 19 | result[#result + 1] = node 20 | elseif is_table(node) then 21 | if node.type and node_handlers[node.type] then 22 | local node_handler = node_handlers[node.type] 23 | assert(node_handler, ('No handler found for type "%s".'):format(node.type)) 24 | result[#result + 1] = node_handler(node) 25 | else 26 | for i = 1, #node do 27 | compile(node[i]) 28 | end 29 | end 30 | end 31 | end 32 | 33 | compile(t) 34 | result = unfold(result) 35 | 36 | return table.concat(result) 37 | end 38 | -------------------------------------------------------------------------------- /workshop/system/install_assert_functions.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Create "assert_" family of global functions. 3 | ]] 4 | 5 | local data_types = request('!.lua.data_types') 6 | 7 | for _, type_name in ipairs(data_types) do 8 | _G['assert_' .. type_name] = 9 | function(a, responsibility_level) 10 | local responsibility_level = (responsibility_level or 1) 11 | if (type(a) ~= type_name) then 12 | error( 13 | ('Argument must have a type "%s", not "%s".'): 14 | format(type_name, type(a)), 15 | responsibility_level + 1 16 | ) 17 | end 18 | end 19 | end 20 | 21 | _G.assert_integer = 22 | function(a, responsibility_level) 23 | local responsibility_level = (responsibility_level or 1) 24 | if (math.type(a) ~= 'integer') then 25 | error( 26 | ('Argument must be integer, not %s.'):format(type(a)), 27 | responsibility_level + 1 28 | ) 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /workshop/system/install_is_functions.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Create "is_" family of global functions. 3 | ]] 4 | 5 | local data_types = request('!.lua.data_types') 6 | 7 | for _, type_name in ipairs(data_types) do 8 | _G['is_' .. type_name] = 9 | function(a) 10 | return (type(a) == type_name) 11 | end 12 | end 13 | 14 | _G.is_integer = 15 | function(n) 16 | return (math.type(n) == 'integer') 17 | end 18 | -------------------------------------------------------------------------------- /workshop/table/as_string.lua: -------------------------------------------------------------------------------- 1 | return request('!.formats.lua_table_code.save') 2 | -------------------------------------------------------------------------------- /workshop/table/clone.lua: -------------------------------------------------------------------------------- 1 | local cloned = {} 2 | 3 | local clone 4 | clone = 5 | function(node) 6 | if (type(node) == 'table') then 7 | if cloned[node] then 8 | return cloned[node] 9 | else 10 | local result = {} 11 | cloned[node] = result 12 | for k, v in pairs(node) do 13 | result[clone(k)] = clone(v) 14 | end 15 | setmetatable(result, getmetatable(node)) 16 | return result 17 | end 18 | else 19 | return node 20 | end 21 | end 22 | 23 | return 24 | function(node) 25 | cloned = {} 26 | return clone(node) 27 | end 28 | 29 | --[[ 30 | * Metatables is shared, not cloned. 31 | 32 | * This code optimized for performance. 33 | 34 | Main effect gave changing "is_table" to explicit type() check. 35 | ]] 36 | -------------------------------------------------------------------------------- /workshop/table/get_key_vals.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(t) 3 | assert_table(t) 4 | local result = {} 5 | for k, v in pairs(t) do 6 | result[#result + 1] = {key = k, value = v} 7 | end 8 | return result 9 | end 10 | -------------------------------------------------------------------------------- /workshop/table/map_values.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(t) 3 | assert_table(t) 4 | local result = {} 5 | for k, v in pairs(t) do 6 | result[v] = true 7 | end 8 | return result 9 | end 10 | -------------------------------------------------------------------------------- /workshop/table/merge.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(t_dest, t_src) 3 | assert_table(t_src) 4 | assert_table(t_dest) 5 | for k, v in pairs(t_src) do 6 | t_dest[k] = v 7 | end 8 | return t_dest 9 | end 10 | -------------------------------------------------------------------------------- /workshop/table/new.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Clone table . Optionally override fields in clone with 3 | fields from . 4 | 5 | Returns cloned table. 6 | ]] 7 | 8 | local clone = request('clone') 9 | local patch = request('patch') 10 | 11 | return 12 | function(base_obj, overriden_params) 13 | assert_table(base_obj) 14 | local result = clone(base_obj) 15 | if is_table(overriden_params) then 16 | patch(result, overriden_params) 17 | end 18 | return result 19 | end 20 | -------------------------------------------------------------------------------- /workshop/table/ordered_pass.lua: -------------------------------------------------------------------------------- 1 | local default_comparator = request('ordered_pass.default_comparator') 2 | local get_key_vals = request('get_key_vals') 3 | 4 | -- Sort and return iterator function to pass that sorted 5 | return 6 | function(t, comparator) 7 | assert_table(t) 8 | comparator = comparator or default_comparator 9 | assert_function(comparator) 10 | 11 | local key_vals = get_key_vals(t) 12 | table.sort(key_vals, comparator) 13 | 14 | local i = 0 15 | local sorted_next = 16 | function() 17 | i = i + 1 18 | if key_vals[i] then 19 | return key_vals[i].key, key_vals[i].value 20 | end 21 | end 22 | 23 | return sorted_next, t 24 | end 25 | -------------------------------------------------------------------------------- /workshop/table/ordered_pass/default_comparator.lua: -------------------------------------------------------------------------------- 1 | local val_rank = 2 | { 3 | string = 1, 4 | number = 2, 5 | other = 3, 6 | } 7 | 8 | local comparable_types = 9 | { 10 | number = true, 11 | string = true, 12 | } 13 | 14 | return 15 | function(a, b) 16 | local a_key = a.key 17 | local a_key_type = type(a_key) 18 | local rank_a = val_rank[a_key_type] or val_rank.other 19 | 20 | local b_key = b.key 21 | local b_key_type = type(b_key) 22 | local rank_b = val_rank[b_key_type] or val_rank.other 23 | 24 | if (rank_a ~= rank_b) then 25 | return (rank_a < rank_b) 26 | else 27 | if comparable_types[a_key_type] and comparable_types[b_key_type] then 28 | return (a_key < b_key) 29 | else 30 | return (tostring(a_key) < tostring(b_key)) 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /workshop/table/patch.lua: -------------------------------------------------------------------------------- 1 | local patch 2 | patch = 3 | function(t_src, t_patch) 4 | for k, v in pairs(t_patch) do 5 | if (t_src[k] == nil) then 6 | local err_msg = 7 | ('Destination table dont have key "%s".'):format(tostring(k)) 8 | error(err_msg, 2) 9 | end 10 | if is_table(t_src[k]) and is_table(v) then 11 | patch(t_src[k], v) 12 | else 13 | t_src[k] = v 14 | end 15 | end 16 | end 17 | 18 | return patch 19 | -------------------------------------------------------------------------------- /workshop/table/replace.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(dest, src) 3 | assert_table(dest) 4 | assert_table(src) 5 | assert(src ~= dest) 6 | for k in pairs(dest) do 7 | dest[k] = nil 8 | end 9 | for k, v in pairs(src) do 10 | dest[k] = v 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /workshop/table/unfold.lua: -------------------------------------------------------------------------------- 1 | return 2 | function(t) 3 | assert_table(t) 4 | local result = {} 5 | 6 | local unfold 7 | unfold = 8 | function(node) 9 | for i = 1, #node do 10 | if is_table(node[i]) then 11 | unfold(node[i]) 12 | else 13 | result[#result + 1] = node[i] 14 | end 15 | end 16 | end 17 | 18 | unfold(t) 19 | 20 | return result 21 | end 22 | --------------------------------------------------------------------------------