Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
KOLANICH committed Oct 13, 2023
0 parents commit 7315678
Show file tree
Hide file tree
Showing 19 changed files with 599 additions and 0 deletions.
12 changes: 12 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
root = true

[*]
charset = utf-8
indent_style = tab
indent_size = 4
insert_final_newline = true
end_of_line = lf

[*.{yml,yaml}]
indent_style = space
indent_size = 2
1 change: 1 addition & 0 deletions .github/.templateMarker
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
KOLANICH/python_project_boilerplate.py
8 changes: 8 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
version: 2
updates:
- package-ecosystem: "pip"
directory: "/"
schedule:
interval: "daily"
allow:
- dependency-type: "all"
15 changes: 15 additions & 0 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
name: CI
on:
push:
branches: [master]
pull_request:
branches: [master]

jobs:
build:
runs-on: ubuntu-22.04
steps:
- name: typical python workflow
uses: KOLANICH-GHActions/typical-python-workflow@master
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
11 changes: 11 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
__pycache__
*.pyc
*.pyo
*.egg-info
build
dist
.eggs
/monkeytype.sqlite3
/*.srctrldb
/*.srctrlbm
/*.srctrlprj
14 changes: 14 additions & 0 deletions .gitlab-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#image: pypy:latest
image: registry.gitlab.com/kolanich-subgroups/docker-images/fixed_python:latest

variables:
DOCKER_DRIVER: overlay2
SAST_ANALYZER_IMAGE_TAG: latest
SAST_DISABLE_DIND: "true"
SAST_CONFIDENCE_LEVEL: 5
CODECLIMATE_VERSION: latest

include:
- template: SAST.gitlab-ci.yml
- template: Code-Quality.gitlab-ci.yml
- template: License-Management.gitlab-ci.yml
1 change: 1 addition & 0 deletions Code_Of_Conduct.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
No codes of conduct!
4 changes: 4 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
include UNLICENSE
include *.md
include tests
include .editorconfig
25 changes: 25 additions & 0 deletions ReadMe.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
antlrCompile.py [![Unlicensed work](https://raw.githubusercontent.com/unlicense/unlicense.org/master/static/favicon.png)](https://unlicense.org/)
===============
~~[wheel (GitLab)](https://gitlab.com/KOLANICH/antlrCompile.py/-/jobs/artifacts/master/raw/dist/antlrCompile-0.CI-py3-none-any.whl?job=build)~~
~~[wheel (GHA via `nightly.link`)](https://nightly.link/UniGrammar-libs/antlrCompile.py/workflows/CI/master/antlrCompile-0.CI-py3-none-any.whl)~~
~~![GitLab Build Status](https://gitlab.com/KOLANICH/antlrCompile.py/badges/master/pipeline.svg)~~
~~![GitLab Coverage](https://gitlab.com/UniGrammar/antlrCompile.py/badges/master/coverage.svg)~~
~~[![GitHub Actions](https://github.com/UniGrammar-libs/antlrCompile.py/workflows/CI/badge.svg)](https://github.com/UniGrammar-libs/antlrCompile.py/actions/)~~
[![Libraries.io Status](https://img.shields.io/librariesio/github/UniGrammar-libs/antlrCompile.py.svg)](https://libraries.io/github/UniGrammar-libs/antlrCompile.py)
[![Code style: antiflash](https://img.shields.io/badge/code%20style-antiflash-FFF.svg)](https://codeberg.org/KOLANICH-tools/antiflash.py)

A python wrapper for [ANTLR 4](https://github.com/antlr/antlr4) - a well-known multilanguage LL(*) parser generator. The libs allows you to

* transpile an ANTLR grammar into the corresponding source code.
* visualize AST resulted from parsing source code with a grammar for debug purposes.

Currently it lacks docs, but be brave to read the source code, it is only a wrapper that is not that big.

Our fork is currently needed
------------------------
You need [our fork of ANTL](https://codeberg.org/UniGrammar/antlr4/tree/tool_refactoring) in order for this to work. We [have tried to upstream our changes](https://github.com/antlr/antlr4/pull/2774), but the project's BDFL is against "big changes". Then I had refactored the changes into several smaller PRs that don't introduce the changes but make it easier for me to introduce them with subclassing. They are still not merged:

* [Refactor `CodeGenerator`: move target initialization code into a separate method](https://github.com/antlr/antlr4/pull/3925)
* [Extracted getting the interp file name into a separate method `Tool::getInterpFileName`](https://github.com/antlr/antlr4/pull/3924)
* [Move a part of `CodeGenerator.write` into a separate reusable method `writeToWriter` to be able to reuse it later](https://github.com/antlr/antlr4/pull/3923)
* [Convert some members of CodeGenerator from private into protected](https://github.com/antlr/antlr4/pull/3922)
24 changes: 24 additions & 0 deletions UNLICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
This is free and unencumbered software released into the public domain.

Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.

In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

For more information, please refer to <https://unlicense.org/>
19 changes: 19 additions & 0 deletions antlrCompile/ANTLRInternalClasses.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from abc import ABC, abstractmethod


class ANTLRInternalClasses(ABC):
__slots__ = ()

ext = None

@abstractmethod
def CharStreams(self, src):
raise NotImplementedError

@abstractmethod
def CommonTokenStream(self, lexers):
raise NotImplementedError

@abstractmethod
def _toClasses(self, compResult):
raise NotImplementedError
156 changes: 156 additions & 0 deletions antlrCompile/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
import typing
from abc import ABC, abstractmethod
from pathlib import Path
from collections import OrderedDict, defaultdict
import threading

from .antlr import neededAntlrClasses, getAntlrPath, ANTLRLanguage
from .core import CompilationResult

from UniGrammarRuntimeCore.ICompiler import ICompiler


class ANTLR(ICompiler):
__slots__ = ("ji", "t")

def __init__(self) -> None:
from JAbs import SelectedJVMInitializer

antlrClassPath = getAntlrPath()
self.ji = SelectedJVMInitializer(
[
antlrClassPath
],
neededAntlrClasses
)

self.t = self.ji.Tool()
self.t.outputDirectory = None

def parse(self, grammar: typing.Union[Path, str]):
if isinstance(grammar, Path):
gF = grammar.absolute()
gT = gF.read_text()
elif isinstance(grammar, str):
gT = grammar

return self.t.parseGrammarFromString(gT)

def compileStr(self, grammarText: str, target: ANTLRLanguage = None, fileName: typing.Union[Path, str] = "grammar.g4") -> CompilationResult:
"""Parses the DSL string and generates internal object from it. `fileName` must not be used for retrieving source, instead it must provide hints, useful for caching or more meaningful error messages and may be just ignored if the backend doesn't support its usage this way."""

target = ANTLRLanguage(target)
rAST = self.parse(grammarText)

if fileName is None:
fileName = "grammar.g4"
else:
fileName = str(fileName)

rAST.defaultOptions["language"] = target.value

g = self.t.createGrammar(rAST)
#print(g.ast.toStringTree())

g.fileName = fileName

res = self.t.processInMemory(g, True)

#r = g.getRule(0)
#mainRuleName = r.name

return CompilationResult(res, target, g.name)

def compile(self, grammar: typing.Union[Path, str], target: ANTLRLanguage = ANTLRLanguage.Python3, fileName: typing.Optional[typing.Union[Path, str]] = None) -> CompilationResult:
if isinstance(grammar, Path):
assert fileName is None
return self.compileFile(grammar, target)
return self.compileStr(grammar, target, fileName)


class Vis:
__slots__ = ("ji", "MyListener")
def __init__(self) -> None:
from JAbs import SelectedJVMInitializer

antlrClassPath = getAntlrPath()
self.ji = SelectedJVMInitializer(
[
antlrClassPath
],
[
"org.antlr.v4.gui.Trees",
"org.antlr.v4.gui.TreePostScriptGenerator",
"java.util.ArrayList",
"java.util.Arrays",
"java.awt.event.WindowListener"
]
)

class MyListener(self.ji.WindowListener, metaclass=self.ji._Implements):
# fuck, no __slots__ here
def __init__(self, e: threading.Event):
self.e = e

@self.ji._Override
def windowClosed(self, e: "java.awt.event.WindowEvent"):
pass
self.e.set()

@self.ji._Override
def windowClosing(self, e: "java.awt.event.WindowEvent"):
pass
@self.ji._Override
def windowOpened(self, e: "java.awt.event.WindowEvent"):
pass
@self.ji._Override
def windowIconified(self, e: "java.awt.event.WindowEvent"):
pass
@self.ji._Override
def windowDeiconified(self, e: "java.awt.event.WindowEvent"):
pass
@self.ji._Override
def windowActivated(self, e: "java.awt.event.WindowEvent"):
pass
@self.ji._Override
def windowDeactivated(self, e: "java.awt.event.WindowEvent"):
pass

self.MyListener = MyListener

def blockOnGUIWindow(self, wind):
"""Blocks untill window is closed"""
e = threading.Event()
listn = self.MyListener(e)
wind.addWindowListener(listn)
e.wait()

def treeGUIVisualization(self, parser: "ANTLRParser", source: str, block: bool=True) -> None:
"""Shows a GUI dialog with parse tree"""
fullTree = parser(source)
task = self.ji.Trees.inspect(fullTree, self.ji.Arrays.asList(parser.ruleNames))
wind = task.get()

if block:
self.blockOnGUIWindow(wind)

return wind

def treePostScriptVisualization(self, parser: "ANTLRParser", source: str, fontName: str = "Helvetica", fontSize: int = 11, gapBetweenLevels: typing.Optional[float] = None, gapBetweenNodes: typing.Optional[float] = None, nodeWidthPadding: typing.Optional[int] = None, nodeHeightPaddingAbove: typing.Optional[int] = None, nodeHeightPaddingBelow: typing.Optional[int] = None) -> str:
"""Returns a PostScript string for parse tree image"""
fullTree = parser(source)

g = self.ji.TreePostScriptGenerator(self.ji.Arrays.asList(parser.ruleNames), fullTree, fontName, fontSize)

if gapBetweenLevels is not None:
g.gapBetweenLevels = gapBetweenLevels
if gapBetweenNodes is not None:
g.gapBetweenNodes = gapBetweenNodes
if nodeWidthPadding is not None:
g.nodeWidthPadding = nodeWidthPadding
if nodeHeightPaddingAbove is not None:
g.nodeHeightPaddingAbove = nodeHeightPaddingAbove
if nodeHeightPaddingBelow is not None:
g.nodeHeightPaddingBelow = nodeHeightPaddingBelow

return g.getPS()
42 changes: 42 additions & 0 deletions antlrCompile/antlr.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
from enum import Enum
from pathlib import Path

antlrNs = "org.antlr"
antlrRuntimeNs = antlrNs + ".runtime"
antlrVNs = antlrNs + ".v4"
antlrVRuntimeNs = antlrVNs + ".runtime"

neededAntlrClasses = [
antlrVNs + ".tool.Grammar",
antlrVNs + ".parse.ANTLRParser",
antlrVNs + ".Tool",
antlrVNs + ".analysis.AnalysisPipeline",

antlrRuntimeNs + ".ANTLRFileStream",
antlrRuntimeNs + ".ANTLRStringStream",
]

neededAntlrRuntimeClasses = [
antlrVRuntimeNs + ".CharStream",
antlrVRuntimeNs + ".CharStreams",
antlrVRuntimeNs + ".TokenStream",
antlrVRuntimeNs + ".CommonTokenStream",
]

# todo: get targets via reflection from org.antlr.v4.codegen.target (class names are <target_name>Target)


def getAntlrPath() -> Path:
return Path("./antlr4-4.11.2-SNAPSHOT-complete.jar")


class ANTLRLanguage(Enum):
Python3 = "Python3"
Python2 = "Python2"
Java = "Java"
Cpp = "Cpp"
CSharp = "CSharp"
Go = "Go"
ECMAScript = JavaScript = "JavaScript"
PHP = "PHP"
Swift = "Swift"
8 changes: 8 additions & 0 deletions antlrCompile/backends/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from .java import ANTLRInternalClassesJava
from .python import ANTLRInternalClassesPython
from ..antlr import ANTLRLanguage

langs2InternalMapping = {
ANTLRLanguage.Java: ANTLRInternalClassesJava,
ANTLRLanguage.Python3: ANTLRInternalClassesPython,
}
Loading

0 comments on commit 7315678

Please sign in to comment.