{"id":28646713,"url":"https://github.com/igrishaev/toy-parser","last_synced_at":"2025-06-13T02:06:39.451Z","repository":{"id":296995065,"uuid":"977210248","full_name":"igrishaev/toy-parser","owner":"igrishaev","description":null,"archived":false,"fork":false,"pushed_at":"2025-06-03T09:35:36.000Z","size":19,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-06-03T20:16:17.309Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/igrishaev.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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,"zenodo":null}},"created_at":"2025-05-03T17:12:46.000Z","updated_at":"2025-06-03T09:35:37.000Z","dependencies_parsed_at":"2025-06-03T20:16:19.356Z","dependency_job_id":"be1a6270-48ff-4418-a2fe-0005db6e9ded","html_url":"https://github.com/igrishaev/toy-parser","commit_stats":null,"previous_names":["igrishaev/toy-parser"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/igrishaev/toy-parser","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/igrishaev%2Ftoy-parser","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/igrishaev%2Ftoy-parser/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/igrishaev%2Ftoy-parser/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/igrishaev%2Ftoy-parser/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/igrishaev","download_url":"https://codeload.github.com/igrishaev/toy-parser/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/igrishaev%2Ftoy-parser/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":259565562,"owners_count":22877347,"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":[],"created_at":"2025-06-13T02:06:38.734Z","updated_at":"2025-06-13T02:06:39.428Z","avatar_url":"https://github.com/igrishaev.png","language":"Clojure","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Toy Parser\n\nA simple, immutable combinator parser for educational purpose.\n\nThe `api` namespace provides a number of functions to build parsers. Each parser\nis a function accepting parameters and returning a parsing function. The parsing\nfunction is always called with a sequence of characters. The result is either a\nvector `[result, chars-rest]`, where:\n\n- `result` is what the parser has taken from the characters sequence;\n- `chars-rest` is the rest of the initial sequence of characters.\n\nShould a parser fail, it turns nil. Not so informative but it's up to you how to\nimprove it.\n\nKeep in mind, this is **not a library** but rather an example of excercise\nstudents usually do. Surely there is ChatGPT for that, but I hope you'll want to\ndo it by your own.\n\nThe parser ships two demos (demo1 and demo2) with examples how to parse postfix and\ninfix notations.\n\n# A Small Example Of Infix Notation:\n\n~~~clojure\n(ns toy-parser.demo1\n  (:require\n   [toy-parser.api :as p]))\n\n;;\n;; Parse a simple infix notation fragment\n;; like '1 2 3 + 3 1 foo -'\n;;\n\n(def p-int\n  ;; very naive\n  (p/+ (p/range \\0 \\9)))\n\n(def p-var\n  (p/+ (p/range \\a \\z)))\n\n(def p-operator\n  (p/nor :minus (p/char \\-)\n         :plus (p/char \\+)\n         :times (p/char \\*)\n         :div (p/char \\\\)))\n\n(def p-operand\n  (p/nor :int p-int\n         :var p-var))\n\n(def p-item\n  (p/nor :operand p-operand\n         :operator p-operator))\n\n(def p-ws\n  (p/+ (p/enum \\space \\r \\n \\t)))\n\n(def p-expression\n  (p/sep p-item p-ws))\n\n\n(defn parse-postfix [string]\n  (-\u003e string\n      (str/trim)\n      (seq)\n      (p-expression)))\n\n(parse-postfix \"1 2 3 + 3 1 foo -\")\n\n[[:operand [:int [\\1]]]\n [:operand [:int [\\2]]]\n [:operand [:int [\\3]]]\n [:operator [:plus \\+]]\n [:operand [:int [\\3]]]\n [:operand [:int [\\1]]]\n [:operand [:var [\\f \\o \\o]]]\n [:operator [:minus \\-]]]\n~~~\n\nNow let's wrap it back:\n\n~~~clojure\n(defn render-postfix [parsed]\n  (str/trim\n   (reduce\n    (fn [acc [tag inner]]\n      (case tag\n        :operand\n        (let [[tag chars] inner]\n          (case tag\n            (:int :var)\n            (str acc (apply str (-\u003e inner second)) \\space)))\n        :operator\n        (str acc (-\u003e inner second) \\space)))\n    \"\"\n    parsed)))\n\n(render-postfix\n   [[:operand [:int [\\1]]]\n    [:operand [:int [\\2]]]\n    [:operand [:int [\\3]]]\n    [:operator [:plus \\+]]\n    [:operand [:int [\\3]]]\n    [:operand [:int [\\1]]]\n    [:operand [:var [\\f \\o \\o]]]\n    [:operator [:minus \\-]]])\n\n\"1 2 3 + 3 1 foo -\"\n~~~\n\n## Postfix Notation\n\n~~~clojure\n(ns toy-parser.demo2\n  (:require\n   [clojure.string :as str]\n   [toy-parser.api :as p]))\n\n;;\n;; Parse a complex infix notation like\n;; (a + b) * (c / 3) + (1 * (a - b))\n;;\n;;\n;; expression\n;;   el ws? operator ws? expression\n;;   el\n;;\n;; el\n;;   group\n;;   operand\n;;\n;; group\n;;   ( ws? expression ws? )\n;;\n;;  operand\n;;    var\n;;    int\n;;\n;; operator\n;;   -\n;;   +\n;;   *\n;;   /\n\n\n(def p-int\n  (p/+ (p/range \\0 \\9)))\n\n(def p-var\n  (p/+ (p/range \\a \\z)))\n\n\n(def p-operand\n  (p/nor :int p-int\n         :var p-var))\n\n(def p-operator\n  (p/nor :minus (p/char \\-)\n         :plus (p/char \\+)\n         :times (p/char \\*)\n         :div (p/char \\\\)))\n\n(def p-ws?\n  (p/* (p/enum \\space \\r \\n \\t)))\n\n;; used for recursive declaration\n(declare p-expr)\n\n(def p-group\n  (p/ncat :lparen (p/char \\()\n          :ws p-ws?\n          :expr (var p-expr)\n          :ws p-ws?\n          :rparen (p/char \\))))\n\n(def p-el\n  (p/nor :group p-group\n         :operand p-operand))\n\n\n(def p-expr\n  (p/nor :triple (p/ncat :el p-el\n                         :ws p-ws?\n                         :operator p-operator\n                         :ws p-ws?\n                         ;; will be an actual parser later\n                         :expr (var p-expr))\n         :el p-el))\n\n\n(defn parse-infix [string]\n  (p-expr string))\n\n(parse-infix \"(a + b) * (b - (a + 42))\")\n\n[:triple\n [[:el\n   [:group\n    [[:lparen \\(]\n     [:ws []]\n     [:expr\n      [:triple\n       [[:el [:operand [:var [\\a]]]]\n        [:ws [\\space]]\n        [:operator [:plus \\+]]\n        [:ws [\\space]]\n        [:expr [:el [:operand [:var [\\b]]]]]]]]\n     [:ws []]\n     [:rparen \\)]]]]\n  [:ws [\\space]]\n  [:operator [:times \\*]]\n  [:ws [\\space]]\n  [:expr\n   [:el\n    [:group\n     [[:lparen \\(]\n      [:ws []]\n      [:expr\n       [:triple\n        [[:el [:operand [:var [\\b]]]]\n         [:ws [\\space]]\n         [:operator [:minus \\-]]\n         [:ws [\\space]]\n         [:expr\n          [:el\n           [:group\n            [[:lparen \\(]\n             [:ws []]\n             [:expr\n              [:triple\n               [[:el [:operand [:var [\\a]]]]\n                [:ws [\\space]]\n                [:operator [:plus \\+]]\n                [:ws [\\space]]\n                [:expr [:el [:operand [:int [\\4 \\2]]]]]]]]\n             [:ws []]\n             [:rparen \\)]]]]]]]]\n      [:ws []]\n      [:rparen \\)]]]]]]]\n~~~\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Figrishaev%2Ftoy-parser","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Figrishaev%2Ftoy-parser","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Figrishaev%2Ftoy-parser/lists"}