{"id":19212549,"url":"https://github.com/fasterthanlime/pegviz","last_synced_at":"2025-10-14T04:30:49.208Z","repository":{"id":40047039,"uuid":"259779150","full_name":"fasterthanlime/pegviz","owner":"fasterthanlime","description":"PEG trace visualizer","archived":false,"fork":false,"pushed_at":"2025-10-05T10:18:08.000Z","size":78,"stargazers_count":89,"open_issues_count":0,"forks_count":10,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-10-05T12:14:17.709Z","etag":null,"topics":["debugging","grammar","parser","peg","rust"],"latest_commit_sha":null,"homepage":"","language":"Rust","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/fasterthanlime.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","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,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":["fasterthanlime"]}},"created_at":"2020-04-28T23:55:44.000Z","updated_at":"2025-10-05T10:18:12.000Z","dependencies_parsed_at":"2025-10-05T12:08:32.960Z","dependency_job_id":"3cbb7cf0-67c6-4cb9-8e6b-f12137a66943","html_url":"https://github.com/fasterthanlime/pegviz","commit_stats":{"total_commits":27,"total_committers":2,"mean_commits":13.5,"dds":0.07407407407407407,"last_synced_commit":"73d003fe96566677796119c8ea54133c9a9b1673"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/fasterthanlime/pegviz","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fasterthanlime%2Fpegviz","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fasterthanlime%2Fpegviz/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fasterthanlime%2Fpegviz/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fasterthanlime%2Fpegviz/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fasterthanlime","download_url":"https://codeload.github.com/fasterthanlime/pegviz/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fasterthanlime%2Fpegviz/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279017942,"owners_count":26086213,"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","status":"online","status_checked_at":"2025-10-14T02:00:06.444Z","response_time":60,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["debugging","grammar","parser","peg","rust"],"created_at":"2024-11-09T13:47:18.508Z","updated_at":"2025-10-14T04:30:48.911Z","avatar_url":"https://github.com/fasterthanlime.png","language":"Rust","funding_links":["https://github.com/sponsors/fasterthanlime"],"categories":["Rust"],"sub_categories":[],"readme":"# pegviz\n\n![Quick hack status: Yes!](https://img.shields.io/badge/quick%20hack%3F-yes!-green)\n![Maintenance status: Not really](https://img.shields.io/badge/maintained%3F-not%20really-red)\n[![license: MIT/Apache-2.0](https://img.shields.io/badge/license-MIT%2FApache--2.0-blue.svg)](LICENSE-MIT)\n\nVisualizer for https://crates.io/crates/peg parsers.\n\n## Screenshot\n\n`pegviz` reads peg's tracing markers and generates a collapsible HTML tree.\n\n![](https://user-images.githubusercontent.com/7998310/80628077-1fe05100-8a51-11ea-87aa-4b8362adf56c.png)\n\nLeft side:\n\n  * Green: matched rule\n  * Yellow: partial match (see below)\n  * Red: failed rule\n\nRight side:\n\n  * Gray: previous input, for context\n  * Blue background: input matched by this rule\n  * White text: rest of input after matching\n\n\n## Partial Matches\n\nA partial match is a (special kind of) match failure.\nIt happens, if a rule consists of multiple sub-rules and some of them do match, but they do not all match.\n\nConsider for example the grammar\n\n```rust\n    pub rule traits() -\u003e (Vec\u003cString\u003e, Vec\u003cString\u003e)\n    = awesome_traits:(awesome() ++ \". \") \".\"? \" \"?\n      boring_traits:(boring() ** \". \") \".\"?\n    {\n      (awesome_traits, boring_traits)\n    }\n\n    rule awesome() -\u003e String\n    = name() \" is awesome due to \" reason:$(['a'..='z' | ' ']+) { reason.to_string() }\n\n    rule boring() -\u003e String\n    = name() \" is boring because of \" reason:$(['a'..='z' | ' ']+) { reason.to_string() }\n\n    rule name() -\u003e String = s:$(['A'..='Z']['a'..='z']+) { s.to_string() }\n```\n\nHere both `awesome` and `boring` start with `name()`.\nWhen parsing a string like\n\n    \"Paul is awesome due to his kindness. Ludwig is boring because of his cat.\"\n\nthe first sentence will match to the `awesome` rule, the second does not, but it _partially_ matches, because `Ludwig` also matches to `name()`.\nIt will, though, match to the `boring` rule.\n\n![partial match](https://github.com/user-attachments/assets/99fe050d-2ba6-44a7-9a76-a3d96956d788)\n\n\n## Format\n\n`pegviz` expects input in the following format:\n\n```\n[PEG_INPUT_START]\nint a = 12 + 45;\n[PEG_TRACE_START]\n[PEG_TRACE] Attempting to match rule `translation_unit0` at 1:1\n[PEG_TRACE] Attempting to match rule `list0` at 1:1\n[PEG_TRACE] Attempting to match rule `node` at 1:1\n[PEG_TRACE] Attempting to match rule `external_declaration` at 1:1\n[PEG_TRACE] Attempting to match rule `declaration` at 1:1\n[PEG_TRACE] Attempting to match rule `node` at 1:1\n[PEG_TRACE] Attempting to match rule `declaration0` at 1:1\n[PEG_TRACE] Attempting to match rule `gnu` at 1:1\n[PEG_TRACE] Attempting to match rule `gnu_guard` at 1:1\n[PEG_TRACE] Failed to match rule `gnu_guard` at 1:1\n[PEG_TRACE] Failed to match rule `gnu` at 1:1\n[PEG_TRACE] Attempting to match rule `_` at 1:1\n[PEG_TRACE] Matched rule `_` at 1:1 to 1:1\n... \u003comitted\u003e ...\n[PEG_TRACE_STOP]\n```\n\nThe `_START` and `_STOP` marker are pegviz-specific, you'll need to add\nthem to your program. See the **Integration** section for more information.\n\nMultiple traces may be processed, they'll all show up in the output file.\nOutput that occurs *between* traces is ignored.\n\n## Compatibility\n\npegviz has been used with:\n\n  * peg 0.5.7\n  * peg 0.6.2\n  * peg 0.8.4\n\nThere are no tests. It's quickly thrown together.\n\n## Integration\n\nIn your crate, re-export the `trace` feature:\n\n```\n# in Cargo.toml\n\n[features]\ntrace = [\"peg/trace\"]\n```\n\nThen, in your parser, add a `tracing` rule that captures all the input\nand outputs the markers `pegviz` is looking for:\n\n```rust\npeg::parser! { pub grammar example() for str {\n\nrule traced\u003cT\u003e(e: rule\u003cT\u003e) -\u003e T =\n    \u0026(input:$([_]*) {\n        #[cfg(feature = \"trace\")]\n        println!(\"[PEG_INPUT_START]\\n{}\\n[PEG_TRACE_START]\", input);\n    })\n    e:e()? {?\n        #[cfg(feature = \"trace\")]\n        println!(\"[PEG_TRACE_STOP]\");\n        e.ok_or(\"\")\n    }\n\npub rule toplevel() -\u003e Toplevel = traced(\u003ctoplevel0()\u003e)\n\n}}\n```\n\nIf your parser uses slices (such as `\u0026[u8]`, `\u0026[T]`), then each character or token must be on a new line.\n\n```rust\npeg::parser! { pub grammar example() for str {\n\nrule traced\u003cT\u003e(e: rule\u003cT\u003e) -\u003e T =\n    \u0026(input:$([_]*) {\n        #[cfg(feature = \"trace\")]\n        println!(\n            \"[PEG_INPUT_START]\\n{}\\n[PEG_TRACE_START]\",\n            input.iter().fold(\n                String::new(),\n                |s1, s2| s1 + \"\\n\" + s2.to_string().as_str()\n            ).trim_start().to_string()\n        );\n    })\n    e:e()? {?\n        #[cfg(feature = \"trace\")]\n        println!(\"[PEG_TRACE_STOP]\");\n        e.ok_or(\"\")\n    }\n\npub rule toplevel() -\u003e Toplevel = traced(\u003ctoplevel0()\u003e)\n\n}}\n```\n\nThe above is the recommended way *if you're maintaining the grammar* and want\nto be able to turn on pegviz support anytime.\n\nIf you're debugging someone else's parser, you may want to print the start/stop\nmarkers and the source yourself, around the parser invocation, like so:\n\n```rust\n    let source = std::fs::read_to_string(\u0026source).unwrap();\n    println!(\"[PEG_INPUT_START]\\n{}\\n[PEG_TRACE_START]\", source);\n    let res = lang_c::driver::parse_preprocessed(\u0026config, source);\n    println!(\"[PEG_TRACE_STOP]\");\n```\n\nMake sure you've installed `pegviz` into your `$PATH`:\n\n```shell\ncd pegviz/\ncargo install --force --path .\n```\n\n\u003e While installing it, you may notice `pegviz` depends on `peg`.\n\u003e That's right! It's using a PEG to analyze PEG traces.\n\nThen, simply run your program with the `trace` Cargo feature enabled, and\npipe its standard output to `pegviz`.\n\n```shell\ncd example/\ncargo run --features trace | pegviz --output ./pegviz.html\n```\n\nNote that the `--output` argument is mandatory.\n\nThe last step is to open the resulting HTML file in a browser and click around!\n\n## License\n\npegviz is released under the MIT License. See the LICENSE file for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffasterthanlime%2Fpegviz","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffasterthanlime%2Fpegviz","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffasterthanlime%2Fpegviz/lists"}