{"id":46096023,"url":"https://github.com/zalgonoise/parse","last_synced_at":"2026-03-01T18:37:04.356Z","repository":{"id":65146230,"uuid":"583500775","full_name":"zalgonoise/parse","owner":"zalgonoise","description":"a generic parser library written in Go","archived":false,"fork":false,"pushed_at":"2023-02-10T13:51:20.000Z","size":47,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2023-08-12T05:35:39.216Z","etag":null,"topics":["generic","go","golang","parse","parser","parsetree","treegraph"],"latest_commit_sha":null,"homepage":"","language":"Go","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/zalgonoise.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":"2022-12-30T00:55:15.000Z","updated_at":"2023-01-08T12:11:00.000Z","dependencies_parsed_at":"2023-02-19T01:10:14.306Z","dependency_job_id":null,"html_url":"https://github.com/zalgonoise/parse","commit_stats":null,"previous_names":[],"tags_count":0,"template":null,"template_full_name":null,"purl":"pkg:github/zalgonoise/parse","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zalgonoise%2Fparse","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zalgonoise%2Fparse/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zalgonoise%2Fparse/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zalgonoise%2Fparse/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/zalgonoise","download_url":"https://codeload.github.com/zalgonoise/parse/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zalgonoise%2Fparse/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29979130,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-01T16:35:47.903Z","status":"ssl_error","status_checked_at":"2026-03-01T16:35:44.899Z","response_time":124,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["generic","go","golang","parse","parser","parsetree","treegraph"],"created_at":"2026-03-01T18:37:03.700Z","updated_at":"2026-03-01T18:37:04.344Z","avatar_url":"https://github.com/zalgonoise.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# parse\n\n*a generic parser library written in Go*\n\n_______________\n\n## Concept\n\n`parse` is a parser library for Go, based on the concept of the [`text/template`](https://pkg.go.dev/text/template) lexer, as a generic implementation. The logic behind this parser is mostly based off of [Rob Pike](https://github.com/robpike)'s talk about [Lexical Scanning in Go](https://www.youtube.com/watch?v=HxaD_trXwRE), which is also seen in the standard library (in [`text/template/parse/parse.go`](https://cs.opensource.google/go/go/+/refs/tags/go1.19.4:src/text/template/parse/parse.go)).\n\n\nThe lexer is a state-machine that analyzes input data (split into single units) by traversing the slice and classifying *blobs* of the slice (as lexemes) with a certain token. The lexer emits items, which are composed of three main elements:\n- the starting position in the slice where the lexeme starts\n- the (comparable type) token classifying this piece of data\n- a slice containing the actual data \n\nThe lexer is usually in-sync with a parser (also a state-machine, running in tandem with a lexer), that will consume the items emited by the lexer to build a parse tree. The parse tree, as the name implies, is a tree graph data structure that will layout the received tokens with a certain path / structure, with some logic in mind. It is finally able to output the processed tree as an output type, configurable by the developer, too.\n\n## Why generics?\n\nGenerics are great for when there is a solid algorithm that serves for many types, and can be abstracted enough to work without major workarounds; and this approach to a lexer / parser is very straight-forward and yet so simple (the Go way). Of course when I refer [Rob Pike](https://github.com/robpike)'s talk about lexers I am aware that the context is parsing text (for templating). The *approach with generics* will limit the potential that shines in the original implementation, one way or the other (simply with EOF being a zero value, for example -- zero types should not be used for this).\n\nBut all in all, it was a great exercise to practice using generics. Maybe I will just use this library once or twice, maybe it will be actually useful for some. I am just in it for the ride. :)\n\n## Overview\n\n\nThe idea behind implementing a generic algorithm for a lexer and parser came from trying to build a graph (data structure) representing the logic blocks in a Go file. Watching the talk above was a breath of fresh air when it came to the design of the lexer and its simple approach. So, it would be nice to leverage this algorithm for the Go code graph idea from before. By making the logic generic, one could implement an `Item` type to hold a defined token type, and a set of (any type of) values and `StateFn` state-functions to tokenize input data. In concept this works for any given type, as the point is to label elements of a slice with identifying tokens, that will be processed into a parse tree (with a specific parser implementation).\n\nThe parser is a state-machine that works exactly the same way as the lexer, but with a slightly different direction. The parser consumes items emitted by a lexer one by one (with look-ahead capabilities), and builds a parse tree from them. The way this works is by running `ParseFn`s on the parser until all items from the lexer are depleted. This is the similarity with the lexer's logic, in terms of developer extensibility and the implementation of these functions.\n\nParsers will also be able to (optionally) convert the parse tree into a meaningful type, with the help of `ProcessFn`s and `NodeFn`s. Like `ParseFn`s and `StateFn`s, these will take in a `parse.Tree` and a `parse.Node` respectively, returning a generic type R (any type) and an error. The developer can either implement these processor functions or simply work with the `parse.Tree`.\n\n## Installation \n\n\u003e Note: this library is not ready out-of-the box! You will need to implement your own `ParseFn` parse-functions and optionally your own `ProcessFn` processor-functions with defined types. This repo will expose simple examples to understand the flow of the parser, below.\n\nYou can add this library to your Go project with a `go get` command:\n\n```\ngo get github.com/zalgonoise/parse\n```\n\n## Features\n\n### Entities\n\n#### Tree\n\nThe Tree is the state-machine holding all the nodes as lexemes are parsed. It stores a (tree) graph data structure by storing (and exporting) the Root Node -- one that is created when the Tree is initialized -- which may or may not contain additional nodes as edges.\n\nA Tree buffers the items emitted by a lexer in a slice (`Tree.items`) that is initialized with a certain size. It buffers the items because the slice only populates more than the first slot when looking-ahead; making this the least expensive approach possible.\n\nThe Tree also holds the `lex.Lexer` lexer, which it leverages when then next item is requested (by a `ParseFn`), as it is actually calling the lexer's `l.NextItem()` method.\n\nAlso, the Tree has a `map[BackupSlot]*Node[C, T]` field representing backup slots for nodes. The library exposes 5 `BackupSlot`s as an exported type, so the caller can store and load positions in the Tree when processing it.\n\nLast but not least, just like the `lex.Lexer`, it holds a `ParseFn` that is called until `nil` is returned (similar to the `lex.StateFn`).\n\n```go\n// Tree is a generic tree data structure to represent a lexical tree\n//\n// The Tree will buffer tokens of type T from a Lexer, identified by the same\n// type of comparable tokens. The parser runs in tandem with a lexer -- as the\n// parser advances to the next token, it is actually consuming a token from the lexer\n// by calling its `lexer.NextItem()` method.\n//\n// A Tree exposes methods for creating and moving around Nodes, and to consume, buffer and\n// backup lex Items as it converts them into nodes in the Tree.\n//\n// A Tree will store every node it contains, nested within the Root node. To navigate through\n// the nodes in a Tree, the Tree stores (and exports) a Root element, pointing to this Root node.\n//\n// The Root node contains all top-level items in lexical order, which may or may not have\n// edges themselves. It is the responsibility of the caller to ensure that the Tree is\n// navigated through entirely, when processing it.\ntype Tree[C comparable, T any] struct {\n\tRoot *Node[C, T]\n\n\tnode    *Node[C, T]\n\titems   []lex.Item[C, T]\n\tlex     lex.Emitter[C, T]\n\tpeek    int\n\tbackup  map[BackupSlot]*Node[C, T]\n\tparseFn ParseFn[C, T]\n}\n```\n\n#### Node \n\nA Node stores the `lex.Item` received from a lexer, modified or not. It will also store a pointer to its parent Node, as well as a list of edges, or child Nodes.\n\n```go\n// Node is a generic tree data structure unit. It is presented as a bidirectional\n// tree knowledge graph that starts with a root Node (one without a parent) that\n// can have zero-to-many children.\n//\n// It holds a reference to its parent (so that ParseFns can return to the correct\n// point in the tree), the item's (joined) lexemes, and edges (if any)\n//\n// Edges (child nodes) are defined in a list containing the same lexical order as\n// received. This allows safely nesting one or mode nodes without losing context of\n// the overall structure of the Nodes in the Tree\ntype Node[C comparable, T any] struct {\n\tlex.Item[C, T]\n\tParent *Node[C, T]\n\tEdges  []*Node[C, T]\n}\n```\n\n#### ParseFn\n\nSimilar to the lexer's `lex.StateFn`, it is a recursive function called by the Tree which should consume the items received by the lexer, and organizing them in the parse Tree.\n\nIt is a defined type and the consumer of the library must implement their own logic to consume the lexemes emitted by the lexer.\n\n```go\n// ParseFn is similar to the Lexer's StateFn, as a recursive function that the Tree\n// will keep calling during runtime until it runs out of items received from the Lexer\n//\n// The ParseFn will return another ParseFn that will keep processing the items; which\n// could be done in a number of ways (switch statements, helper functions, etc). When\n// `nil` is returned, the parser will stop processing lex items\ntype ParseFn[C comparable, T any] func(t *Tree[C, T]) ParseFn[C, T]\n```\n\n#### ProcessFn\n\nThe `ProcessFn` is a post-parsing function (a processor-function) that can be implemented in order to convert the input parse Tree into a meaningful data type R (any type).\n\nIn the context of parsing a string, type `T` would be a `rune`, and type `R` would be a `string`. It returns an error for seamless error handling.\n\n```go\n// ProcessFn is a function that can be executed after parsing all the items, and will\n// return a known-good type for the developer to work on. This is a step taken after a\n// Tree is built\ntype ProcessFn[C comparable, T any, R any] func(t *Tree[C, T]) (R, error)\n```\n\n#### NodeFn\n\nA `NodeFn` is a function called by a `ProcessFn` implementation, to process a Node. Similar to `ProcessFn`, it is an optional type that serves as a building block for more complex or structured parsing and processing.\n\nIn the context of parsing Markdown into HTML, the Node `n` could contain *h1 item* or a *hyperlink item*; that could be reused in a Markdown-to-HTML converter.\n\n```go\n// NodeFn is a function that can be executed against a single node, when processing the\n// parse.Tree\ntype NodeFn[C comparable, T any, R any] func(n *Node[C, T]) (R, error)\n```\n\n### Helpers\n\n#### Run function\n\nRun simplifies a few actions when running a converter, so that the consumer of the library only needs to provide:\n- the data\n- the lexer's `StateFn`\n- the parser's `ParseFn`\n- the parser's `ProcessFn`\n\n```go\n// Run encapsulates the lexer and parser runtime into a single one-shot action\n//\n// The caller must supply the input data []T `input`, a lex.StateFn to kick-off the lexer,\n// a ParseFn to kick-off the parser, and a ProcessFn to convert the parse.Tree into the\n// desired return type (or an error)\nfunc Run[C comparable, T any, R any](\n\tinput []T,\n\tinitStateFn lex.StateFn[C, T],\n\tinitParseFn ParseFn[C, T],\n\tprocessFn ProcessFn[C, T, R],\n) (R, error) {\n\tvar rootEOF C\n\tl := lex.New(initStateFn, input)\n\tt := New((lex.Emitter[C, T])(l), initParseFn, rootEOF)\n\tt.Parse()\n\treturn processFn(t)\n}\n```\n\n#### ParseTo function\n\nSimilar to `Run`, `ParseTo` will process the input into the `output` variable of type `*R`, returning an error if there is one.\n\n```go\n// ParseTo is similar to Run, but writes the processed type to type *R `output`\nfunc ParseTo[C comparable, T any, R any](\n\tinput []T,\n\tinitStateFn lex.StateFn[C, T],\n\tinitParseFn ParseFn[C, T],\n\tprocessFn ProcessFn[C, T, R],\n\toutput *R,\n) (err error) {\n\tvar rootEOF C\n\tl := lex.New(initStateFn, input)\n\tt := New((lex.Emitter[C, T])(l), initParseFn, rootEOF)\n\tt.Parse()\n\tif output == nil {\n\t\toutput = new(R)\n\t}\n\t*output, err = processFn(t)\n\treturn err\n}\n```\n\n## Implementing\n\n**Note**: *Example and tests can be found in the [`impl`](./impl/) directory; from the lexer to the parser*\n\n________\n\n### Token type\n\nImplementing a Lexer requires considering the format of the input data and how it can be tokenized. For this example, the input data is a string, where the lexeme units will be runes.\n\n\u003e The `TemplateItem` will be a comparable (unique) TextToken type, where the lexemes will be runes\n\n```go\n// TemplateItem represents the lex.Item for a runes lexer based on TextToken identifiers\ntype TemplateItem[C TextToken, I rune] lex.Item[C, I]\n```\n\nFor this, the developer needs to define a token type (with an enumeration of expected tokens, where the zero-value for the type is EOF).\n\n\u003e A set of expected tokens are enumerated. In this case the text template will take text\n\u003e between single braces (like `{this}`), and ...replace the braces with double angle-brackets\n\u003e (like `\u003e\u003ethis\u003c\u003c`). Not very fancy but serves as an example.\n\n```go\n// TextToken is a unique identifier for this text template implementation\ntype TextToken int\n\nconst (\n\tTokenEOF TextToken = iota\n\tTokenError\n\tTokenIDENT\n\tTokenTEMPL\n\tTokenLBRACE\n\tTokenRBRACE\n)\n```\n\n\nAfter defining the type, a (set of) `StateFn`(s) need to be created, in context of the input data and how it should be tokenized. Each `StateFn` will hold the responsibility of tokenizing a certain lexeme, and each `StateFn` will have a different flow and responsibility.\n\n### Lexer and state functions\n\n\u003e `initState` switches on the next lexable unit's value, to either emit an item or simply return a new state. This state should be able to listen to all types of (supported) symbols since this example supports so (a user could start a template in the very first char, and end it on the last one)\n\u003e\n\u003e The checks for `l.Width() \u003e 0` ensures that an existing *stack* is being pushed before \n\u003e advancing to the next token in a different procedure (e.g., consider all identifier tokens\n\u003e before going into the `stateBRACE` routine)\n\n\n```go\n// initState describes the StateFn to kick off the lexer. It is also the default fallback StateFn\n// for any other StateFn\nfunc initState[C TextToken, T rune](l lex.Lexer[C, T]) lex.StateFn[C, T] {\n\tswitch l.Next() {\n\tcase '}':\n\t\tif l.Width() \u003e 0 {\n\t\t\tl.Prev()\n\t\t\tl.Emit((C)(TokenIDENT))\n\t\t}\n\t\tl.Ignore()\n\t\treturn stateRBRACE[C, T]\n\tcase '{':\n\t\tif l.Width() \u003e 0 {\n\t\t\tl.Prev()\n\t\t\tl.Emit((C)(TokenIDENT))\n\t\t}\n\t\tl.Ignore()\n\t\treturn stateLBRACE[C, T]\n\tcase 0:\n\t\treturn nil\n\tdefault:\n\t\treturn stateIDENT[C, T]\n\t}\n}\n```\n\n\u003e `stateIDENT` absorbs all text characters until it hits a `{`, `}` or EOF. Then, if the following \n\u003e character is a `{`, or a `}` it returns the `stateLBRACE` or `stateRBRACE` routine, respectively. \n\u003e If it hits EOF, it will return a EOF token and a nil `StateFn`.\n\n\n```go\n// stateIDENT describes the StateFn to parse text tokens.\nfunc stateIDENT[C TextToken, T rune](l lex.Lexer[C, T]) lex.StateFn[C, T] {\n\tl.AcceptRun(func(item T) bool {\n\t\treturn item != '}' \u0026\u0026 item != '{' \u0026\u0026 item != 0\n\t})\n\tswitch l.Next() {\n\tcase '}':\n\t\tif l.Width() \u003e 0 {\n\t\t\tl.Prev()\n\t\t\tl.Emit((C)(TokenIDENT))\n\t\t}\n\t\treturn stateRBRACE[C, T]\n\tcase '{':\n\t\tif l.Width() \u003e 0 {\n\t\t\tl.Prev()\n\t\t\tl.Emit((C)(TokenIDENT))\n\t\t}\n\t\treturn stateLBRACE[C, T]\n\tdefault:\n\t\tif l.Width() \u003e 0 {\n\t\t\tl.Emit((C)(TokenIDENT))\n\t\t}\n\t\tl.Emit((C)(TokenEOF))\n\t\treturn nil\n\t}\n}\n```\n\n\u003e `stateLBRACE` tokenizes the `{` character, returning the initial state after skipping this character\n\n```go\n// stateLBRACE describes the StateFn to check for and emit an LBRACE token\nfunc stateLBRACE[C TextToken, T rune](l lex.Lexer[C, T]) lex.StateFn[C, T] {\n\tl.Next() // skip this symbol\n\tl.Emit((C)(TokenLBRACE))\n\treturn initState[C, T]\n}\n```\n\n\u003e Similarly, `stateRBRACE` tokenizes the `}` character:\n\n```go\n// stateRBRACE describes the StateFn to check for and emit an RBRACE token\nfunc stateRBRACE[C TextToken, T rune](l lex.Lexer[C, T]) lex.StateFn[C, T] {\n\tl.Next() // skip this symbol\n\tl.Emit((C)(TokenRBRACE))\n\treturn initState[C, T]\n}\n```\n\n\u003e Finally `stateError` tokenizes an error if found (none in this lexer's example)\n\n```go\n// stateError describes an errored state in the lexer / parser, ignoring this set of tokens and emitting an\n// error item\nfunc stateError[C TextToken, T rune](l lex.Lexer[C, T]) lex.StateFn[C, T] {\n\tl.Backup()\n\tl.Prev() // mark the previous char as erroring token\n\tl.Emit((C)(TokenError))\n\treturn initState[C, T]\n}\n```\n\n### Parser\n\n#### Parse functions\n\n\u003e Just like the lexer, start by defining a top-level ParseFn that will scan for all expected tokens\n\u003e\n\u003e This function will peek into the next item from the lexer and return the appropriate ParseFn before actually consuming the token\n\n```go\n// initParse describes the ParseFn to kick off the parser. It is also the default fallback \n// for any other ParseFn\nfunc initParse[C TextToken, T rune](t *parse.Tree[C, T]) parse.ParseFn[C, T] {\n\tfor t.Peek().Type != C(TokenEOF) {\n\t\tswitch t.Peek().Type {\n\t\tcase (C)(TokenIDENT):\n\t\t\treturn parseText[C, T]\n\t\tcase (C)(TokenLBRACE), (C)(TokenRBRACE):\n\t\t\treturn parseTemplate[C, T]\n\t\t}\n\t}\n\treturn nil\n}\n```\n\n\u003e `parseText` simply consumes the item as a new node under the current.\n\n```go\n// parseText consumes the next item as a text token, creating a node for it under the\n// current one in the tree. \nfunc parseText[C TextToken, T rune](t *parse.Tree[C, T]) parse.ParseFn[C, T] {\n\tt.Node(t.Next())\n\treturn initParse[C, T]\n}\n```\n\n\u003e `parseTemplate` is a state where we're about to consume either a `{` or a `}`. \n\u003e\n\u003e For `{` tokens, a template Node is created, as it will be a wrapper for one or more text or template items. Returns the initial state.\n\u003e\n\u003e For `}` tokens, the node that is parent to the `{` is set as the current position (closing the template)\n\n```go\n// parseTemplate creates a node for a template item, for which it expects both a text item edge\n// that which also needs to contain an end-template edge.\n//\n// If it encounters a `}` token to close the template, it sets the position up three levels\n// (back to the template's parent)\nfunc parseTemplate[C TextToken, T rune](t *parse.Tree[C, T]) parse.ParseFn[C, T] {\n\tswitch t.Peek().Type {\n\tcase (C)(TokenLBRACE):\n\t\tt.Set(t.Parent())\n\t\tt.Node(t.Next())\n\tcase (C)(TokenRBRACE):\n\t\tt.Node(t.Next())\n\t\tt.Set(t.Parent().Parent.Parent)\n\t}\n\treturn initParse[C, T]\n}\n```\n\n#### Process functions\n\n\u003e `processFn` is the *top-level* processor function, that will consume the nodes in the Tree.\n\u003e\n\u003e It will use a strings.Builder to create the returned string, and iterate through the Tree's\n\u003e root Node's edges and switching on its Type.\n\u003e\n\u003e The content written to the strings.Builder comes from the appropriate `NodeFn` for the Node type.\n\n```go\n// processFn is the ProcessFn that will process the Tree's Nodes, returning a string and an error\nfunc processFn[C TextToken, T rune, R string](t *parse.Tree[C, T]) (R, error) {\n\tvar sb = new(strings.Builder)\n\tfor _, n := range t.List() {\n\t\tswitch n.Type {\n\t\tcase (C)(TokenIDENT):\n\t\t\tproc, err := processText[C, T, R](n)\n\t\t\tif err != nil {\n\t\t\t\treturn (R)(sb.String()), err\n\t\t\t}\n\t\t\tsb.WriteString((string)(proc))\n\t\tcase (C)(TokenLBRACE):\n\t\t\tproc, err := processTemplate[C, T, R](n)\n\t\t\tif err != nil {\n\t\t\t\treturn (R)(sb.String()), err\n\t\t\t}\n\t\t\tsb.WriteString((string)(proc))\n\t\t}\n\t}\n\n\treturn (R)(sb.String()), nil\n}\n```\n\n\u003e for text it's straight-forward, it just casts the T-type values as rune, and returns a string value of it\n\n```go\n// processText converts the T-type items into runes, and returns a string value of it\nfunc processText[C TextToken, T rune, R string](n *parse.Node[C, T]) (R, error) {\n\tvar val = make([]rune, len(n.Value), len(n.Value))\n\tfor idx, r := range n.Value {\n\t\tval[idx] = (rune)(r)\n\t}\n\treturn (R)(val), nil\n}\n```\n\n\u003e for templates, a few checks need to be made -- in this particular example it is to ensure that templates are terminated.\n\u003e \n\u003e the `processTemplate ProcessFn` does that exactly -- it replaces the wrapper text with the appropriate content, adds in the text in the next node, and looks into that text node's edges for a `}` item (to mark the template as closed). Otherwise returns an error:\n\n```go\n// processTemplate prcesses the text within two template nodes\n//\n// Returns an error if a template is not terminated appropriately\nfunc processTemplate[C TextToken, T rune, R string](n *parse.Node[C, T]) (R, error) {\n\tvar sb = new(strings.Builder)\n\tvar ended bool\n\n\tsb.WriteString(\"\u003e\u003e\")\n\tfor _, node := range n.Edges {\n\t\tswitch node.Type {\n\t\tcase (C)(TokenIDENT):\n\t\t\tproc, err := processText[C, T, R](node)\n\t\t\tif err != nil {\n\t\t\t\treturn (R)(sb.String()), err\n\t\t\t}\n\t\t\tfor _, e := range node.Edges {\n\t\t\t\tif e.Type == (C)(TokenRBRACE) {\n\t\t\t\t\tended = true\n\t\t\t\t}\n\t\t\t}\n\t\t\tsb.WriteString((string)(proc))\n\t\tcase (C)(TokenLBRACE):\n\t\t\tproc, err := processTemplate[C, T, R](node)\n\t\t\tif err != nil {\n\t\t\t\treturn (R)(sb.String()), err\n\t\t\t}\n\t\t\tsb.WriteString((string)(proc))\n\t\t}\n\t}\n\tif !ended {\n\t\treturn (R)(sb.String()), fmt.Errorf(\"parse error on line: %d\", n.Pos)\n\t}\n\n\tsb.WriteString(\"\u003c\u003c\")\n\treturn (R)(sb.String()), nil\n}\n```\n\n#### Wrapper\n\n\u003e Perfect! Now all components are wired-up among themselves, and it just needs a simple entrypoint function\n\u003e\n\u003e For this, we can use the template `Parse` function to run it all at once:\n\n```go\n// Run parses the input templated data (a string as []rune), returning\n// a processed string and an error\nfunc Run[C TextToken, T rune, R string](s []T) (R, error) {\n\treturn parse.Run(\n\t\ts,\n\t\tinitState[C, T],\n\t\tinitParse[C, T],\n\t\tprocessFn[C, T, R],\n\t)\n}\n```\n\n## Benchmarks\n\n### Lex + Parse benchmark\n\n```\ngoos: linux\ngoarch: amd64\npkg: github.com/zalgonoise/parse/example/simple-template\ncpu: AMD Ryzen 3 PRO 3300U w/ Radeon Vega Mobile Gfx\nPASS\nbenchmark                                 iter        time/iter   bytes alloc          allocs\n---------                                 ----        ---------   -----------          ------\nBenchmarkLexParseAndProcess/Simple-4    284025    5667.00 ns/op     1448 B/op    37 allocs/op\nBenchmarkLexParseAndProcess/Complex-4    49509   25624.00 ns/op     4696 B/op   153 allocs/op\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzalgonoise%2Fparse","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzalgonoise%2Fparse","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzalgonoise%2Fparse/lists"}