{"id":17783325,"url":"https://github.com/sience/yarnparser","last_synced_at":"2026-05-04T07:33:54.653Z","repository":{"id":259032429,"uuid":"876144364","full_name":"SiENcE/yarnparser","owner":"SiENcE","description":"A Yarn parser written in Lua to convert Yarn Spinner dialogues into Lua structures. (also ebnf desc. of yarn) ","archived":false,"fork":false,"pushed_at":"2024-10-21T21:00:50.000Z","size":16,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-10-21T22:02:56.797Z","etag":null,"topics":["dialogues","lua","parser","script","yarn","yarnscript"],"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/SiENcE.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":"2024-10-21T13:25:43.000Z","updated_at":"2024-10-21T21:00:53.000Z","dependencies_parsed_at":"2024-10-22T17:05:18.964Z","dependency_job_id":null,"html_url":"https://github.com/SiENcE/yarnparser","commit_stats":null,"previous_names":["sience/yarnparser"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SiENcE%2Fyarnparser","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SiENcE%2Fyarnparser/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SiENcE%2Fyarnparser/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SiENcE%2Fyarnparser/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/SiENcE","download_url":"https://codeload.github.com/SiENcE/yarnparser/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":229714239,"owners_count":18112681,"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":["dialogues","lua","parser","script","yarn","yarnscript"],"created_at":"2024-10-27T07:03:13.976Z","updated_at":"2026-05-04T07:33:54.613Z","avatar_url":"https://github.com/SiENcE.png","language":"Lua","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Yarn Parser \u0026 Interpreter\nA Yarn parser written in Lua to convert Yarn Spinner dialogues into Lua structures. There is also a [interpreter](yarn_interpreter.lua) to demonstrate how to interpret the parsed node structures.\n\n## Overview\n\nThis Lua module provides a parser for Yarn scripts, which are commonly used in interactive narrative games. The parser can handle various elements of Yarn syntax, including dialogue, choices, conditional statements, variable assignments, and commands.\n\nThe supplied interpreter is only an example of how the parsed structure can be interpreted. You must adapt it or implement your own interpreter that meets your requirements.\n\n## Features\n\n- Parse Yarn scripts into structured node objects\n- Support for:\n  - Dialogue lines\n  - Choices (including nested choices)\n  - Conditional statements (if/else)\n  - Variable assignments, declarations, and interpolation\n  - Commands (including jump, set, declare)\n  - Comments (single-line and multi-line)\n- Grouping of related content (e.g., choices and their responses)\n- Ability to find dialogue preceding choice groups\n- Sample interpreter with callbacks to run the dialogues.\n\n## Usage Sample\n\nmain.lua\n```lua\nlocal YarnParser = require(\"yarn_parser\")\n\nlocal script = [[\ntitle: Start\n---\nPlayer: Hello, world!\n\n\u003c\u003cdeclare $goldAmount = 100\u003e\u003e\n\u003c\u003cset $health to 100\u003e\u003e\nYour health is {$health}.\n-\u003e Choice 1: gold\n    NPC: You chose {$goldAmount} gold.\n    \u003c\u003cset $health to 50\u003e\u003e\n-\u003e Choice 2: health\n    NPC: Your health is {$health}.\n\nNPC: You have {$health} health.\n\n\u003c\u003cjump TEST\u003e\u003e\n\nJump does not work.\n===\ntitle: TEST\n---\nJump: Test.\n===\n]]\n\n-- Function to print the content of a node\nlocal function print_content(content, indent)\n    indent = indent or \"\"\n    for _, item in ipairs(content) do\n        if item.type == \"dialogue\" then\n            print(indent .. item.text)\n        elseif item.type == \"choice\" then\n            print(indent .. \"    -\u003e \" .. item.text)\n            print_content(item.response, indent .. \"  \")\n        elseif item.type == \"set\" then\n            print(indent .. \"Set: \" .. item.variable .. \" to \" .. item.value)\n        elseif item.type == \"conditional\" then\n            print(indent .. \"If: \" .. item.condition)\n            print(indent .. \"Then:\")\n            print_content(item.if_block, indent .. \"  \")\n            if #item.else_block \u003e 0 then\n                print(indent .. \"Else:\")\n                print_content(item.else_block, indent .. \"  \")\n            end\n        elseif item.type == \"jump\" then\n            print(indent .. \"Jump to: \" .. item.target)\n        elseif item.type == \"declare\" then\n            print(indent .. \"Declare: \" .. item.variable .. \" = \" .. item.value)\n        elseif item.type == \"comment\" then\n            print(indent .. \"Comment: \" .. item.text)\n        else\n            print(indent .. \"Unknown type: \" .. tostring(item.type))\n        end\n    end\nend\n\nlocal parsed_nodes = YarnParser:parse(script)\n\n-- Print the parsed structure\nif parsed_nodes then\n    for i, node in ipairs(parsed_nodes) do\n        print(\"Node: \" .. node.title)\n        print_content(node.content, \"  \")\n        print(\"\") -- Empty line between nodes for readability\n    end\nend\n```\n\n## API\n\n### YarnParser:parse(script)\n\nParses a Yarn script and returns an array of node objects.\n\n- `script`: A string containing the entire Yarn script.\n- Returns: An array of parsed node objects.\n\n## Node Structure\n\nEach parsed node has the following structure (exported json table):\n\n```json\n[\n\t{\n\t\t\"title\": \"Start\",\n\t\t\"content\": [\n\t\t\t{\n\t\t\t\t\"text\": \"Player: Hello, world!\",\n\t\t\t\t\"indent\": 0,\n\t\t\t\t\"type\": \"dialogue\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"variable\": \"goldAmount\",\n\t\t\t\t\"value\": \"100\",\n\t\t\t\t\"indent\": 0,\n\t\t\t\t\"type\": \"declare\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"variable\": \"health\",\n\t\t\t\t\"value\": \"100\",\n\t\t\t\t\"indent\": 0,\n\t\t\t\t\"type\": \"set\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"text\": \"Your health is {$health}.\",\n\t\t\t\t\"indent\": 0,\n\t\t\t\t\"type\": \"dialogue\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"response\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"text\": \"NPC: You chose {$goldAmount} gold.\",\n\t\t\t\t\t\t\"indent\": 4,\n\t\t\t\t\t\t\"type\": \"dialogue\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"variable\": \"health\",\n\t\t\t\t\t\t\"value\": \"50\",\n\t\t\t\t\t\t\"indent\": 4,\n\t\t\t\t\t\t\"type\": \"set\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"text\": \"Choice 1: gold\",\n\t\t\t\t\"indent\": 0,\n\t\t\t\t\"type\": \"choice\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"response\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"text\": \"NPC: Your health is {$health}.\",\n\t\t\t\t\t\t\"indent\": 4,\n\t\t\t\t\t\t\"type\": \"dialogue\"\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\t\"text\": \"Choice 2: health\",\n\t\t\t\t\"indent\": 0,\n\t\t\t\t\"type\": \"choice\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"text\": \"NPC: You have {$health} health.\",\n\t\t\t\t\"indent\": 0,\n\t\t\t\t\"type\": \"dialogue\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"target\": \"TEST\",\n\t\t\t\t\"indent\": 0,\n\t\t\t\t\"type\": \"jump\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"text\": \"Jump does not work.\",\n\t\t\t\t\"indent\": 0,\n\t\t\t\t\"type\": \"dialogue\"\n\t\t\t}\n\t\t]\n\t},\n\t{\n\t\t\"title\": \"TEST\",\n\t\t\"content\": [\n\t\t\t{\n\t\t\t\t\"text\": \"Jump: Test.\",\n\t\t\t\t\"indent\": 0,\n\t\t\t\t\"type\": \"dialogue\"\n\t\t\t}\n\t\t]\n\t}\n]\n```\n\nContent objects can be of various types, including \"dialogue\", \"choice\", \"conditional\", \"set\", \"declare\", \"jump\", and \"comment\".\n\n## Yarn Syntax\n\nFor a detailed description of the Yarn syntax, please refer to [Yarn syntax description](yarn_syntax.md).\n\n## Interpreter\n\nThe included interpreter demonstrates how to run parsed Yarn scripts with callback support for various events.\n\n### Basic Usage\n\n```lua\nlocal YarnParser = require(\"yarn_parser\")\nlocal YarnInterpreter = require(\"yarn_interpreter\")\n\n-- Parse your Yarn script\nlocal parsed_nodes = YarnParser:parse(script)\n\n-- Create interpreter instance\nlocal interpreter = YarnInterpreter.new(parsed_nodes)\n\n-- OPTIONAL: Set up callbacks\ninterpreter:set_callbacks({\n    on_dialogue = function(text)\n        -- Custom dialogue display\n        print(\"[DIALOGUE] \" .. text)\n    end,\n    on_choice = function(choices)\n        -- Custom choice handling\n        print(\"[CHOICE]\")\n        for i, choice in ipairs(choices) do\n            print(i .. \": \" .. choice)\n        end\n        return tonumber(io.read())\n    end,\n    on_variable = function(name, value)\n        -- Variable change notification\n        print(\"[VARIABLE] \" .. name .. \" = \" .. tostring(value))\n    end,\n    on_node_enter = function(title)\n        print(\"[ENTER NODE] \" .. title)\n    end,\n    on_node_exit = function(title)\n        print(\"[EXIT NODE] \" .. title)\n    end\n})\n\n-- Run the interpreter\ninterpreter:run()\n```\n\n### Available Callbacks\n\n- `on_dialogue(text)`: Called when dialogue text is encountered\n- `on_choice(choices, path)`: Called when choices are presented. Must return the selected choice index\n- `on_variable(name, value)`: Called when a variable is set or declared\n- `on_node_enter(title)`: Called when entering a new node\n- `on_node_exit(title)`: Called when exiting a node\n\n### Variable Management\n\nThe interpreter maintains its own variable state, but you can interact with it:\n\n```lua\n-- Get a variable value\nlocal value = interpreter:get_variable(\"health\")\n\n-- Set a variable value\ninterpreter:set_variable(\"health\", 100)\n```\n\n### Handling Choices\n\nThe `on_choice` callback receives:\n- An array of choice texts with variables already interpolated\n- A path string showing the hierarchy of nested choices (e.g., \"Choice 1 \u003e Nested Choice 2\")\n\nThe callback must return a number indicating the selected choice (1-based index).\n\n### Error Handling\n\nThe interpreter will emit warnings (via print) when:\n- Invalid choices are selected\n- Jump targets are not found\n- Variables are undefined\n- Conditions cannot be evaluated\n\n## Limitations\n\n- The parser assumes well-formed Yarn syntax. Malformed scripts may lead to unexpected results.\n- Complex nested structures (e.g., conditionals within choices within conditionals) may not be handled perfectly and might require additional processing.\n\n## Author\n\nFlorian Fischer ( https://github.com/SiENcE )\n\n## License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.\n\n## Contributing\n\nContributions are welcome! Please feel free to submit a Pull Request.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsience%2Fyarnparser","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsience%2Fyarnparser","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsience%2Fyarnparser/lists"}