{"id":37022977,"url":"https://github.com/julianthome/inmemantlr","last_synced_at":"2026-01-14T02:45:14.413Z","repository":{"id":9145902,"uuid":"57291674","full_name":"julianthome/inmemantlr","owner":"julianthome","description":"ANTLR as a libray for JVM based languages","archived":false,"fork":false,"pushed_at":"2023-04-17T04:55:35.000Z","size":1040,"stargazers_count":108,"open_issues_count":4,"forks_count":23,"subscribers_count":13,"default_branch":"master","last_synced_at":"2025-07-08T19:05:46.137Z","etag":null,"topics":["antlr","ast","ast-generation","generic-parser","grammar","parser-generator","parsing"],"latest_commit_sha":null,"homepage":"","language":"Java","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/julianthome.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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}},"created_at":"2016-04-28T10:08:26.000Z","updated_at":"2025-06-26T11:34:43.000Z","dependencies_parsed_at":"2024-06-18T21:35:05.689Z","dependency_job_id":"6d2daf09-6da8-41d7-a21c-6eee903bfc38","html_url":"https://github.com/julianthome/inmemantlr","commit_stats":{"total_commits":516,"total_committers":12,"mean_commits":43.0,"dds":0.08720930232558144,"last_synced_commit":"2e23ab407ed4200f5808e8a0bfd5bf6ea0d39e4d"},"previous_names":[],"tags_count":17,"template":false,"template_full_name":null,"purl":"pkg:github/julianthome/inmemantlr","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/julianthome%2Finmemantlr","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/julianthome%2Finmemantlr/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/julianthome%2Finmemantlr/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/julianthome%2Finmemantlr/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/julianthome","download_url":"https://codeload.github.com/julianthome/inmemantlr/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/julianthome%2Finmemantlr/sbom","scorecard":{"id":541993,"data":{"date":"2025-08-11","repo":{"name":"github.com/julianthome/inmemantlr","commit":"2e23ab407ed4200f5808e8a0bfd5bf6ea0d39e4d"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3,"checks":[{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Code-Review","score":0,"reason":"Found 1/25 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENCE.md:0","Info: FSF or OSI recognized license: MIT License: LICENCE.md:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 8 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":7,"reason":"3 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-78wr-2p64-hpwj","Warn: Project is vulnerable to: GHSA-j288-q9x7-2f5v","Warn: Project is vulnerable to: GHSA-599f-7c49-w659"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-20T08:25:07.226Z","repository_id":9145902,"created_at":"2025-08-20T08:25:07.226Z","updated_at":"2025-08-20T08:25:07.226Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28408734,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T01:52:23.358Z","status":"online","status_checked_at":"2026-01-14T02:00:06.678Z","response_time":107,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["antlr","ast","ast-generation","generic-parser","grammar","parser-generator","parsing"],"created_at":"2026-01-14T02:45:13.844Z","updated_at":"2026-01-14T02:45:14.402Z","avatar_url":"https://github.com/julianthome.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# inmemantlr\n\ninmemantlr provides the functionality of [ANTLR v4](http://www.antlr.org/) \nthrough a simple API. Usually ANTLR requires the user to perform the grammar generation and compilation \nsteps. Instead, inmemantlr does all of these steps automatically\nand in-memory while keeping all of the original ANTLR objects accessible through\nits `GenericParser` class which is serializable, and hence, can be reused at a\nlater point in time or across different applications. inmemantlr can be used\nvia an easy-to-use Java API or as command-line tool.\n\nMoreover, you can easily generate a parse tree from a parsed file and convert\nit into various formats such as `.dot`, `.xml` or `.json`. A parse tree\ncan be processed/translated by means of inmemantlr's `ParseTreeProcessor` class.\n\nAll of the above-mentioned inmemantlr features are illustrated by\n[examples](#toc). inmemantlr is ready to use for all of the\n[grammars-v4](https://github.com/antlr/grammars-v4) grammars (for detailed\nexamples please have a look at [grammars-v4](#grammars-v4)).\n\n# Status\n\n[![Linux Build Status](https://api.travis-ci.org/julianthome/inmemantlr.svg?branch=master)][travis]\n[![Test Coverage](https://codecov.io/gh/julianthome/inmemantlr/branch/master/graph/badge.svg)][coverage]\n[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.github.julianthome/inmemantlr/badge.svg)][mcentral]\n\n[travis]: https://travis-ci.org/julianthome/inmemantlr\n[coverage]: https://codecov.io/gh/julianthome/inmemantlr\n[mcentral]: https://maven-badges.herokuapp.com/maven-central/com.github.julianthome/inmemantlr\n\n# Authors and major contributors\n\n- Julian Thome, julian.thome@gmail.com, main author\n- [Alan Stewart](https://github.com/alankstewart) (code style improvements, performance improvements, Tool customization)\n- Radoslaw Cymer (bugfixes, code style improvements, typos in Javadoc)\n- [Nikolas Havrikov](https://github.com/havrikov) (code style, bugfixes, performance improvements)\n- [Calogero G. Zarba](https://github.com/calogero81) (feature improvements)\n- [dafei1288](https://github.com/dafei1288) (Maintainability)\n\n# TOC\n\n[Integration](#integration)\n\n[API Usage Scenarios](#api-usage-scenarios)\n  * [Get started](#get-started)\n  * [Simple parsing](#simple-parsing)\n  * [Parse tree generation](#parse-tree-generation)\n  * [Parse tree serialization](#parse-tree-serialization)\n  * [Parse tree pruning](#parse-tree-pruning)\n  * [Parse tree processing](#parse-tree-processing)\n  * [Sequential parsing](#sequential-parsing)\n  * [Non-combined grammars](#non-combined-grammars)\n  * [Lexer-only grammars](#lexer-only-grammars)\n  * [Accessing ANTLR objects](#accessing-antlr-objects)\n  * [Parser serialization](#parser-serialization)\n  * [grammars-v4](#grammars-v4)\n\n[Licence](#licence)\n\n# Integration\n\n## Maven \ninmemantlr is available on maven central and can be integrated by\nusing the following dependency in the `pom.xml` file. Note, that the maven\nreleases do not necessarily contain the newest changes that are available in\nthe repository. The maven releases are kept in sync with the tagged\n[releases](https://github.com/julianthome/inmemantlr/releases). The API\ndocumentation for every release is avalable\n[here](http://www.javadoc.io/doc/com.github.julianthome/inmemantlr). However,\nthe content of this documentation, in particular the code examples and usage\nscenarios, is always aligned with the master branch of this repository. Hence,\nit might be that the latest inmemantlr features are not yet available through\nthe maven package.\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003ecom.github.julianthome\u003c/groupId\u003e\n    \u003cartifactId\u003einmemantlr-api\u003c/artifactId\u003e\n    \u003cversion\u003e1.9.2\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n# API Usage Scenarios\n\nThe following code snippets shows an example how to use the API of inmemantlr;\ndescriptions are provided as source code comments. For the sake of simplicity,\nexception handling is omitted for all of the following examples.\n\n## Get started\n\nThe code sample below shows how you could get started with inmemantlr. The\nclass `GenericParserToGo` (available as of release 1.6) provides a very simple API that should be sufficient\nfor most of the use cases: you only have to provide the ANTLR grammar file in\nconjunction with the file/string to parse, and a call to `parse()` (with\nthe string and starting-rule as parameters) will return the corresponding parse\ntree.\n\n\n```java\nFile gfile = new File(\"Java.g4\");\nFile cfile = new File(\"HelloWorld.java\");\nParseTree pt = new GenericParserToGo(gfile).parse(cfile, \"compilationUnit\");\n```\n\n\n## Simple parsing\n\n``` java\n// 1. load grammar\nFile f = new File(\"Java.g4\");\nGenericParser gp = new GenericParser(f);\n// 2. load file content into string\nString s = FileUtils.loadFileContent(\"HelloWorld.java\");\n// 3. set listener for checking parse tree elements. Here you could use any ParseTreeListener implementation. The default listener is used per default\ngp.setListener(new DefaultListener());\n// 4. compile Lexer and parser in-memory\ngp.compile();\n// 5. parse the string that represents the content of HelloWorld.java\nParserRuleContext ctx = gp.parse(s, \"compilationUnit\", GenericParser.CaseSensitiveType.NONE);\n```\n\n## Parse tree generation\n\nWhile providing access to the original `ParseTree` data-structure of ANTLR\nwhich can be obtained through the `ParserRuleContext` object, inmemantlr also\nprovides it's own `ParseTree` data-structure and some ready-to-use utility\nclasses which help with everyday tasks such as pruning, data-conversion,\ntree-traversal and translation.\n\nIf you would like to obtain the `ParseTree` from a parsed file,\nthe following snippet could be of use:\n\n``` java\nFile f = new File(\"Java.g4\");\nGenericParser gp = new GenericParser(f);\nString s = FileUtils.loadFileContent(\"HelloWorld.java\");\n\n// this listener will create a parse tree from the java file\nDefaultTreeListener dlist = new DefaultTreeListener();\n\ngp.setListener(dlist);\ngp.compile();\n\nParserRuleContext ctx = gp.parse(s, \"compilationUnit\", GenericParser.CaseSensitiveType.NONE);\n// get access to the parse tree of inmemantlr\nParseTree pt = dlist.getParseTree();\n```\n\n## Parse tree serialization\n\nAs depicted below, our `ParseTree` implementation can be serialized to various\noutput formats such as `.xml`, `.json` or `.dot`.\n\n```java\nString xml = parseTree.toXml();\nString json = parseTree.toJson();\nString dot = parseTree.toDot();\n```\n\nSerialization may be useful in case you would like to use parsing results\nacross different applications or in case you would like get a quick and simple\nvisualization of your parse tree by means of graphviz as in the example\nbelow. \n\n\u003cimg src=\"https://github.com/julianthome/inmemantlr/blob/master/images/pt.png\" alt=\"Example Parse tree\" width=\"400px\" align=\"second\"\u003e\n\n## Parse tree pruning\n\nIn case you are just interested in particular nodes of the parse tree, it is\npossible to pass a lambda expression as parameter to the `DefaultTreeListener`\nas illustrated in the example below. Only those nodes remain in the parse\ntree for which the lambda expression returns true.\n\n``` java\nprivate Set\u003cString\u003e filter  = new HashSet\u003c\u003e(Arrays.asList(new String []{\n        \"alternation\", \"expr\", \"literal\",\n}));\n// ...\ndlist = new DefaultTreeListener(s -\u003e filter.contains(s));\n// ...\n```\n\n\n## Parse tree processing\n\nWith inmemantlr, you can easily process or translate a given Parse tree by means of an\n`ParseTreeProcessor`. Note, that ANTLR can automatically generate\nvisitors/listeners from a given grammar which you can obtain through the\n`GenericParser` member function `getAllCompiledObjects`. However,\nin case you would like to develop a simple application inmemantlr\n`ParseTreeProcessor` might be sufficient for your use case.\n\nThe following example illustrates how to process a simple\nparse tree that represents a mathematical expression. Given the grammar definition\nbelow, parsing the string `'3+100'` would yield this parse tree:\n\n\u003cimg src=\"https://github.com/julianthome/inmemantlr/blob/master/images/simpleop.png\" alt=\"ParseTree derived from simple expression '3+100'\" width=\"200px\" align=\"second\"\u003e\n\n```\ngrammar Ops;\n\nPlus: '+';\nMinus: '-';\nNumber: '-'?([0-9]|[1-9][0-9]+);\n\ns: (expression)* EOF;\nplus: Plus;\nminus: Minus;\noperation: plus | minus;\nexpression: operand operation operand;\noperand: Number;\n\nWS  :  [ \\t\\r\\n]+ -\u003e skip;\n```\n\nThe following code example illustrates how to compute the result of a\nmathematical expression based on the above-mentioned grammar.\n\n\n```java\n// ...\ngp.compile();\n// this example shows you how one could use inmemantlr for sequential parsing\nParseTree pt;\ngp.parse(\"3+100\");\npt = t.getParseTree();\n// Process the Parse tree bottom-up starting from the leafs up to the root node\nParseTreeProcessor\u003cString, String\u003e processor = new ParseTreeProcessor\u003cString, String\u003e(pt) {\n  @Override\n  public String getResult() {\n    // when all nodes have been processed, the result is available in the smap\n    // value of the root node which is returned here\n    return smap.get(pt.getRoot());\n  }\n  @Override\n  protected void initialize() {\n    // initialize smap - a data structure that keeps track of the intermediate\n    // values for every node\n    pt.getNodes().forEach(n -\u003e smap.put(n, n.getLabel()));\n  }\n  // This operation is executed for each and every node in left to right and\n  // bottom up order. Non-leaf nodes are processed only if all of their siblings\n  // have been already processed\n  @Override\n  protected void process(ParseTreeNode n) {\n    if(n.getRule().equals(\"expression\")){\n      int n0 = Integer.parseInt(smap.get(n.getChild(0)));\n      int n1 = Integer.parseInt(smap.get(n.getChild(2)));\n      int result = 0;\n      switch(smap.get(n.getChild(1))) {\n        case \"+\":\n          result = n0 + n1;\n        break;\n        case \"-\":\n          result = n0 - n1;\n        break;\n      }\n      // store computation result of addition subtraction for current node\n      smap.put(n, String.valueOf(result));\n    } else {\n      // when node is no expression NT, propate child node value 1:1\n      // to parent\n      simpleProp(n);\n    }\n  }\n};\n// compute the result\nprocessor.process();\n// print the computation results which is 103\nSystem.out.println(processor.getResult());\n```\n\nA more practical example on how to use the Parse tree processor can be found within\nmy [CTrans project](https://github.com/julianthome/ctrans) which takes\na given boolean formula and translates it into CNF or DNF, respectively.\n\n## Sequential parsing \n\nIf you have multiple strings to parse one after another,\nthe following code snippet might be useful:\n\n```java\nFile f = new File(\"Simple.g4\");\nGenericParser gp = new GenericParser(f);\n\n// note that the listener should always be set before\n// the compilation. Otherwise, the listener cannot\n// capture the parsing information.\ngp.setListener(new DefaultTreeListener());\ngp.compile();\n\nParseTree pt;\ngp.parse(\"PRINT a+b\");\npt = t.getParseTree();\n// do something with parsing result\n\ngp.parse(\"PRINT \\\"test\\\"\");\npt = t.getParseTree();\n// do something with parsing result\n```\n\n## Non-combined grammars\n\n```java\n// define array of ANTLR files to consider -- inmemantlr will automatically\n// analyses their interdependencies\nFile files [] = {\n  new File(\"MySQLLexer.g4\"),\n  new File(\"MySQLParser.g4\")\n};\n// simply pass files to constructor\nGenericParser gp = new GenericParser(files);\n// parser is ready to use\n```\n\n## Lexer-only grammars\n\nIn case you are interested in only using a lexer grammar, you can use the \n`lex` method which will provide you with a list of tokens.\n\n``` java\nGenericParser gp = new GenericParser(new File(\"LexerGrammar\"));\ngp.compile();\ntry {\n    List\u003cToken\u003e tokens = gp.lex(\"a09\");\n    Assertions.assertEquals(tokens.size(), 2);\n} catch (IllegalWorkflowException e) {\n    Assertions.assertFalse(true);\n}\n```\n\n\n## Accessing ANTLR objects\n\nFor accessing the ANTLR parser/lexer objects, you can use the\n`getAllCompiledObjects` method which will return the source and byte code of\nthe source files that were generated by ANTLR and the corresponding byte code\ngenerated by inmenantlr.\n\n```java\n// get access to ANTLR objects\nMemoryTupleSet set = gp.getAllCompiledObjects();\n// memory tuple contains the generated source code of ANTLR\n// and the associated byte code\nfor(MemoryTuple tup : set) {\n  // get source code object\n  MemorySource = tup.getSource();\n  // get byte code objects\n  Set\u003cMemoryByteCode\u003e bcode = tup.getByteCodeObjects();\n}\n```\n\nThrough the method `writeAntlrArtifactsTo`, which belongs to the `GenericParser`\nclass, inmemantlr also offers the possibility to write the ANTLR artifacts,\ni.e., the Java source files for a grammar/lexer, to a file.\n\n``` java\n// ...\nGenericParser gp = new GenericParser(tc,sgrammarcontent);\n// ...\ngp.writeAntlrAritfactsTo(\"/tmp/grammar\");\n```\n\nAfter the call above, you will find the `.java` files in the specified\ndestination `/tmp/grammar`.\n\n## Parser serialization\n\nFor avoiding unnecessary compilation and for enabling the re-use of a generic\nparser across different Java applications or runs, it is possible to serialize\na generic parser.\n\nA generic parser could be serialized to a file with the following code:\n```java\n// store a generic parser in the file \"/tmp/gp.out\" and\n// overwrite the file if it already exists\ngp.store(\"/tmp/gp.out\", true);\n```\n\nA generic parser can be loaded from a file with the following\ncode:\n\n```java\n// load generic parser from file /tmp/gp.out\nGenericParser gp = GenericParser.load(\"/tmp/gp.out\");\n```\n\n## grammars-v4\n\nThe [grammars-v4](https://github.com/antlr/grammars-v4) repository is added as\na submodule. For executing all the grammars-v4 test cases, one could run the\nfollowing commands from within the `inmemantlr-api` maven module.\n\n```bash\ngit submodule init\ngit submodule update\nmvn -Dtest=TestExternalGrammars test\n```\n\n# Licence\n\nThe MIT License (MIT)\n\nCopyright (c) 2016 Julian Thome \u003cjulian.thome.de@gmail.com\u003e\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies\nof the Software, and to permit persons to whom the Software is furnished to do\nso, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjulianthome%2Finmemantlr","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjulianthome%2Finmemantlr","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjulianthome%2Finmemantlr/lists"}