{"id":21042012,"url":"https://github.com/bitpuffin/sexpresso","last_synced_at":"2025-05-15T17:30:40.521Z","repository":{"id":6059199,"uuid":"54602478","full_name":"BitPuffin/sexpresso","owner":"BitPuffin","description":"An s-expression library for C++","archived":false,"fork":false,"pushed_at":"2024-05-06T17:53:01.000Z","size":112,"stargazers_count":55,"open_issues_count":4,"forks_count":15,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-04-03T12:01:42.479Z","etag":null,"topics":["modern-cpp","parser","s-expression","s-expressions","serialization"],"latest_commit_sha":null,"homepage":null,"language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"cc0-1.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/BitPuffin.png","metadata":{"files":{"readme":"README.org","changelog":null,"contributing":null,"funding":null,"license":"COPYING.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-03-24T00:30:18.000Z","updated_at":"2024-12-26T05:18:39.000Z","dependencies_parsed_at":"2022-08-06T19:01:08.643Z","dependency_job_id":null,"html_url":"https://github.com/BitPuffin/sexpresso","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BitPuffin%2Fsexpresso","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BitPuffin%2Fsexpresso/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BitPuffin%2Fsexpresso/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BitPuffin%2Fsexpresso/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/BitPuffin","download_url":"https://codeload.github.com/BitPuffin/sexpresso/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254388033,"owners_count":22062971,"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":["modern-cpp","parser","s-expression","s-expressions","serialization"],"created_at":"2024-11-19T13:57:07.849Z","updated_at":"2025-05-15T17:30:40.225Z","avatar_url":"https://github.com/BitPuffin.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"#+TITLE: Sexpresso\n\nSexpresso is c++ centric [[https://en.wikipedia.org/wiki/S-expression][s-expression]] parser library. It uses value types and\nmove semantics and thus manages to completely avoid calling new/delete, so it's probably\nfree from memory leaks!\n\nSexpresso aims to be very simple, nodes are parsed either as s-expressions or strings, even\na number would be parsed a string, so if you expect a node to be a number, please convert the\nstring to a number!\n\n* How to use\n\nBy default Sexpresso uses [[http://doc.cat-v.org/bell_labs/pikestyle][pike style]] which means that it does *not* include any header files\nin its headers by default. If you wanna keep using /pike style/ you'll have to locate the line\nin the header you are about to include that will look something like this:\n\n#+BEGIN_SRC c++\n#ifdef SEXPRESSO_OPT_OUT_PIKESTYLE\n#ifndef SEXPRESSO_HEADER\n#define SEXPRESSO_HEADER\n#include \u003cvector\u003e\n#include \u003cstring\u003e\n#include \u003ccstdint\u003e\n// #include \"sexpresso.hpp\"\n#endif\n#endif\n#+END_SRC\n\nAnd copy all of the includes before you include the actual header. If you don't wanna do this\nand use header guards which can add extra processing time for the pre-processor, and all in all\nmore unclearness of what includes what (since it ends up being a big chain), you can define\n~SEXPRESSO_OPT_OUT_PIKESTYLE~ for the pre-processor.\n\nAfter that you can move onward!\n\n** Parsing\n\n#+BEGIN_SRC c++\nauto parsetree = sexpresso::parse(mysexpr);\n#+END_SRC\n\nThis code will parse the ~std::string~ in mysexpr and return a Sexp struct value.\nThere are two main things you can do with this value you can:\n\n1. Turn it back to a string with ~parsetree.toString()~\n2. Query it using ~parsetree.getChildByPath(\"path/to/node\")~\n\nNumber 2 might be slightly confusing, how does it follow the path? Well if you've ever used lisp,\nyou know that the /command/ in an s-expression is the first element. The same thing here determines\nwhere it goes looking for values. For example if you have an s-expression such as\n\n#+BEGIN_SRC lisp\n(my-values (sweet baby jesus) (hi mom) just-a-thing)\n#+END_SRC\n\nYou can query it like:\n\n#+BEGIN_SRC c++\nauto sub = parsetree.getChildByPath(\"my-values/sweet\");\n#+END_SRC\n\nand\n\n#+BEGIN_SRC c++\nauto sub = parsetree.getChildByPath(\"my-values/hi\");\n#+END_SRC\n\nNote that you get the sexpr node that *contains* the value you\nwere looking for as its first value. The sexpr then simply holds an ~std::vector~ of all the sub-values.\nHowever, it might not always use the vector, if it's simply a string value like ~just-a-thing~ in the\nabove example, then the vector will be empty, and you need to access the string value instead.\n\n#+BEGIN_SRC c++\nif(sub-\u003eisSexp()) {\n  std::cout \u003c\u003c sub-\u003evalue.sexpr[1];\n} else {\n  std::cout \u003c\u003c sub-\u003evalue.str;\n}\n\n// or\n\nswitch(sub-\u003ekind) {\ncase sexpresso::SexpValueKind::SEXP:\n  std::cout \u003c\u003c sub-\u003evalue.sexpr[1];\n  break;\ncase sexpresso::SexpValueKind::STRING:\n  std::cout \u003c\u003c sub-\u003evalue.str;\n}\n#+END_SRC\n\nSexpresso provides a comfortable way to iterate only over the \"arguments of a s-expression.\nFor example if we have an s-expression like ~(hi 1 2 3)~ then the arguments are ~1~, ~2~ and ~3~.\nIf we've parsed and stored that s-expression in a variable called ~hi~, we iterate over its arguments\nlike this:\n\n#+BEGIN_SRC c++\nfor(auto\u0026\u0026 arg : hi.arguments()) {\n  // ..\n}\n\n// or \nfor(auto\u0026\u0026 it = hi.arguments().begin(); it != hi.arguments().end(); ++it) {\n  // ..\n}\n#+END_SRC\n\nYou can also check if the arguments are empty and how many there are with the ~empty~ and ~size~ methods\nof the ~SexpArgumentIterator~ class.\n\n*WARNING* Be *REALLY* careful that your query result does not exceed the lifetime of\nthe parse tree:\n\n#+BEGIN_SRC c++\nSexp* sub;\n{\nauto sexp = sexpresso::parse(mysexpr);\nsub = sexp.getChildByPath(\"my-values/just-a-thing\")\n} // sexp gets destroyed here\ncout \u003c\u003c sub.toString(); // BAD!\n#+END_SRC\n\n** Serializing\nSexp structs have an ~addChild~ method that takes a Sexp method. Furthermore, Sexp has a constructor\nthat takes a std::string, so this should make it really easy to build your own Sexp objects from code that\nyou can serialize with ~toString~.\n\n#+BEGIN_SRC c++\nauto myvalues = sexpresso::Sexp{\"my-values\"};\n\nauto sweet = sexpresso::Sexp{\"sweet\"};\nsweet.addChild(\"baby\");\nsweet.addChild(\"jesus\");\n\nauto hi = sexpresso::Sexp{\"hi\"};\nhi.addChild(\"mom\");\n\nauto justathing = sexpresso::Sexp{\"just-a-thing\"};\n\nauto myvaluesholder = sexpresso::Sexp{};\nmyvaluesholder.addChild(std::move(myvalues));\nmyvaluesholder.addChild(std::move(sweet));\nmyvaluesholder.addChild(std::move(hi));\nmyvaluesholder.addChild(std::move(justathing));\n\nauto sexp = sexpresso::Sexp{};\nsexp.addChild(myvaluesholder);\n\n// sexp should now hold the same s-expression we wrote in text earlier\nstd::cout \u003c\u003c sexp.toString();\n#+END_SRC\n\n*** Important\n\nThe outermost s-expression does not get surrounded by paretheses when calling toString, as it treats a string\nas being implicitly surrounded by parentheses. This is so that you can have multiple s-expressions in the \"root\"\nof your code, and serialization goes back to text the same way it came in. That's why we have the ~sexp~\nin the above code example. If we simply called ~toString~ on ~myvaluesholder~ we would get\n\n#+BEGIN_SRC lisp\nmy-values (sweet baby jesus) (hi mom) just-a-thing\n#+END_SRC\n\ninstead of\n\n#+BEGIN_SRC lisp\n(my-values (sweet baby jesus) (hi mom) just-a-thing)\n#+END_SRC\n\nCool? Cool.\n\n* S-expression primer\n\nConfused? I mean what *iiiis* an s-expression?\n\ns-expressions come from the lisp family of programming languages, it is an\nincredibly simple notation for *lists*, however, since these lists can be nested\nit also means that they are great for representing hierarchies as well, which makes\nit an excellent replacement for XML or JSON.\n\nThe notation is simply to surround the elements, separated by whitespace in parentheses,\nlike this:\n\n#+BEGIN_SRC lisp\n(here we have an s-expression)\n#+END_SRC\n\nWhat you see here is a list of 5 symbols: ~here~, ~we~, ~have~, ~an~ and ~s-expression~.\nLike I said you can also put s-expressions inside s-expressions to create hierarchies:\n\n#+BEGIN_SRC lisp\n(my-objects \n  (object-a (name \"isak andersson\") \n            (countries swe uk)) \n  (object-b (name \"joe bain\")\n            (countries uk)))\n#+END_SRC\n\nAnd as you could see earlier in the [[How to use]] section you can query this hierachy easily with\nthis library. Say that this s-expression is stored in a variable called ~objs~, you can query it like this:\n\n#+BEGIN_SRC lisp\nauto joe = objs.getChildByPath(\"my-objects/object-b/name\");\n#+END_SRC\n\n* FAQ\n** Why should I use s-expressions\nbecause they are more elegant and simple than XML or JSON. Much less work required to parse. And they look nice! (subjective)\n\n* Contributing\nThis library is public domain (CC0). Generally speaking by default, you have copyright on anything you make.\nSo you have to explicitly give up copyrights in order to put something in the public domain.\nIn this repository, please add your signature to [[CONTRIBUTORS.txt]] when contributing.\n\n\n* Future direction\nMake it a header-only library instead perhaps?\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbitpuffin%2Fsexpresso","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbitpuffin%2Fsexpresso","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbitpuffin%2Fsexpresso/lists"}