{ "info": { "author": "Hydra Billing Solutions", "author_email": "", "bugtrack_url": null, "classifiers": [ "Development Status :: 4 - Beta", "Framework :: Pytest", "Intended Audience :: Developers", "License :: OSI Approved :: Apache Software License", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3 :: Only", "Topic :: Software Development", "Topic :: Software Development :: Code Generators" ], "description": "# HYAML\n\nHYAML is an expression-oriented language which serves the purpose of writing consice config files without introducing all the power of a fully-featured scripting language. In a nutshell, it's an extensible DSL for writing configs.\n\nThe syntax is inspired by the Python programming language. At the same time, it lacks a lot of features Python has since they're not relevant to the common configuration tasks. For example, you won't see list expressions or lambdas here.\n\nInitially, HYAML was built with the library named [CodeTalker](https://pypi.org/project/CodeTalker/). That lib got the job done, however, its main purpose was to be as fast as possible. To be the fastest language parser around CodeTalker used Cython underneath that is essentially C with Python-like syntax. Since Cython, just as C, is a compiled language, it compiles CodeTalker's sources on installation. As time went by, this became a major pain point for maintenance. In addition to that, HYAML wasn't meant to be _that_ fast because of the way it's used: after reading a config file all HYAML-expressions are translated to regular Python functions. This is done once on startup and it's usually _fast enough_. Eventually, HYAML's backend was switched to [ANTLR](https://www.antlr.org/).\n\n## Installation\n\nUse pip to install HYAML. It's tested against Python 3.7 and work with earlier versions is not guaranteed.\n\n```\npip install hyaml\n```\n\n### Work-in-progress version\n\nTo install a version that hasn't yet been published to the main index run the following command:\n\n```\npip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple hyaml\n```\n\n## Language\n\nHYAML is an expression-oriented language or, to put it another way, it's a one-liner-oriented language. It doesn't support statements or things like assignments (directly, though it can be enhanced in certain ways).\n\n## Features\n\n### Primitives\n\nThe exhaustive list of supported primitive values:\n\n- Numbers, including integers and floats (use regular notation, as in `1`, `-1`, `1.0`).\n- Strings, enclosed by single or double quotes (`'foo'`, `\"bar\"`).\n- Boolean values are represented as `true` and `false`.\n- Variables (`$var_name`).\n\n### Basic math\n\nAddition, subtraction, multiplication, and division are backed by Python. Pay special attention to division since it's translated to `/` which is float point division rather than integer one (namely, `//`). It works like this for historical reasons only.\n\n```\n1 + 5 # addition\n-3--5 # subtraction, translated to \"-3 - -5\", use if you don't like spaces\n5 * 2 # multiplication\n10 / 2 # division\n```\n\n### Logical operators\n\nLogical operators are `and`, `or`, and `not`, just as in Python:\n\n```\n$a and $b # conjunction\n$b or $c # disjunction\nnot $a # negation\n$a and ($b or $c) # grouping\n$foo and not false # negation precedes other operations, conjunction precedes disjunction\n```\n\n### Comparsison\n\nAgain, as in Python and most modern programming languages\n\n```\n1 > 2 # > stands for greater\n1 >= 2 # greater or equal\n1 < 2 # less\n1 <= 2 # less or equal\n1 == 2 # equal\n1 != 2 # not equal\n```\n\nRange comparisons are supported :tada:\n\n```\n10 > $a > 5 # checks whether $a less than 10 and greater than 5\n```\n\n### Lists and dicts\n\nCompared to the previous version of HYAML lists (aka arrays) and dicts (aka associative arrays, maps, and hashmaps) are first-level citizens :tada::\n\n```\n[1, 2, 3] + [$x, $y, $z] # concatenation of two lists\n{abc: 123} # creates a dict in python {\"abc: 123\"}\n```\n\nNote that dict keys don't require quotes and support hyphens and colons as non-terminating symbols. In other words, `{Sub-Attr:1: 500}` is translated to `{\"Sub-Attr:1\": 500}`.\n\n### Accessing attributes\n\nA common task when working with HYAML is accessing nested values in a dict. This is what `.` does:\n\n```\n$dict_var.Dict-Key-Name.Nested-Key-Name:1\n```\n\nThe expression above corresponds to the following Python code\n\n```python\ndict_var[\"Dict-Key-Name\"][\"Nested-Key-Name:1\"]\n```\n\n`-`, `_`, `:` are special characters allowed for attribute names. When a dict doesn't have the key requested, it will produce a runtime error.\n\n### Calling methods\n\nValues in HYAML can have methods. Essentially, they are not methods at all but regular functions. There's nothing special about method calls except for adding parentheses is required even if the method doesn't take arguments.\n\n```\n\"abc\".substring(1)\n$var.upper()\n'0xfade114'.to_i(16)\n```\n\nMethods returning booleans usually end with `?`:\n\n```\n1.odd?()\n```\n\n### Work around null\n\n`null` value (known in Python as `None`) is a common source of errors when the value you're working with is not set. It's a burden for both static (such as C or C++) and dynamic (such as Python or JavaScript) languages. Nowadays, many of them offer a concise form of checks for value presence (null-checks). HYAML follows the trend and has support for the [safe navigation operator](https://en.wikipedia.org/wiki/Safe_navigation_operator):\n\n```\n$dict_var?.Key-Name:1\n```\n\nIn the example above, if `$dict_var` is `None` or doesn't contain `Key-Name:1` this won't produce an error. Of course, you can have chains when accessing nested attributes:\n\n```\n$dict_var?.Key?.Nested-Key # this is safe now, yay!\n```\n\nThe operators works for methods as well:\n\n```\n$dict_var?.substring(4)?.size() or 0\n```\n\n## API\n\n### Translator API\n\nTranslator takes a (presumably) valid string HYAML and generates a (most likely) valid Python expression:\n\n```python\nfrom hyaml.translator import translate\n\ntranslate(\"$foo\")\n# get(variables, 'foo')\ntranslate(\"1.odd?()\")\n# is_odd(1)\n```\n\n### Compiler API\n\nCompiler takes one step further and makes a function from the given expression:\n\n```python\nfrom hyaml.compiler import compile\n\nnine_plus_five = compiler(\"9 + 5\")\nnine_plus_five()\n# 9\n```\n\nFor using variables in expressions you'll need to add bindings to functions:\n\n```python\nfrom hyaml.compiler import Compiler\ncompile = Compiler(bindings=(\"variables\",))\n\ninc = compile(\"$var + 1\")\ninc({\"var\": 1})\n# 2\ninc({\"var\": 10})\n# 11\n```\n\nIn order to work with methods you'll need to provide a table of functions to the compiler:\n\n```python\nfrom hyaml.compiler import Compiler\n\nmethods = {\n \"square\": lambda x: x ** 2,\n \"is_like\": lambda x, y: x.startswith(y)\n}\ncompile = Compiler(method_name=methods)\n\nsquare = compile(\"5.square()\")\nsquare()\n# 25\nlike = compiler(\"'abcdef'.like?('ab')\")\nlike()\n# True\n```\n\nFinally, using variables and methods together:\n\n```python\nfrom hyaml.compiler import Compiler\n\nmethods = {\n \"square\": lambda x: x ** 2,\n \"is_like\": lambda x, y: x.startswith(y)\n}\ncompile = Compiler(method_name=methods, bindings=(\"variables\",))\nsquare = compile(\"$x.square()\")\nsquare({\"x\": 10})\n# 100\nsquare({\"x\": 7})\n# 49\nlike = compile(\"'abcdef'.like?($str)\")\nlike({\"str\": \"ab\"})\n# True\nlike({\"str\": \"abc\"})\n# True\n```\n\nFor those who curious, there's a module named \"prelude\" which holds globally available methods. They are used for implementing some language features. Specifically, the subscription operator (aka square brackets) relies on `get`, safe navigation to attributes and method calls relies on `safe_get` and `safe_call` respectively. You can override those methods with your own variants using `method_table` (but you cannot remove them, why would you anyway?).\n\n## Running tests\n\n```bash\npython -m unittest tests/test_*\n```\n\n## Playground\n\nHYAML is provided with interactive shell for testing out the language, fire it up with `hyaml`\n\n```\n$ hyaml\n\n>>> evaluate(\"$x.starts_with?('foo')\", x=\"foobar\")\nTrue\n>>>\n```\n\n## ANTLR\n\nTL;DR: ANTLR is a tool for building language parsers. Be sure you visited its [website](https://www.antlr.org/) and checked out the [source code](https://github.com/antlr/antlr4). ANTLR is written in Java but can generate parsers in Python. Compared to CodeTalker it has plenty of [examples](https://github.com/jszheng/py3antlr4book) and a ton of answered question at StackOverflow.\n\nIn order to build a new parser, follow the instructions (taken from the ANTLR's github [page](https://github.com/antlr/antlr4/blob/master/doc/python-target.md)):\n\n- Install ANTLR from https://www.antlr.org/.\n- Define your language grammar in the .g4 format (there's an extension for VSCode!).\n- Generate the output by running\n ```bash\n antlr4 -Dlanguage=Python3 MyGrammar.g4\n ```\n- Add all generated/updated files to commit.\n- You're all set!\n\n## G4\n\nOne can the documentation or even read a book about ANTLR but building a new language can be done by examples. At least, this is the way Hyaml.g4 was written.\n\n## Lexer/Parser API\n\nA simple example of using generated lexer and parser follows:\n\n```python\nimport sys\nfrom antlr4 import *\nfrom HyamlLexer import HyamlLexer\nfrom HyamlParser import HyamlParser\n\ndef main(argv):\n input = FileStream(argv[1])\n ### Create a lexer instance and pass an IO object to its constructor\n lexer = HyamlLexer(input)\n stream = CommonTokenStream(lexer)\n ### Create a parser and feed it with tokens\n parser = HyamlParser(stream)\n tree = parser.prog()\n lisp_tree_str = tree.toStringTree(recog=parser)\n ### Print a simple tree representation\n print(lisp_tree_str)\n\nif __name__ == '__main__':\n main(sys.argv)\n```\n\nRunning this script with `python parser-example.py sample.txt` prints something like:\n```\n(prog (expr (expr $cdr) (callChain (link (methodCall . some (arguments ( (exprList (expr (listLiteral [ ]))) )))))) \\n)\n```\n\nFor transforming parsed structures you'll need a listener.\n\n## Listener API\n\nAlong with a lexer and a parser, ANTLR generates a listener, a class which can be used to track the parsing process. In order to use it, create a subclass for the generated listener and implement necessary hook methods:\n\n```python\nclass Listener(MyGrammarListener):\n def __init__(self):\n self._initial_state = MyState()\n\n def enterMyExpression(self, ctx):\n self._modifyStateAsNeeded()\n\n def exitMyExpression(self, ctx):\n self._modifyStateAsNeeded()\n```\n\nCheck out `translator.py` to see a real example, it's rather straightforward.\n\n## Development\n\nHYAML works with Python 3.7+. Install Python and pip then run\n\n```bash\npip install -e .[dev]\n```\n\n## Packaging and publishing\n\nAccording to the [docs](https://packaging.python.org/tutorials/packaging-projects/), run the following commands:\n\n```\npip install --upgrade setuptools wheel twine\npython3 setup.py sdist bdist_wheel\n```\n\nYou may want to publish the package to the Test PyPi repo first:\n\n```\ntwine upload --repository-url https://test.pypi.org/legacy/ dist/*\n```\n\nIf you sure and ready, publish it to the main index:\n\n```\ntwine upload --repository-url https://pypi.org/legacy/ dist/*\n```\n\nNote that for publishing you'll be asked for your credentials for access to PyPi repositories. See additional [instructions](https://packaging.python.org/guides/using-testpypi/#setting-up-testpypi-in-pypirc) on managing credentials.\n\n## Contributing\n\nBug reports and pull requests are welcome on GitHub at .\n\n\n", "description_content_type": "", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/latera/hyaml", "keywords": "antlr language one-liner hydra hyaml", "license": "", "maintainer": "", "maintainer_email": "", "name": "hyaml", "package_url": "https://pypi.org/project/hyaml/", "platform": "", "project_url": "https://pypi.org/project/hyaml/", "project_urls": { "Homepage": "https://github.com/latera/hyaml" }, "release_url": "https://pypi.org/project/hyaml/0.1.5/", "requires_dist": [ "antlr4-python3-runtime (>=4.7.1)", "black ; extra == 'dev'" ], "requires_python": "", "summary": "HYAML is a one-liner oriented language", "version": "0.1.5" }, "last_serial": 4732111, "releases": { "0.1.5": [ { "comment_text": "", "digests": { "md5": "128e38a619c15329577b58531bc43571", "sha256": "2d7b959511b0c333310baeb0e3f3752f1679ff218868871f2b98ae9731dd5b17" }, "downloads": -1, "filename": "hyaml-0.1.5-py3-none-any.whl", "has_sig": false, "md5_digest": "128e38a619c15329577b58531bc43571", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 26569, "upload_time": "2019-01-23T16:22:53", "url": "https://files.pythonhosted.org/packages/96/a1/75366873897007887198a460abdd8b0fc51a95607b1c906f19d152ab857f/hyaml-0.1.5-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "859f2120924c98f2bdba21523b0e5b8a", "sha256": "e62ca658575fd52b8c99a4d0e5fa238d8b793033ff6e34effbb41b0a8035a9ce" }, "downloads": -1, "filename": "hyaml-0.1.5.tar.gz", "has_sig": false, "md5_digest": "859f2120924c98f2bdba21523b0e5b8a", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 23900, "upload_time": "2019-01-23T16:22:56", "url": "https://files.pythonhosted.org/packages/f5/59/d5295e2ddcc1f6406bb0066796df8cd703ff1c7d18089bfe893d6230384f/hyaml-0.1.5.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "128e38a619c15329577b58531bc43571", "sha256": "2d7b959511b0c333310baeb0e3f3752f1679ff218868871f2b98ae9731dd5b17" }, "downloads": -1, "filename": "hyaml-0.1.5-py3-none-any.whl", "has_sig": false, "md5_digest": "128e38a619c15329577b58531bc43571", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 26569, "upload_time": "2019-01-23T16:22:53", "url": "https://files.pythonhosted.org/packages/96/a1/75366873897007887198a460abdd8b0fc51a95607b1c906f19d152ab857f/hyaml-0.1.5-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "859f2120924c98f2bdba21523b0e5b8a", "sha256": "e62ca658575fd52b8c99a4d0e5fa238d8b793033ff6e34effbb41b0a8035a9ce" }, "downloads": -1, "filename": "hyaml-0.1.5.tar.gz", "has_sig": false, "md5_digest": "859f2120924c98f2bdba21523b0e5b8a", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 23900, "upload_time": "2019-01-23T16:22:56", "url": "https://files.pythonhosted.org/packages/f5/59/d5295e2ddcc1f6406bb0066796df8cd703ff1c7d18089bfe893d6230384f/hyaml-0.1.5.tar.gz" } ] }