{"id":18057385,"url":"https://github.com/1computer1/lexure","last_synced_at":"2026-03-17T15:45:05.255Z","repository":{"id":37787168,"uuid":"258745064","full_name":"1Computer1/lexure","owner":"1Computer1","description":"Parser and utilities for non-technical user input","archived":false,"fork":false,"pushed_at":"2023-10-31T13:44:37.000Z","size":286,"stargazers_count":71,"open_issues_count":1,"forks_count":5,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-25T02:43:57.407Z","etag":null,"topics":["args","lexer","parser"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","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/1Computer1.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,"publiccode":null,"codemeta":null}},"created_at":"2020-04-25T10:11:34.000Z","updated_at":"2024-08-16T02:19:31.000Z","dependencies_parsed_at":"2024-06-18T17:08:26.182Z","dependency_job_id":"cb215985-8e45-45eb-ac7b-f5c6d60e9057","html_url":"https://github.com/1Computer1/lexure","commit_stats":{"total_commits":169,"total_committers":1,"mean_commits":169.0,"dds":0.0,"last_synced_commit":"060d67cdfd09fafe1f25be8a1c14eae84b0fe4f1"},"previous_names":[],"tags_count":23,"template":false,"template_full_name":"1Computer1/istnlt","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/1Computer1%2Flexure","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/1Computer1%2Flexure/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/1Computer1%2Flexure/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/1Computer1%2Flexure/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/1Computer1","download_url":"https://codeload.github.com/1Computer1/lexure/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248345276,"owners_count":21088242,"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":["args","lexer","parser"],"created_at":"2024-10-31T02:07:34.928Z","updated_at":"2026-03-17T15:45:05.192Z","avatar_url":"https://github.com/1Computer1.png","language":"TypeScript","readme":"# lexure\n\n`npm i lexure`  \n\nParser and utilities for non-technical user input.  \n[Documentation (includes reference and cookbook) available here](./docs).  \n\n## Features\n\n- Parses quoted input with multiple quote types.\n- Parses flags and options with customizable parsing implementation.\n- Keeps trailing whitespace.\n- Always parses input by allowing some mis-inputs.\n- Includes a convenient wrapper to retrieve arguments.\n- Includes abstractions for creating an input loop.\n\n## Example\n\nCheck out the [cookbook](./docs/cookbook) for complete examples.  \nFirst, import lexure:  \n\n```ts\n// TypeScript or ES Module\nimport * as lexure from 'lexure';\n\n// CommonJS\nconst lexure = require('lexure');\n```\n\nConsider some user input in the form of a command like so:  \n\n```ts\nconst input = '!hello world \"cool stuff\" --foo --bar=baz a b c';\n```\n\nWe first tokenize the input string to individual tokens.  \nAs you can see, lexure supports custom open and close quotes for devices with special keyboards and other locales.  \n\nThe `!hello` part of the input is usually interpreted as a command, which the Lexer class can handle too.  \nThe remaining input is delayed as a function so that you can ignore the rest of the input if it is an invalid command.  \n\n```ts\nconst lexer = new lexure.Lexer(input)\n    .setQuotes([\n        ['\"', '\"'],\n        ['“', '”']\n    ]);\n\nconst res = lexer.lexCommand(s =\u003e s.startsWith('!') ? 1 : null);\nif (res == null) {\n    // The input might be invalid.\n    // You might do something else here.\n    return;\n}\n\nconst cmd = res[0];\n\u003e\u003e\u003e { value: 'hello', raw: 'hello', trailing: ' ' }\n\nconst tokens = res[1]();\n\u003e\u003e\u003e [\n    { value: 'world',      raw: 'world',        trailing: ' ' },\n    { value: 'cool stuff', raw: '\"cool stuff\"', trailing: ' ' },\n    { value: '--foo',      raw: '--foo',        trailing: ' ' },\n    { value: '--bar=baz',  raw: '--bar=baz',    trailing: ' ' },\n    { value: 'a',          raw: 'a',            trailing: ' ' },\n    { value: 'b',          raw: 'b',            trailing: ' ' },\n    { value: 'c',          raw: 'c',            trailing: ''  }\n]\n```\n\nNow, we can take the tokens and parse them into a structure.  \nIn lexure, you are free to describe how you want to match unordered arguments like flags.  \nThere are also several built-in strategies for common usecases.  \n\n```ts\nconst parser = new lexure.Parser(tokens)\n    .setUnorderedStrategy(lexure.longStrategy());\n\nconst out = parser.parse();\n\u003e\u003e\u003e {\n    ordered: [\n        { value: 'world',      raw: 'world',        trailing: ' ' },\n        { value: 'cool stuff', raw: '\"cool stuff\"', trailing: ' ' },\n        { value: 'a',          raw: 'a',            trailing: ' ' },\n        { value: 'b',          raw: 'b',            trailing: ' ' },\n        { value: 'c',          raw: 'c',            trailing: ''  }\n    ],\n    flags: Set { 'foo' },\n    options: Map { 'bar' =\u003e ['baz'] }\n}\n\nlexure.joinTokens(out.ordered)\n\u003e\u003e\u003e 'world \"cool stuff\" a b c'\n```\n\nA wrapper class Args is available for us to retrieve the arguments from the output of the parser.  \nIt keeps track of what has already been retrieved and has several helpful methods.  \n\n```ts\nconst args = new lexure.Args(out);\n\nargs.single()\n\u003e\u003e\u003e 'world'\n\nargs.single()\n\u003e\u003e\u003e 'cool stuff'\n\nargs.findMap(x =\u003e x === 'c' ? lexure.some('it was a C') : lexure.none())\n\u003e\u003e\u003e { exists: true, value: 'it was a C' }\n\nargs.many()\n\u003e\u003e\u003e [\n    { value: 'a', raw: 'a', trailing: ' ' },\n    { value: 'b', raw: 'b', trailing: ' ' }\n]\n\nargs.flag('foo')\n\u003e\u003e\u003e true\n\nargs.option('bar')\n\u003e\u003e\u003e 'baz'\n```\n\nSuppose we would like to prompt the user input, and retry until a valid input is given.  \nlexure has various functions for this, in which the logic of an input loop is abstracted out.  \n\n```ts\n// Suppose we have access to this function that prompts the user.\n// You can imagine this as a CLI or chat bot.\nfunction prompt(): string | null {\n    return '100';\n}\n\nconst result = lexure.loop1({\n    getInput() {\n        const input = prompt();\n        if (input == null) {\n            return lexure.fail('no input');\n        } else {\n            return lexure.step(input);\n        }\n    }\n\n    parse(s: string) {\n        const n = Number(s);\n        if (isNaN(n)) {\n            return lexure.fail('cannot parse input');\n        } else {\n            return lexure.finish(n);\n        }\n    }\n});\n\nresult\n\u003e\u003e\u003e { success: true, value: 100 }\n```\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F1computer1%2Flexure","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F1computer1%2Flexure","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F1computer1%2Flexure/lists"}