{"id":15974495,"url":"https://github.com/alex-mashin/formatterii","last_synced_at":"2026-02-04T06:40:54.514Z","repository":{"id":109043013,"uuid":"470657146","full_name":"alex-mashin/FormatterII","owner":"alex-mashin","description":"A Lua table formatting tool","archived":false,"fork":false,"pushed_at":"2025-01-08T16:50:03.000Z","size":164,"stargazers_count":1,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-31T18:51:18.291Z","etag":null,"topics":["formatter","lua","lua-library","template-engine"],"latest_commit_sha":null,"homepage":"","language":"Lua","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/alex-mashin.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2022-03-16T16:08:22.000Z","updated_at":"2025-01-08T16:50:06.000Z","dependencies_parsed_at":null,"dependency_job_id":"3a1f1337-7ea1-494e-9b7f-bce94629b5a0","html_url":"https://github.com/alex-mashin/FormatterII","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alex-mashin%2FFormatterII","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alex-mashin%2FFormatterII/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alex-mashin%2FFormatterII/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alex-mashin%2FFormatterII/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/alex-mashin","download_url":"https://codeload.github.com/alex-mashin/FormatterII/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253155547,"owners_count":21862717,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["formatter","lua","lua-library","template-engine"],"created_at":"2024-10-07T21:42:11.681Z","updated_at":"2026-02-04T06:40:49.457Z","avatar_url":"https://github.com/alex-mashin.png","language":"Lua","funding_links":[],"categories":[],"sub_categories":[],"readme":"# FormatterII\n\n*Version 0.3*\n\n*FormatterII* is a Lua table querying and formatting tool, in other words, a [template engine](https://en.wikipedia.org/wiki/Template_processor).\n\n## Features\n*FormatterII* can:\n- query table fields fuzzily, using various regular expressions to filter table fields and values, so that:\n  - it is not necessary to know table keys up to capitalisation, spaces, hyphens and underscores and\n  - item values can be validated before output,\n- parse strings for further re-rendering,\n- handle missing data gracefully, avoiding rendering unnecessary headings for empty lists,\n- avoid dangling commas (or other separators),\n- keep track of data supplied but never output,\n- be flexibly configured, changing the very syntax of the format string,\n- be run in *MediaWiki* *Scribunto* environment, provided that coroutines, *LPEG* and, optionally, regular expression engines from *lrexlib* are available (see [my fork](https://github.com/alex-mashin/php-luasandbox) of *luasandbox* extension for PHP).\n\n*FormatterII*'s syntax of format strings aims to be concise and natural, as similar to the resulting formatted string, and with as little boilerplate code, as possible; yet flexible and powerful.\n\n*FormatterII* is fully declarative. The only way to run arbitrary Lua code is to add a member function to the formatted table and invoke it from the template.\n\n## Requirements\n- Lua 5.1, 5.2, 5.3, 5.4 or LuaJIT,\n - `coroutine` table ought to be available,\n- Lua [LPEG](http://www.inf.puc-rio.br/~roberto/lpeg/) library,\n- [lrexlib](https://github.com/rrthomas/lrexlib) (optional; needed for GNU, Oniguruma, POSIX, PCRE, PCRE2 and Tre regular expressions). Note that PCRE2 regular expressions are not available, *FormatterII* will attempt to use PCRE, and vice versa.\n\n## Usage\n- `local formatter = require 'FormatterII'` — require library,\n- `formatter.format (format_string, ...)` — format \t`...` values according to `format_string`,\n- `local func = formatter.formatter (format_string); local formatted = func (table)` — create a function `func` formatting its argument according to `format_string` and then call it,\n- `local config = formatter.config` — get *FormatterII* configuration (mainly, syntax),\n- `formatter.config.open, formatter.config.close = '『', '』'` — use Chinese quotation marks for macro delimiters instead of `\u003c\u003c` and `\u003e\u003e`,\n- `formatter.config.string = mw.ustring` — replace the standard Lua `string` library with [Scribunto](https://www.mediawiki.org/wiki/Extension:Scribunto)'s `mw.ustring`,\n- `formatter.initialise()` — activate changes in *FormatterII* configuration.\n\n## Format string syntax\n### Format string\nA *format string* is a plain literal text with optional embedded *macros* (`\u003c\u003c…\u003e\u003e`). Each literal chunk between macros is treated as a `printf()`-style format that is passed to [`string.format ()`](http://www.lua.org/manual/5.1/manual.html#pdf-string.format) function; in particular, if the string is a plain literal, it is returned as is.\n\nIf a macro returns `nil`, then the whole format string will return `nil` and this will be propagated all the way up. But an *optional macro* (`\u003c\u003c?…\u003e\u003e`) will return an empty string instead of `nil`.\n\nAny format string has a *context*: the outermost macro's context is the first argument to `format()`; and non-empty selectors move context to the selected fields.\n\n### Macro\nMacro syntax can be:\n- `\u003c\u003cselector|format string 1|format string 2|…|format string n\u003e\u003e` — each value yielded by `selector` will be formatted according to the first `format string` that does not return `nil`. This can be used to define fallbacks for absent values,\n- `\u003c\u003c|format string 1|format string 2|…|format string n\u003e\u003e` — no selector means that the value itself rather than its fields will be output,\n- `\u003c\u003cselector\u003e\u003e` — no format means that `selector` values will be output as they are,\n- `\u003c\u003c\u003e\u003e` — the current value will be output as is,\n- `\u003c\u003c,\u003e\u003e` — the default separator (`p.config.separator = ', '`) will be output between the formatted values yielded by the selector of the containing macro,\n- `\u003c\u003c,|; \u003e\u003e` — a custom separator (`; ` in this case) will be output, if required,\n- `\u003c\u003c?selector…\u003e\u003e` (*optional macro*) — if `selector` yields nothing, the macro will not fail, producing an empty string instead, and not causing the enclosing format string to fail,\n- `\u003c\u003c!\u003e\u003e` (*conditional macro*) — if the selector yields nothing, macro containing `\u003c\u003c!\u003e\u003e` will fail, if even the format containing `\u003c\u003c!\u003e\u003e` is constant. If selector yields data, `\u003c\u003c!\u003e\u003e` will display an empty string,\n- `\u003c\u003c!1|format string\u003e\u003e` — if `format string` repeats, macro that contains it will fail. This can be used to prevent repetition of values (emulate a unique selector) (likely to be replaced with a different implementation).\n\n### Selector\nA selector can be:\n- *simple*. The new context will be created by captures. Below are subtypes of simple selectors:\n  - `\u003c\u003c\u003e\u003e`, meaning the formatted value itself and not changing the context,\n  - `\u003c\u003ckey…\u003e\u003e` or `\u003c\u003c'key'…\u003e\u003e` or `\u003c\u003c\"key\"…\u003e\u003e` — a key to the table. If a key is absent, it will be looked all the way up in the parent tables, to the globals table `_G`. In particular:\n    - `\u003c\u003c__unused\u003e\u003e` is a table of values that were never output,\n    - `\u003c\u003c..\u003e\u003e` is the parent table.\n    - `\u003c\u003c@\u003e\u003e` is the key of the currently selected value in the parent table.\n    - `\u003c\u003c@@\u003e\u003e` is the counter of the current row returned by table iterator. It can be used to distinguish table items by some natural order, in which they are expected.\n  - `\u003c\u003cdynamic key…\u003e\u003e` — a key to the table that can include macros. The corresponding value will be yielded. If the key is absent, it will be looked all the way up in the parent tables, to the globals table `_G`:\n    - if the returned value is userdata containing a compiled LPEG pattern or grammar or a regular expression from lrexlib, it will be used as `lpeg/…/` or `flavoured_regex/…/` selector respectively, i.e., the table will be matched against it. This allows to reuse complex LPEG or regex patterns,\n  - *regular expression*. At least, two flavours (standard Lua and LPEG Re) are available, and more can be made available with *[lrexlib](https://github.com/rrthomas/lrexlib)*. Any pair of matching non-space non-alphanumeric characters can delimit a regular expression, except characters that have a special meaning in the selector syntax (`().:*+-,\\|@`); and except quotes (`'\"`), if regular expression flavour is not specified, the default one is to be used; but a quoted string without a flavour prefix will be treated as a plain table key. Below, `//` are used as an example of regular expression delimiters. The possible flags include `AiDsxXmUu_`, but some of them may be unavailable to a certain flavour. All regular expression flavours support `i` flag for case-insensitive matching and a the non-standard *condense* flag (`_`) meaning that spaces, hyphens and underscores will be ignored:\n    - `\u003c\u003c/regular expression/flags…\u003e\u003e` — a regular expression of the flavour set by `formatter.config.regex`, by default, `pcre2`, falling back to `pcre`, if available. Quotes `'\"` cannot be used to delimit a regular expression of the default flavour,\n    - `\u003c\u003cpcre2/regular expression/flags…\u003e\u003e` or `\u003c\u003cpcre2'regular expression'flags…\u003e\u003e` or `\u003c\u003cpcre2\"regular expression\"flags…\u003e\u003e` — a [Perl-compatible regular expression v2](https://www.pcre.org/current/doc/html/), falling back to `pcre`, if available,\n    - `\u003c\u003cpcre/regular expression/flags…\u003e\u003e` or `\u003c\u003cpcre'regular expression'flags…\u003e\u003e` or `\u003c\u003cpcre\"regular expression\"flags…\u003e\u003e` — a [Rerl-compatible regular expression v1](https://www.pcre.org/original/doc/html/), falling back to `pcre2`, if available,\n    - `\u003c\u003cgnu/regular expression/flags…\u003e\u003e` or `\u003c\u003cgnu'regular expression'flags…\u003e\u003e` or `\u003c\u003cgnu\"regular expression\"flags…\u003e\u003e` — an extended [GNU-compatible regular expression](https://www.gnu.org/software/grep/manual/html_node/Regular-Expressions.html), if available,\n    - `\u003c\u003conig/regular expression/flags…\u003e\u003e` or `\u003c\u003conig'regular expression'flags…\u003e\u003e` or `\u003c\u003conig\"regular expression\"flags…\u003e\u003e` — an [Oniguruma regular expression](https://github.com/kkos/oniguruma/blob/master/doc/RE), if available, subject to features implemented in *lrexlib*,\n    - `\u003c\u003cposix/regular expression/flags…\u003e\u003e` or `\u003c\u003cposix'regular expression'flags…\u003e\u003e` or `\u003c\u003cposix\"regular expression\"flags…\u003e\u003e` — an extended [POSIX regular expression](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap09.html), if available,\n    - `\u003c\u003ctre/regular expression/flags…\u003e\u003e` or `\u003c\u003ctre'regular expression'flags…\u003e\u003e` or `\u003c\u003ctre\"regular expression\"flags…\u003e\u003e` — an extended [TRE regular expression](https://github.com/laurikari/tre), if available, subject to features implemented in *lrexlib*,\n    - `\u003c\u003clua/regular expression/flags…\u003e\u003e` or `\u003c\u003clua'regular expression'flags…\u003e\u003e` or `\u003c\u003clua\"regular expression\"flags…\u003e\u003e` — a [standard Lua regular expression](https://www.lua.org/pil/20.2.html),\n    - `\u003c\u003cre/regular expression/flags…\u003e\u003e` or `\u003c\u003cre'regular expression'flags…\u003e\u003e` or `\u003c\u003cre\"regular expression\"flags…\u003e\u003e` — an LPEG [Re](http://www.inf.puc-rio.br/~roberto/lpeg/re.html) \"regular expression\", with some additional features:\n      - `\u003c` — back assertion (`lpeg.B`),\n      - ``{` `}`` — constant capture (`lpeg.Cc`),\n      - `{# #}` — argument capture (`lpeg.Carg`),\n      - `{flavour/regex/flags}` — the following text will be matched against `flavour` regular expression with `flags`. If match fails, so will the re selector or its part. If match succeeds, position in the string will advance, and all captures from the regular expression will be captured by re as well, but named captures will become anonymous. If `flavour` is omitted, the default flavour from `formatter.config.regex` will be used,\n      - optional `i` flag for case insensitive matching;\n  - *iterating*:\n    - `\u003c\u003c#…\u003e\u003e` for `ipairs()`,\n    - `\u003c\u003c$…\u003e\u003e` for `pairs()` but ordered by keys;\n  - *function*: `\u003c\u003cfunc (param1, …, paramn)…\u003e\u003e` will call `func` field of the type `function` of the formatted value, passing to it the formatted value, `param1`, …, `paramn`, and finally, the whole table, and producing the returned value of the function;\n- *composite*, ordered by priority, from highest to lowest (order of composition can be changed by parentheses; priorities and operators are configurable via `formatter.config.operators`):\n  - `\u003c\u003cselector1 selector2…\u003e\u003e` — an intersection of `selector1` and `selector2`,\n  - `\u003c\u003cselector1 . selector2…\u003e\u003e` — `selector2` applied to each values returned by `selector1`,\n  - `\u003c\u003cselector1 : selector2…\u003e\u003e` — `selector1` filtered by `selector2`. Useful in the cases, when it is impossible or difficult to integrate that filter into `selector1`, e.g., `: @@ = 2` to show only the second row returned by `selector1`,\n  - `\u003c\u003cselector1 * selector2…\u003e\u003e` — a Cartesian product of `selector1` and `selector2`,\n  - `\u003c\u003cselector1 + selector2…\u003e\u003e` — values returned by `selector1`, followed by values of `selector2`,\n  - `\u003c\u003cselector1 - selector2…\u003e\u003e` — values returned by `selector1`, except values returned by `selector2`,  \n  - `\u003c\u003cselector1 , selector2…\u003e\u003e` — values returned by `selector1`, if any; otherwise values of `selector2`.\n\nIf a selector (except iterating ones) is preceded with an equal sign, it is applied to table values, not keys. This makes the following syntax possible: `\u003c\u003ckey selector = value selector…\u003e\u003e`.\n\n### Configuration\n`formatter.config` contains a table of library configuration variables, mainly describing format string syntax.\n\nDefault value:\n\n```lua\nformatter.config = {\n\tstring\t\t= string,\t\t-- string library to use.\n\tcondense\t= '_',\t\t\t-- \"condense\" (ignore whitespaces, hyphens and underscores) flag.\n\tfillers\t\t= '[-_%s]',\t\t-- characters to ignore when the condense flag is used.\n\tconditional\t= '!',\t\t\t-- conditional macro flag.\n\toptional\t= '?',\t\t\t-- optional macro flag.\n\tseparator\t= ',',\t\t\t-- separator macro flag.\n\tdefault_separator\n\t\t\t\t= ', ',\t\t\t-- default separator.\n\tkey\t\t\t= '@',\t\t\t-- key selector.\n\tcounter\t\t= '@@',\t\t\t-- iterator counter.\n\tself\t\t= '',\t\t\t-- self selector.\n\tparent\t\t= '..',\t\t\t-- parent selector.\n\tunused\t\t= '__unused',\t-- a table of unused items.\n\tescape\t\t= '\\\\',\t\t\t-- escape character.\n\topen\t\t= '\u003c\u003c',\t\t\t-- macro start.\n\tpipe\t\t= '|',\t\t\t-- separator between selector and format string, or between format string and fallback format string.\n\tclose\t\t= '\u003e\u003e',\t\t\t-- macro end.\n\tunique\t\t= '!1',\t\t\t-- unique selector for unrepeatable formats.\n\toperators\t= {\t\t\t\t-- operators over selectors' symbols and priorities.\n\t\t{ ['']\t= 'intersect' },\n\t\t{ ['.']\t= 'enter' },\n\t\t{ [':'] = 'filter' },\n\t\t{ ['*']\t= 'cartesian' },\n\t\t{ ['+']\t= 'union' },\n\t\t{ ['-'] = 'except' },\n\t\t{ [',']\t= 'first' }\n\t},\n\tipairs\t\t= '#',\t\t\t-- ipairs() selector.\n\tpairs\t\t= '$',\t\t\t-- pairs() selector.\n\tregex\t\t= 'pcre2',\t\t-- the default regular expression flavour.\n\tregex_jit\t= true,\t\t\t-- load libraries from lrexlib at first use.\n\tre\t\t\t= { 'lualibs/re', 'Module:Re' }\n\t\t\t\t\t\t\t\t-- path to re Lua library.\n}\n```\n\nAfter changing configuration, call `formatter.initialise()`.\n\n### Examples\n| Description | Format | Result |\n| --- | --- | --- |\n| **Constant format** |\n| Present value, constant format | `const string` | const string |\n| Absent value, constant format | `const string` | const string |\n| **Plain format** |\n| Present item, plain format | `\u003c\u003ckey\u003e\u003e` | value |\n| Present item, plain format, prefix | `\"key\" is \"\u003c\u003ckey\u003e\u003e\"` | \"key\" is \"value\" |\n| Absent item, plain format | `\u003c\u003ckey\u003e\u003e` | nil |\n| Absent item, plain format, prefix | `\"key\" is \"\u003c\u003ckey\u003e\u003e\"` | nil |\n| Plain format with escaped special character | `The value is \\\\|\u003c\u003ckey\u003e\u003e\\\\|` | The value is \\|Value\\| |\n| `\u003c\u003c\u003e\u003e`, present | `Value is \"\u003c\u003c\u003e\u003e\"` | Value is \"Some value\" |\n| `\u003c\u003c\u003e\u003e`, nil | `Value is \u003c\u003c\u003e\u003e` | nil |\n| `\u003c\u003c\u003e\u003e`, present, const format | `Value is \u003c\u003c\\|\"there is some value\"\u003e\u003e` | Value is \"there is some value\" |\n| `\u003c\u003c\u003e\u003e`, present, header and footer in macro | `\u003c\u003c\\|the value is \"\u003c\u003c\u003e\u003e\"\u003e\u003e` | the value is \"Some value\" |\n| `\u003c\u003c\u003e\u003e`, present, nested header and footer | `They say \u003c\u003c\\|the value is \"\u003c\u003c\u003e\u003e\"\u003e\u003e` | They say the value is \"Some value\" |\n| `\u003c\u003c\u003e\u003e`, present, header and footer | `Header - \u003c\u003c\u003e\u003e - Footer` | Header - Some value - Footer |\n| `\u003c\u003c\u003e\u003e`, nil, header and footer | `Header - \u003c\u003c\u003e\u003e - Footer` | nil |\n| `\u003c\u003c\\|\u003e\u003e`, nil, header and footer | `\u003c\u003c\\|Header \u003c\u003c\u003e\u003e Footer\u003e\u003e` | nil |\n| **Fallbacks** |\n| Absent value with fallback | `\u003c\u003ckey\\|\u003c\u003c\u003e\u003e\\|fallback\u003e\u003e` | fallback |\n| Absent value with empty fallback | `\u003c\u003ckey\\|\u003c\u003c\u003e\u003e\\|\u003e\u003e` |  |\n| Absent value with fallback, short syntax | `\u003c\u003c?key\\|fallback\u003e\u003e` | fallback |\n| Absent value with empty fallback, short syntax | `\u003c\u003c?key\u003e\u003e` |  |\n| Present value with empty fallback | `\u003c\u003ckey\\|\u003c\u003c\u003e\u003e\\|\u003e\u003e` | Value |\n| Present value with empty fallback: short form | `\u003c\u003c?key\u003e\u003e` | Value |\n| Present value with non-empty fallback | `\u003c\u003ckey\\|\u003c\u003c\u003e\u003e\\|Fallback\u003e\u003e` | Value |\n| Present value with non-empty fallback: short form | `\u003c\u003c?key\\|Fallback\u003e\u003e` | Value |\n| Absent value with fallback, prefix and suffix | `\u003c\u003ckey\\|Header \u003c\u003c\u003e\u003e footer\\|fallback\u003e\u003e` | fallback |\n| Absent value with empty fallback, prefix and suffix | `\u003c\u003ckey\\|Header \u003c\u003c\u003e\u003e footer\\|\u003e\u003e` |  |\n| Present value with empty fallback, prefix and suffix | `\u003c\u003ckey\\|Header \u003c\u003c\u003e\u003e footer\\|\u003e\u003e` | Header Value footer |\n| Present value with non-empty fallback, prefix and suffix | `\u003c\u003ckey\\|Header \u003c\u003c\u003e\u003e footer\\|Fallback\u003e\u003e` | Header Value footer |\n| Empty and non-empty | `\u003c\u003ckey\u003e\u003e, \u003c\u003citem\u003e\u003e` | nil |\n| Optional empty and non-empty | `\u003c\u003ckey\\|\u003c\u003c\u003e\u003e\\|\u003e\u003e, \u003c\u003citem\\|\u003c\u003c\u003e\u003e\\|\u003e\u003e` | value,  |\n| Optional empty and non-empty: short syntax | `\u003c\u003c?key\u003e\u003e, \u003c\u003c?item\u003e\u003e` | value,  |\n| At least one; one present | `\u003c\u003ckey1 + key2\u003e\u003e` | Value1 |\n| At least one; two present | `\u003c\u003ckey1 + key2\\|\u003c\u003c\u003e\u003e\u003c\u003c,\u003e\u003e\u003e\u003e` | Value1, Value2 |\n| At least one; absent | `\u003c\u003ckey1 + key2\u003e\u003e` | nil |\n| At least one; one present; prefix | `\u003c\u003ckey1 + key2\\|Header \u003c\u003c\u003e\u003e\u003e\u003e` | Header Value1 |\n| At least one; absent; prefix | `\u003c\u003ckey1 + key2\\|Header \u003c\u003c\u003e\u003e\u003e\u003e` | nil |\n| At least one; two present; prefix | `\u003c\u003c\\|Header: \u003c\u003ckey1 + key2\\|\u003c\u003c\u003e\u003e\u003c\u003c,\u003e\u003e\u003e\u003e\u003e\u003e` | Header: Value1, Value2 |\n| **Conditional format** |\n| Conditional constant; present | `\u003c\u003ckey\\|\u003c\u003c!\u003e\u003econst string\u003e\u003e` | const string |\n| Conditional constant; absent | `\u003c\u003ckey\\|\u003c\u003c!\u003e\u003econst string\u003e\u003e` | nil |\n| Conditional constant; absent; fallback | `\u003c\u003ckey\\|\u003c\u003c!\u003e\u003econst string\\|fallback\u003e\u003e` | fallback |\n| Conditional expression, first option | `\u003c\u003ckey = value1\\|\u003c\u003c!\u003e\u003eyes\\|no\u003e\u003e` | yes |\n| Conditional expression, second option | `\u003c\u003ckey = value1\\|\u003c\u003c!\u003e\u003eyes\\|no\u003e\u003e` | no |\n| Conditional separator: a and b | `\u003c\u003c\\|\u003c\u003ca\u003e\u003e: \u003c\u003cb\u003e\u003e\\|\u003c\u003ca\u003e\u003e\\|\u003c\u003cb\u003e\u003e\u003e\u003e` | A: B |\n| Conditional separator: a, no b | `\u003c\u003c\\|\u003c\u003cb\u003e\u003e\u003e\u003e` | nil |\n| Conditional separator: a, no b | `\u003c\u003c\\|\u003c\u003ca\u003e\u003e: \u003c\u003cb\u003e\u003e\\|\u003c\u003ca\u003e\u003e\\|\u003c\u003cb\u003e\u003e\u003e\u003e` | A |\n| Conditional separator: no a, b | `\u003c\u003c\\|\u003c\u003ca\u003e\u003e: \u003c\u003cb\u003e\u003e\\|\u003c\u003ca\u003e\u003e\\|\u003c\u003cb\u003e\u003e\u003e\u003e` | B |\n| Conditional separator: no a and no b | `\u003c\u003c\\|\u003c\u003ca\u003e\u003e: \u003c\u003cb\u003e\u003e\\|\u003c\u003ca\u003e\u003e\\|\u003c\u003cb\u003e\u003e\u003e\u003e` | nil |\n| Conditional separator: a and b, short form | `\u003c\u003c?a\u003e\u003e\u003c\u003ca * b\\|\u003c\u003c!\u003e\u003e: \\|\u003e\u003e\u003c\u003c?b\u003e\u003e` | A: B |\n| Conditional separator: a, short form | `\u003c\u003c?a\u003e\u003e\u003c\u003ca * b\\|\u003c\u003c!\u003e\u003e: \\|\u003e\u003e\u003c\u003c?b\u003e\u003e` | A |\n| Conditional separator: b, short form | `\u003c\u003c?a\u003e\u003e\u003c\u003ca * b\\|\u003c\u003c!\u003e\u003e: \\|\u003e\u003e\u003c\u003c?b\u003e\u003e` | B |\n| **Iteration** |\n| `@` numeric | `\u003c\u003c1\\|\u003c\u003c@\u003e\u003e: key = \u003c\u003ckey\u003e\u003e\u003e\u003e` | 1: key = value |\n| `\u003c\u003c#\u003e\u003e`, no separator | `\u003c\u003c#\u003e\u003e` | Onetwothree |\n| `\u003c\u003c#\u003e\u003e`, default separator | `\u003c\u003c#\\|\u003c\u003c\u003e\u003e\u003c\u003c,\u003e\u003e\u003e\u003e` | One, two, three |\n| `\u003c\u003c#\u003e\u003e`, custom separator | `\u003c\u003c#\\|\u003c\u003c\u003e\u003e\u003c\u003c,\\|; \u003e\u003e\u003e\u003e` | One; two; three |\n| `\u003c\u003c$\u003e\u003e`, default separator | `\u003c\u003c$\\|\u003c\u003c\u003e\u003e\u003c\u003c,\u003e\u003e\u003e\u003e` | one, three, two |\n| `\u003c\u003c$\u003e\u003e`, custom separator | `\u003c\u003c$\\|\u003c\u003c\u003e\u003e\u003c\u003c,\\|; \u003e\u003e\u003e\u003e` | one; three; two |\n| `\u003c\u003c#\u003e\u003e`, {} | `\u003c\u003c#\u003e\u003e` | nil |\n| `\u003c\u003c#\\|format\u003e\u003e` | `\u003c\u003c#\\|\u003c\u003c\u003e\u003e, \u003e\u003e` | One, two, three,  |\n| `\u003c\u003c1\\|format\u003e\u003e`, 2D | `\u003c\u003c1\\|Numeral: \u003c\u003cnumeral\u003e\u003e, ordinal: \u003c\u003cordinal\u003e\u003e, \u003e\u003e` | Numeral: one, ordinal: first,  |\n| `\u003c\u003c#\\|format\u003e\u003e`, 2D | `\u003c\u003c#\\|Numeral: \u003c\u003cnumeral\u003e\u003e, ordinal: \u003c\u003cordinal\u003e\u003e, \u003e\u003e` | Numeral: one, ordinal: first, Numeral: two, ordinal: second, Numeral: three, ordinal: third,  |\n| `\u003c\u003c#\\|format\u003e\u003e`, 2D, custom separator | `\u003c\u003c#\\|Numeral: \u003c\u003cnumeral\u003e\u003e, ordinal: \u003c\u003cordinal\u003e\u003e\u003c\u003c,\\|; \u003e\u003e\u003e\u003e` | Numeral: one, ordinal: first; Numeral: two, ordinal: second; Numeral: three, ordinal: third |\n| numeric key | `\u003c\u003c1\\|some table\u003e\u003e` | some table |\n| `@` numeric | `\u003c\u003c1\\|\u003c\u003c@\u003e\u003e\u003e\u003e` | 1 |\n| `\u003c\u003c#\\|format\u003e\u003e`, 2D, header, `\u003c\u003c@\u003e\u003e` | `\u003c\u003c\\|One to three: \u003c\u003c#\\|\u003c\u003c@\u003e\u003e: Numeral: \u003c\u003cnumeral\u003e\u003e, ordinal: \u003c\u003cordinal\u003e\u003e, \u003e\u003e\u003e\u003e` | One to three: 1: Numeral: one, ordinal: first, 2: Numeral: two, ordinal: second, 3: Numeral: three, ordinal: third,  |\n| `\u003c\u003c#.ordinal\u003e\u003e` | `\u003c\u003c#.ordinal\\|\u003c\u003c\u003e\u003e, \u003e\u003e` | first, second, third,  |\n| `\u003c\u003c#\\|format\u003e\u003e`, 2D, header, `{}` | `\u003c\u003c\\|One to three: \u003c\u003c#\\|Numeral: \u003c\u003cnumeral\u003e\u003e, cardinal: \u003c\u003cordinal\u003e\u003e, \u003e\u003e\u003e\u003e` | nil |\n| `\u003c\u003c#\\|format\u003e\u003e`, 2D, header, `{}`, fallback | `\u003c\u003c\\|One to three: \u003c\u003c#\\|Numeral: \u003c\u003cnumeral\u003e\u003e, cardinal: \u003c\u003cordinal\u003e\u003e, \u003e\u003e\\|No items\u003e\u003e` | No items |\n| **Selectors** |\n| Single-quoted key | `\u003c\u003c'key'\u003e\u003e` | Value |\n| Double-quoted key | `\u003c\u003c\"key\"\u003e\u003e` | Value |\n| Single-quoted key with spaces | `\u003c\u003c'some key'\u003e\u003e` | Some value |\n| Dynamic key | `\u003c\u003ckey\u003c\u003cwhich\u003e\u003e\u003e\u003e` | Value |\n| /GNU/ | `\u003c\u003cgnu/^key[0-9]+/\u003e\u003e` | Value |\n| /ONIG/ | `\u003c\u003conig/^key[0-9]+/\u003e\u003e` | Value |\n| /POSIX/ | `\u003c\u003cposix/^key[0-9]+/\u003e\u003e` | Value |\n| /PCRE/ | `\u003c\u003c/^key(?\u003cno\u003e\\d+)$/\u003e\u003e` | Value |\n| /TRE/ | `\u003c\u003ctre/^(key){~1}/\u003e\u003e` | Value |\n| /PCRE/i | `\u003c\u003c/^key(?\u003cno\u003e\\d+)$/i\u003e\u003e` | Value |\n| /PCRE/_ | `\u003c\u003c/^key(?\u003cno\u003e\\d+)$/_\u003e\u003e` | Value |\n| /PCRE/i_ | `\u003c\u003c/^key(?\u003cno\u003e\\d+)$/i_\u003e\u003e` | Value |\n| /PCRE/ and @ | `\u003c\u003c/^key(?\u003cno\u003e\\d+)$/\\|\u003c\u003c@\u003e\u003e: \u003c\u003cno\u003e\u003e - \u003c\u003c\u003e\u003e, \u003e\u003e` | key1: 1 - Value1, key3: 3 - Value3, key2: 2 - Value2,  |\n| pcre2\"PCRE\" | `\u003c\u003cpcre2\"^key(?\u003cno\u003e\\d+)$\"\u003e\u003e` | Value |\n| pcre\"PCRE\" | `\u003c\u003cpcre\"^key(?\u003cno\u003e\\d+)$\"\u003e\u003e` | Value |\n| pcre/PCRE/ | `\u003c\u003cpcre2/^key(?\u003cno\u003e\\d+)$/\u003e\u003e` | Value |\n| Absent PCRE key | `\u003c\u003c/^key(?\u003cno\u003e\\d+)$/\u003e\u003e` | nil |\n| Broken PCRE | `\u003c\u003c/^key(?\u003cno\u003e\\d+$/\u003e\u003e` | pcre2 regular expression \"^key(?\u003cno\u003e\\d+$\" with flags \"\" does not compile: missing closing parenthesis (pattern offset: 15) |\n| Re key, re// | `\u003c\u003cre/\"key\" { [0-9]+ }/\u003e\u003e` | Value |\n| Re with PCRE | `\u003c\u003cre~\"key\" {/\\d+/}~\u003e\u003e` | Value1 |\n| Re key, re'' | `\u003c\u003cre'\"key\" { [0-9]+ }'\u003e\u003e` | Value |\n| Re key, re'', case-insensitive | `\u003c\u003cre'\"key\" { [0-9]+ }'i\u003e\u003e` | Value |\n| Absent re key, re'', case-sensitive | `\u003c\u003cre'\"key\" { [0-9]+ }'\u003e\u003e` | nil |\n| Broken re | `\u003c\u003cre/\"key\" {: [0-9]+ }/\u003e\u003e` | LPEG Re selector \"key\" {: [0-9]+ } does not compile: pattern error near ': [0-9]+ }' |\n| Re key, named capture | `\u003c\u003cre/\"key\" {:no: [0-9]+ :}/\\|\u003c\u003cno\u003e\u003e: \u003c\u003c\u003e\u003e\u003e\u003e` | 1: Value |\n| Absent re key | `\u003c\u003cre/\"key\" { [0-9]+ }/\u003e\u003e` | nil |\n| PCRE // key and \u003c\u003c\u003e\u003e | `\u003c\u003c/^key(?\u003cno\u003e\\d+)$/\\|\u003c\u003c@\u003e\u003e: \u003c\u003c\u003e\u003e, \u003e\u003e` | key1: Value1, key2: Value2,  |\n| lua'pattern' | `\u003c\u003clua'key%d+'\u003e\u003e` | Value |\n| lua/pattern/ | `\u003c\u003clua/key%d+/\u003e\u003e` | Value |\n| lua'pattern', case-insensitive | `\u003c\u003clua'key%d+'i\u003e\u003e` | Value |\n| Absent lua'pattern', case-sensitive | `\u003c\u003clua'key%d+'\u003e\u003e` | nil |\n| Unique constraint | `\u003c\u003c/^key\\d+$/\\|value is \u003c\u003c\u003e\u003e\u003c\u003c,\u003e\u003e\u003c\u003c!1\\|\u003c\u003c\u003e\u003e\u003e\u003e\u003e\u003e` | value is Value1, value is Value2 |\n| **Nested tables** |\n| Nested tables | `\u003c\u003ckey.item\u003e\u003e` | Value |\n| Nested tables, regexes | `\u003c\u003c/^key$/./^item$/\u003e\u003e` | Value |\n| Nested tables, outer absent | `\u003c\u003citem.item\u003e\u003e` | nil |\n| Nested tables, upper level as fallback | `\u003c\u003ckey\\|\u003c\u003citem\u003e\u003e, \u003c\u003cdesc\u003e\u003e\u003e\u003e` | Value, Description |\n| Filter and counter | `\u003c\u003c/^\\d+$/ = /^\\d+\\s*(px)?$/ : @@ = 2 \\|Height: \u003c\u003c\u003e\u003e\u003e\u003e` | Height: 50px |\n| **Functions** |\n| Function | `\u003c\u003ceven().#\u003e\u003e` | 102030 |\n| Function with (parameter) | `\u003c\u003cdivisible_by (3).#\u003e\u003e` | 1530 |\n| Function with (\u003c\u003cparameter\u003e\u003e) | `\u003c\u003cdivisible_by (\u003c\u003cdivider\u003e\u003e).#\u003e\u003e` | 1530 |\n| **Composition** |\n| Composed selectors | `\u003c\u003c/^key/ /\\d$/\u003e\u003e` | Value1 |\n| Composed selectors, parentheses not changing the order of operations | `\u003c\u003c(/^key/ /\\d$/)\u003e\u003e` | Value1 |\n| Composition of two equal keys | `\u003c\u003c\"key\" \"key\"\u003e\u003e` | Value |\n| key = value | `\u003c\u003ckey = Value\u003e\u003e` | Value |\n| /PCRE/ = value | `\u003c\u003c/^key\\d+$/ = Value1\u003e\u003e` | Value1 |\n| = /pcre/ | `\u003c\u003c= /^Value\\d+$/\\|\u003c\u003c\u003e\u003e\u003c\u003c,\u003e\u003e\u003e\u003e` | Value1, Value2 |\n| /PCRE/ = /pcre/ | `\u003c\u003c/^key\\d+$/ = /^Value\\d+$/\u003e\u003e` | Value1 |\n| Union all | `\u003c\u003c ( set1 + set2 ).# \\|\u003c\u003c\u003e\u003e\u003c\u003c,\u003e\u003e\u003e\u003e` | Value10, Value11, Value20, Value21 |\n| Filter | `\u003c\u003c/^key/ := /^Mediocre\\|Acceptable\\|Good\\|Excellent$/ \\|\u003c\u003c\u003e\u003e\u003c\u003c,\u003e\u003e\u003e\u003e` | Good, Excellent, Mediocre |\n| Exception | `\u003c\u003c/^key/ -= Bad \\|\u003c\u003c\u003e\u003e\u003c\u003c,\u003e\u003e\u003e\u003e` | Good, Excellent, Mediocre |\n| First non-empty: first | `\u003c\u003c /key\\d+/, /item\\d+/\u003e\u003e` | Value1 |\n| First non-empty: second | `\u003c\u003c /item\\d+/, /key\\d+/\u003e\u003e` | Value1 |\n| First non-empty: absent | `\u003c\u003c /item\\d+/, /key\\d+/\u003e\u003e` | nil |\n| Cartesian: non-empty * non-empty | `\u003c\u003c a.# * b.#\\|\u003c\u003c@\u003e\u003e: (\u003c\u003c1\u003e\u003e, \u003c\u003c2\u003e\u003e)\u003c\u003c,\u003e\u003e\u003e\u003e` | 1: (Value1, Item1), 2: (Value1, Item2), 3: (Value2, Item1), 4: (Value2, Item2) |\n| **Separators** |\n| Separator, default | `\u003c\u003c#\\|\u003c\u003c@\u003e\u003e: \u003c\u003ckey\u003e\u003e\u003c\u003c,\u003e\u003e\u003e\u003e` | 1: Value1, 2: Value2, 3: Value3 |\n| Separator, explicit | `\u003c\u003c#\\|\u003c\u003c@\u003e\u003e: \u003c\u003ckey\u003e\u003e\u003c\u003c,\\|; \u003e\u003e\u003e\u003e` | 1: Value1; 2: Value2; 3: Value3 |\n| Separator, dynamic | `\u003c\u003c#\\|\u003c\u003c@\u003e\u003e: \u003c\u003ckey\u003e\u003e\u003c\u003c,\\|\u003c\u003csep\u003e\u003e\u003e\u003e\u003e\u003e` | 1: Value1; 2: Value2; 3: Value3 |\n| Separator, header and footer | `\u003c\u003c\\|Header \u003c\u003c#\\|\u003c\u003c@\u003e\u003e: \u003c\u003ckey\u003e\u003e\u003c\u003c,\u003e\u003e\u003e\u003e Footer\u003e\u003e` | Header 1: Value1, 2: Value2, 3: Value3 Footer |\n| Separator, fallback | `\u003c\u003c\\|Header \u003c\u003c#\\|\u003c\u003c@\u003e\u003e: \u003c\u003ckey\u003e\u003e\u003c\u003c,\u003e\u003e\u003e\u003e Footer\\|Fallback\u003e\u003e` | Fallback |\n| **printf()-style formatting** |\n| Float format, limited precision | `\u003c\u003cno\\|%.3f\u003e\u003e` | 3.142 |\n# Credits\n*FormatterII* is written by Alexander Mashin in 2022-2023. *Lua* and *LPEG* are created by Roberto Ierusalimschy. *lrexlib* is written by Reuben Thomas and Shmuel Zeigerman.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falex-mashin%2Fformatterii","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falex-mashin%2Fformatterii","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falex-mashin%2Fformatterii/lists"}