{"id":24821801,"url":"https://github.com/sebbekarlsson/gpp","last_synced_at":"2025-10-13T20:31:08.444Z","repository":{"id":40050161,"uuid":"289642477","full_name":"sebbekarlsson/gpp","owner":"sebbekarlsson","description":"General PreProcessor","archived":false,"fork":false,"pushed_at":"2023-05-04T23:32:32.000Z","size":236,"stargazers_count":32,"open_issues_count":7,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-12-31T10:07:02.370Z","etag":null,"topics":["interpreter","jinja","jinja2","preprocessor","static-site-generation","static-site-generator","templating","templating-engine","templating-language"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sebbekarlsson.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-08-23T08:04:35.000Z","updated_at":"2024-10-24T03:02:33.000Z","dependencies_parsed_at":"2023-02-04T16:02:03.999Z","dependency_job_id":null,"html_url":"https://github.com/sebbekarlsson/gpp","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/sebbekarlsson%2Fgpp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sebbekarlsson%2Fgpp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sebbekarlsson%2Fgpp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sebbekarlsson%2Fgpp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sebbekarlsson","download_url":"https://codeload.github.com/sebbekarlsson/gpp/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":236396249,"owners_count":19142170,"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":["interpreter","jinja","jinja2","preprocessor","static-site-generation","static-site-generator","templating","templating-engine","templating-language"],"created_at":"2025-01-30T18:02:30.913Z","updated_at":"2025-10-13T20:31:02.967Z","avatar_url":"https://github.com/sebbekarlsson.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# GPP\n\u003e General PreProcessor  \n\u003e _not just for HTML, it can be used anywhere_\n\n```html\n\u003c!--\n    {{ }} = ** Compute Block ** \n          - A block that is interpreted as templating logic\n\n    (@ @) = ** Virtual File Block **\n          - Anything in here is just treated as if it was a completely new file.\n            This block is also used to tell the pre-processor that something\n            should be evaluated \"lazy\".\n\n    ( ) = ** List **\n          - A list of elements, separated by comma.\n          - Aliases: [ ]\n\n    map = ** Built-in function **\n          - It does what you expect, just like the map function in for example\n            Javascript, it takes in an iterable as the first argument and a\n            yield / function as the second argument. \n\n    sort = ** Built-in function **\n          - Sorts an array in alphabetical order.\n\t\t\tIf the array contains a list of objects/dictionaries,\n\t\t\tthe items will be sorted using the first key in each object/dictionary.\n\n    join = ** Built-in function **\n          - Join a list with some value.\n            First argument is an iterable, second argument is anything you want. \n\n    cat = ** Built-in function **\n          - This function simply reads an infinite list of files, concatenates\n            the contents and dumps it out as it is.\n            The contents of the files are just treated as text and not evaluated.\n            This function takes in an infinite list of strings as an argument.\n\n    cate = ** Built-in function **\n          - Just like `cat` but it also evaluates the content of the files\n            before concatenating them and dumping it out.\n\n    floor = ** Built-in function **\n          - Floors a number. For example, the input 60.6 would return 60.\n\t\t  \n\tload = ** Built-in function **\n          - Loads a JSON file.\n\t\t\tThe signature is:\n\t\t\tload (string filename)\n\n    key = ** Built-in function **\n          - Extract a key by index from an object.\n            The signature is:\n            key (object index)\n\n    value = ** Built-in function **\n          - Extract a value by index from an object.\n            The signature is:\n            value (object index)\n\n    get = ** Built-in function **\n          - Extract a value by index or key from an object or list.\n            The signature is:\n            value ((object || list) (index || key))\n            \n    range = ** Built-in function **\n          - Generate a list from 0 to -\u003e N\n            {{ map (range(100), (@\u003cli\u003e{{ $0 }}\u003c/li\u003e@)) }}\n\n\n    $ = ** Positional parameter constant **\n          - This is heavily inspired by the shell, it allows you to access\n            the positional arguments within a function body.\n            For example, to access the first argument you would use `$0`,\n            to access the second one you would use `$1`, and then `$2`...\n            and so on and so forth. \n!--\u003e\n\u003chtml\u003e\n    \u003cbody\u003e\n        \u003ch1\u003e{{ \"Welcome\" }}\u003c/h1\u003e\n        \u003ch2\u003eThe basics\u003c/h2\u003e\n        \u003csection\u003e\n            \u003ch3\u003eVirtual file blocks and compute blocks\u003c/h3\u003e\n            \u003cspan\u003e\n                (@\n                    Here is some text that will be completely ignored.   \n                    This: \u003cb\u003e{{ \"Is not ignored however\" }}\u003c/b\u003e.\n                @)\n            \u003c/span\u003e\n\n            \u003ch3\u003eMapping a list to HTML\u003c/h3\u003e\n            \u003cul\u003e\n               {{ map ([\"apple\", \"banana\", \"pear\",\"orange\"], (@\u003cli\u003e{{ $0 }}\u003c/li\u003e@)) }}\n            \u003c/ul\u003e\n\n            \u003ch3\u003eDumping out file contents\u003c/h3\u003e\n            \u003cpre\u003e{{ cat (\"src/main.c\") }}\u003c/pre\u003e\n\n            \u003ch3\u003eBelow will be evaluated\u003c/h3\u003e\n            \u003cp\u003e{{ cate (\"examples/hello.html\") }}\u003c/p\u003e\n        \u003c/section\u003e\n        \u003ch2\u003eIntegrating other languages\u003c/h2\u003e\n        \u003csection\u003e\n            \u003ch3\u003eI really want this list to be rendered using Python!\u003c/h3\u003e\n            \u003cul\u003e\n            (@#!/usr/bin/python\n            for name in [\"john\", \"sarah\", \"anna\"]:\n                x = \"\"\"(@\n                    \u003cli\u003e\n                        {}     \n                    \u003c/li\u003e\n                @)\"\"\".format(name)\n                print(x)\n            @)\n            \u003c/ul\u003e\n            \u003ch3\u003eI do want this list to be rendered in Node.js though\u003c/h3\u003e\n            \u003cul\u003e\n            (@#!node\n            console.log([\"john\", \"sarah\", \"anna\"]\n              .map(name =\u003e (@ \"\u003cli\u003e\" + name + \"\u003c/li\u003e\" @)).join('\\n'));\n            @)\n            \u003c/ul\u003e\n        \u003c/section\u003e\n    \u003c/body\u003e\n\u003c/html\u003e\n```\n\n## Templating\n### The basics\n\u003e There are a few important tokens in the templating language,\n\u003e and we will go through them in this section.\n#### {{ , }}\n\u003e The `{{` and the `}}` tokens are used to indicate the start `{{` and the end `}}`\n\u003e of a block of templating logic.\n#### (@ , @)\n\u003e The `(@` and the `@)` tokens are used to indicate the start `(@` and the end `@)`\n\u003e of a virtual file block.\n\u003e A virtual file block is something that is just treated as text by the preprocessor.\n\u003e In other words, nothing within these tokens will be interpreted.\n\u003e The entire document is by default wrapped in a virtual file block.  \n\u003e **There is one exception** to the `(@ @)` (virtual file block) however,\n\u003e if you specify a path to an interpreter, the rest of the content will be\n\u003e interpreted. _(There is an example further down in this document)_\n#### If you still do not understand\n\u003e The following:  \n```html\n\u003cp\u003e{{ \"hello\" }}\u003c/p\u003e\n```\n\u003e Will be rendered as:\n```html\n\u003cp\u003ehello\u003c/p\u003e\n```\n\u003e The following:  \n```html\n\u003cp\u003e(@ \"hello\" @)\u003c/p\u003e\n```\n\u003e Will be rendered as:\n```html\n\u003cp\u003e \"hello\" \u003c/p\u003e\n```\n#### Arrow functions\n\u003e You can also define functions to render data,  \n\u003e Here is an example of creating a function to render a `\u003cul\u003e` list:\n```jsx\n{{\n    itemList = (items) =\u003e map (items, (@\n        \u003cli\u003e{{ $0 }}\u003c/li\u003e\n    @))\n}}\n\n{{  itemList ([\"hello\", \"world\"]) }}\n```\n\u003e This will render as:\n```html\n\u003cul\u003e\n    \u003cli\u003ehello\u003c/li\u003e\n    \u003cli\u003eworld\u003c/li\u003e\n\u003c/ul\u003e\n```\n### Extending / Inheritance\n\u003e You can make templates inherit / extend other templates.  \n\u003e Here is how a parent template could look like: (`parent.hml`)\n```html\n\u003chtml\u003e\n    \u003chead\u003e\n        \u003ctitle\u003e(@#%block title @)\u003c/title\u003e\n    \u003c/head\u003e\n    \u003cbody\u003e\n        (@#%block body @)\n    \u003c/body\u003e\n\u003c/html\u003e\n```\n\u003e Notise the `%` at the start of the comment.  \n\u003e And then you can write a child template that inherits from this one  \n\u003e Like this: (`child.html`)\n```html\n#%extends \"parent.html\"\n\n(@#%block title\n    My Title\n@)\n\n(@#%block body\n    Welcome to my website\n@)\n```\n\u003e Also, notice here the `%` at the start of each comment.  \n\u003e If you run `gpp` over `child.html` now, the output will be:\n```html\n\u003chtml\u003e\n    \u003chead\u003e\n        \u003ctitle\u003eMy Title\u003c/title\u003e\n    \u003c/head\u003e\n    \u003cbody\u003eWelcome to my website\u003c/body\u003e\n\u003c/html\u003e\n```\n\n## Loading data\n\u003e What is a templating engine without any data to work with?  \n\u003e To access data in your templates, simply put a `context.json` file\n\u003e within the root of your project.  \n\u003e Example (`context.json`):\n```json\n{\n    \"title\": \"My Title\",\n    \"favouriteFruits\": [\"apple\", \"pear\", \"banana\"]\n}\n```\n\u003e Now you can access these variables in your templates,  \n\u003e Example:\n```html\n\u003chtml\u003e\n    \u003ctitle\u003e{{ title }}\u003c/title\u003e\n    \u003cbody\u003e\n        {{ map (favouriteFruits, (@\u003cli\u003e{{ $0 }}\u003c/li\u003e@)) }}\n    \u003c/body\u003e\n\u003c/html\u003e\n```\n\u003e You can also use the `load` function. Like this:\n```html\n{{\n\tmydata = load(\"somedata.json\")\n}}\n\n\u003ch1\u003e{{ mydata.firstname }}\u003c/h1\u003e\n```\n\n## Any interpreter you want\n\u003e If you do not want to use the default language,\n\u003e you can use any language you want to interpret the templates.\n\u003e Here is an example where we are rendering a `\u003cul\u003e` list using `Python`.\n```html\n\u003chtml\u003e\n    \u003cbody\u003e\n        \u003cul\u003e\n(@#!/usr/bin/python\nfor i in range(100):\n    x = \"\"\"(@\n        \u003cli\u003e\n            {}     \n        \u003c/li\u003e\n    @)\"\"\".format(i)\n    print(x)\n @)\n        \u003c/ul\u003e\n    \u003c/body\u003e\n\u003c/html\u003e\n```\n\u003e To make this work, we simply put a comment inside our `(@ @)` block.\n\u003e This comment should start with a `#!` and then the path to the\n\u003e program that should be interpreting the rest of the content inside of the\n\u003e `(@ @)` block.\n\n## Installation\n\u003e Ready to use this piece of software?\n### 1. Clone it (or download it)\n\u003e Go and get the source code, if you are reading this on Github,  \n\u003e then just copy the clone URL and clone it down.\n```\ngit clone \u003curl\u003e --recurse-submodules\n```\n\u003e Do NOT forget the `--recurse-submodules` flag. \n### 2. Compile it\n\u003e Make sure you have a compiler on your system, preferably `gcc`.\n\u003e You can read more about `gcc` here: [https://gcc.gnu.org/](https://gcc.gnu.org/)  \n\u003e You also need to make sure you have `make` on your system.  \n\u003e You can read more about `make` here: [https://www.gnu.org/software/make/](https://www.gnu.org/software/make/).  \n\u003e **Alright!** time to compile this thing.\n\u003e\n\u003e **Go to the root directory** - Open up a shell and go to the root directory of this project:\n```bash\ncd gpp/\n```\n\u003e **Execute Make** - Run the make command\n```bash\nmake\n```\n\u003e **!!!Abrakadabra!!!**\n### 3. Run it\n\u003e You are now ready to use this,\n\u003e Here is how you do that:\n```bash\n./a.out index.html\n``` \n\u003e Yes... I know `a.out` is not really a production ready name for a binary.  \n\u003e But this software is still in the _work in progress_ stage.  \n\u003e Anyways, so you execute the `a.out` file and you give it a filename as\n\u003e the first argument and then it will just print out the transpiled / compiled\n\u003e content to `stdout`.\n\n## Plans\n\u003e Here are some plans / thoughts.  \n\u003e If you feel like you could help implement some of them, feel free\n\u003e to make a pull-request.   \n- [ ] Write better documentation\n- [ ] Implement a step-by-step debugger\n- [ ] Create a shared library to allow the use of this in other languages\n- [ ] Implement bindings for NodeJS \n- [ ] Implement bindings for Python  \n\u003e Now, these are not tasks / issues.  \n\u003e This is just some ideas and I am just thinking out loud.  \n\u003e Bugs, Todos, and tasks will be seen in Github's issues section.\n\n## FAQ\n```\nIf I define a variable in my Python code,\nhow can I later access it in my NodeJS block\nbelow?\n```\n\u003e That is unfortunately not possible.  \n\u003e If you want to share data between languages, you need to use the built-in\n\u003e templating language to unserialize the data into your code blocks.\n```\nIsn't it unsafe to call the binary specified in the \"!#\" comment?\n```\n\u003e Well, just don't put an unsafe binary there?  \n\u003e This couldn't care less about what logic you write or which\n\u003e binaries you use to interpret the code blocks.\n\u003e This is just a templating language.\n```\nDoes it work on Windows?\n```\n\u003e I dont know, I haven't tried it there.  \n\u003e But I assume it would be tricky to get it to work there,\n\u003e since this software was developed on a Linux machine and the source code\n\u003e has dependencies on C implementations on Linux... you know header files\n\u003e and stuff.\n```\nHow do I send data into the template context?\n```\n\u003e Put a `context.json` file in the root of your project,\n\u003e you can later access all data in this file inside your templates. \n\n## Status\n![Compile and test](https://github.com/sebbekarlsson/gpp/workflows/Compile%20and%20test/badge.svg)\n\n---\n**\u003cdiv style=\"width: 100%; text-align: center;\" align=\"center\"\u003e:coffee: Not Made with Coffee and Love in San Francisco :coffee:\u003c/div\u003e**\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsebbekarlsson%2Fgpp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsebbekarlsson%2Fgpp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsebbekarlsson%2Fgpp/lists"}