{"id":13466170,"url":"https://github.com/rxwei/Parsey","last_synced_at":"2025-03-25T21:31:26.783Z","repository":{"id":63920435,"uuid":"66696079","full_name":"rxwei/Parsey","owner":"rxwei","description":"Swift Parser Combinators","archived":false,"fork":false,"pushed_at":"2020-01-27T19:08:13.000Z","size":75,"stargazers_count":58,"open_issues_count":5,"forks_count":6,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-03-14T12:02:39.685Z","etag":null,"topics":["parser","parser-combinators","swift"],"latest_commit_sha":null,"homepage":"","language":"Swift","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/rxwei.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}},"created_at":"2016-08-27T04:34:19.000Z","updated_at":"2023-05-18T18:24:16.000Z","dependencies_parsed_at":"2023-01-14T14:00:51.573Z","dependency_job_id":null,"html_url":"https://github.com/rxwei/Parsey","commit_stats":null,"previous_names":[],"tags_count":28,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rxwei%2FParsey","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rxwei%2FParsey/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rxwei%2FParsey/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rxwei%2FParsey/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rxwei","download_url":"https://codeload.github.com/rxwei/Parsey/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245547449,"owners_count":20633366,"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":["parser","parser-combinators","swift"],"created_at":"2024-07-31T15:00:40.256Z","updated_at":"2025-03-25T21:31:26.516Z","avatar_url":"https://github.com/rxwei.png","language":"Swift","readme":"# Parsey\nSwift Parser Combinator Framework\n\nIn addition to simple combinators, **Parsey** supports source location/range tracking, \nbacktracking prevention, and custom error messages.\n\n![Parsey Playground](https://pbs.twimg.com/media/CrVaoBeVYAAWWoj.jpg:large)\n\n## Features\n\n- Combinator interface\n    - `|`, `~~`, `~~\u003e`, `\u003c~~`, `^^` combinator operators\n\n- Lexer primitives: \n    - `Lexer.whitespace`, `Lexer.signedInteger`, ...\n\n- Regex-like combinators:\n    - Postfix `.+` for `.many()`.\n        - Example: `let arrayLiteral = \"[\" ~~\u003e expression.+ \u003c~~ \"]\"`\n    - Postfix `.*` for `.manyOrNone()`.\n        - Example: `let classDef = (attribute | method).*`\n    - Postfix `.?` for `.optional()`.\n        - Example: `let declaration = \"let\" ~~\u003e id ~~ (\":\" ~~\u003e type).? ~~ (\"=\" ~~\u003e expression)`\n    - Postfix `+` for `.manyConcatenated()`.\n        - Example: `let skippedSpaces = (Lexer.space | Lexer.tab)+`\n    - Infix `+` for `.concatenatingResult(with:)`. \n        - Example: `let type = Lexer.upperLetter + Lexer.letter*`\n    - `Lexer.regex(_:)` for directly applying regular expressions.\n        - Example: `let id = Lexer.regex(\"[a-zA-Z][a-zA-Z0-9]*\")`\n\n- Backtracking prevention \n    - `.!` postfix operator or `.nonbacktracking()`\n\n- Parser tagging for error messages\n    - `\u003c!--` operator or `.tagged(_:)`\n\n- Rich error messages with source location\n    - For example:\n    ```\n    Parse failure at 2:4 ----\n    (+ %% 1 -20) 2 3)\n       ^~~~~~~~~~~~~~\n    Expecting an expression, but found \"%\"\n    ```\n\n- Source range tracking\n    - `^^^` operator or `.mapParse(_:)`\n    - For example, S-expression `\\n(+ \\n\\n(+ +1 -20) 2 3)` gets parsed to\n      the following range-tracked AST:\n    ```\n    Expr:(2:1..\u003c4:16):[\n        ID:(2:2..\u003c2:3):+,\n        Expr:(4:1..\u003c4:11):[\n            ID:(4:2..\u003c4:3):+,\n            Int:(4:4..\u003c4:6):1,\n            Int:(4:7..\u003c4:10):-20],\n        Int:(4:12..\u003c4:13):2,\n        Int:(4:14..\u003c4:15):3]\n    ```\n\n## Requirements\n\n- Swift 3\n\n- Any operating system\n\n## Package\n\nTo use it in your Swift project, add the following dependency to your \nSwift package description file.\n\n```swift\n    .Package(url: \"https://github.com/rxwei/Parsey\", majorVersion: 1)\n```\n\n## ⚙ Examples\n\n### 0️⃣ An LLVM Compiler Frontend written in Swift using **Parsey**\n\n[The COOL Programming Language](https://github.com/rxwei/COOL)\n\n### 1️⃣ Parse Left-associative Infix Expressions with Operator Precedence\n\n```swift\nindirect enum Expression {\n    case integer(Int)\n    case symbol(String)\n    case infix(String, Expression, Expression)\n}\n\nenum Grammar {\n    static let integer = Lexer.signedInteger\n        ^^ {Int($0)!} ^^ Expression.integer\n\n    static let symbol = Lexer.regex(\"[a-zA-Z][0-9a-zA-Z]*\")\n        ^^ Expression.symbol\n\n    static let addOp = Lexer.anyCharacter(in: \"+-\")\n        ^^ { op in { Expression.infix(op, $0, $1) } }\n    \n    static let multOp = Lexer.anyCharacter(in: \"*/\")\n        ^^ { op in { Expression.infix(op, $0, $1) } }\n\n    /// Left-associative multiplication\n    static let multiplication = (integer | symbol).infixedLeft(by: multOp)\n\n    /// Left-associative addition\n    static let addition = multiplication.infixedLeft(by: addOp)\n\n    static let expression: Parser\u003cExpression\u003e = addition\n}\n\ntry print(Grammar.expression.parse(\"2\"))\n/// Output:\n/// Expression.integer(2)\n\ntry print(Grammar.expression.parse(\"2+1+2*a\"))\n/// Output:\n/// Expression.infix(\"+\",\n///                  .infix(\"+\", .integer(2), .integer(1)),\n///                  .infix(\"*\", .integer(2), .symbol(\"a\")))\n```\n\n### 2️⃣ Parse S-Expressions\n\n```swift\nindirect enum Expr {\n    case sExp([Expr])\n    case int(Int)\n    case id(String)\n}\n\nenum Grammar {\n    static let whitespaces = (Lexer.space | Lexer.tab | Lexer.newLine)+\n    static let anInt = Lexer.signedInteger ^^ { Int($0)! } ^^ Expr.int\n    static let anID = Lexer.regex(\"[a-zA-Z_+\\\\-*/][0-9a-zA-Z_+\\\\-*/]*\") ^^ Expr.id\n    static let aSExp: Parser\u003cExpr\u003e =\n        \"(\" ~~\u003e (anExp.!).many(separatedBy: whitespaces).amid(whitespaces.?) \u003c~~ \")\"\n        ^^ Expr.sExp\n    static let anExp = anInt | anID | aSExp \u003c!-- \"an expression\"\n}\n\n/// Success\ntry Grammar.anExp.parse(\"(+ (+ 1 -20) 2 3)\")\n/// Output: Expr.sExp(...)\n\n/// Failure\ntry Grammar.anExp.parse(\"(+ \\n(+ %% 1 -20) 2 3)\")\n/// Output: Parse failure at 2:4 ----\n///         (+ %% 1 -20) 2 3)\n///            ^~~~~~~~~~~~~~\n///         Expecting an expression, but found \"%\"\n```\n\n### 3️⃣ Parse S-Expressions with Source Range Tracking\n\n```swift\nindirect enum Expr {\n    case sExp([Expr], SourceRange)\n    case int(Int, SourceRange)\n    case id(String, SourceRange)\n}\n\nenum Grammar {\n    static let whitespaces = (Lexer.space | Lexer.tab | Lexer.newLine)+\n\n    static let anInt = Lexer.signedInteger \n        ^^^ { Expr.int(Int($0.target)!, $0.range) }\n\n    static let anID = Lexer.regex(\"[a-zA-Z_+\\\\-*/][0-9a-zA-Z_+\\\\-*/]*\")\n        ^^^ { Expr.id($0.target, $0.range) }\n\n    static let aSExp: Parser\u003cExpr\u003e =\n        \"(\" ~~\u003e (anExp.!).many(separatedBy: whitespaces).amid(whitespaces.?) \u003c~~ \")\"\n        ^^^ { Expr.sExp($0.target, $0.range) }\n\n    static let anExp = anInt | anID | aSExp \u003c!-- \"an expression\"\n}\n\n/// Success\ntry Grammar.anExp.parse(\"(+ (+ 1 -20) 2 3)\")\n/// Output: Expr.sExp(...)\n\n/// Failure\ntry Grammar.anExp.parse(\"(+ \\n(+ %% 1 -20) 2 3)\")\n/// Output: Parse failure at 2:4 ----\n///         (+ %% 1 -20) 2 3)\n///            ^~~~~~~~~~~~~~\n///         Expecting an expression, but found \"%\"\n```\n\n## Dependency\n\n- [Funky - Functional Programming Library](https://github.com/rxwei/Funky)\n\n\n## License\n\nMIT License\n","funding_links":[],"categories":["Libs","Text [🔝](#readme)"],"sub_categories":["Text"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frxwei%2FParsey","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frxwei%2FParsey","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frxwei%2FParsey/lists"}