-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
44 changed files
with
1,244 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
# This workflow will install Python dependencies, run tests and lint with a variety of Python versions | ||
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python | ||
|
||
name: Python package | ||
|
||
on: | ||
push: | ||
branches: [ "main" ] | ||
pull_request: | ||
|
||
jobs: | ||
build: | ||
|
||
runs-on: ubuntu-latest | ||
strategy: | ||
fail-fast: false | ||
matrix: | ||
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] | ||
|
||
steps: | ||
- uses: actions/checkout@v3 | ||
- name: Set up Python ${{ matrix.python-version }} | ||
uses: actions/setup-python@v3 | ||
with: | ||
python-version: ${{ matrix.python-version }} | ||
- name: Install dependencies | ||
run: | | ||
python -m pip install --upgrade pip | ||
pip install -e .[tests] | ||
- name: Lint with flake8 | ||
run: | | ||
# stop the build if there are Python syntax errors or undefined names | ||
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics | ||
- name: Test with pytest | ||
env: | ||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
run: | | ||
pytest --cov=src | ||
coveralls --service=github | ||
flake8: | ||
runs-on: ubuntu-latest | ||
strategy: | ||
fail-fast: false | ||
matrix: | ||
python-version: ["3.8"] | ||
steps: | ||
- uses: actions/checkout@v3 | ||
- name: Set up Python ${{ matrix.python-version }} | ||
uses: actions/setup-python@v3 | ||
with: | ||
python-version: ${{ matrix.python-version }} | ||
- name: Install dependencies | ||
run: | | ||
python -m pip install --upgrade pip | ||
pip install -e .[tests] | ||
- name: Lint with flake8 | ||
run: | | ||
# stop the build if there are Python syntax errors or undefined names | ||
flake8 . --count --show-source --statistics --exclude src/vba_precompiler/grammar | ||
- name: Static Test with Mypy | ||
run: | | ||
mypy --exclude /grammar/ src/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,66 @@ | ||
[![Coverage Status](https://coveralls.io/repos/github/Beakerboy/VBA-Precompiler/badge.png?branch=main)](https://coveralls.io/github/Beakerboy/VBA-Precompiler?branch=main) ![Build Status](https://github.com/Beakerboy/VBA-Precompiler/actions/workflows/python-package.yml/badge.svg) | ||
# VBA-Precompiler | ||
Precompile VBA source files with specified environment values. | ||
|
||
## About | ||
The Microsoft VBA language includes a simple precompilation language (Conditional Compilation). This tool allows users to specify environment parameters to convert a conditional-module-body into a preprocessed-module-body. | ||
|
||
This software operates as recommended in the [Microsoft VBA Language Specification](https://winprotocoldoc.blob.core.windows.net/productionwindowsarchives/MS-CFB/%5bMS-CFB%5d.pdf). | ||
|
||
## Requirements | ||
vba_precompiler is tested on python 3.8 and higher. | ||
|
||
## Installation | ||
Use the package manager [pip](https://pip.pypa.io/en/stable/) to install VBA-Precompiler. | ||
``` | ||
pip install 'vba_precompiler @ git+https://github.com/Beakerboy/VBA-Precompiler@dev' | ||
``` | ||
|
||
## Getting Started | ||
vba_precompiler will take a specified directory that contains vba source code, and a set of environment values, and produce a set of matching vba source files in which code is excluded as directed by the precomiler directives. | ||
|
||
For example, the following | ||
``` | ||
Attribute VB_Name = "Input" | ||
#Const TestType="testing" | ||
#If Win16 Or Then | ||
foo = 6 | ||
#ElseIf Win32 | ||
foo = 7 | ||
#EndIf | ||
'Additional VBA code follows | ||
``` | ||
|
||
Will be transformed to the following: | ||
``` | ||
Attribute VB_Name = "Input" | ||
'#Const TestType="testing" | ||
'#If Win16 Then | ||
foo = 6 | ||
'#ElseIf Win32 | ||
' foo = 7 | ||
'#EndIf | ||
'Additional VBA code follows | ||
``` | ||
To run the program | ||
``` | ||
python vba_precompiler.py [-h] [-s SYSTEM] [-v VERSION] [-o OUTPUT] directory | ||
positional arguments: | ||
directory The source directory. | ||
options: | ||
-h, --help show this help message and | ||
exit | ||
-s, --system System Type, Win16, Win32, Win64, or Mac. | ||
-v, --version VBA version, 6 or 7. | ||
-o, --output output path, defaults to ./build. | ||
examples: | ||
python -m vba_precompiler -s Win32 -v 7 -o ./build32_7 ./project | ||
``` | ||
|
||
## Tests | ||
The tests directory contains complete unit and functional tests. | ||
|
||
## Contributing | ||
Contributions are welcome. Please ensure new features include unit tests to maintain 100% coverage. All code must adhere to the [PEP8 Standards](https://peps.python.org/pep-0008/) for both formatting and naming. Method signatures must be fully annotated. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
# blank |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
import argparse | ||
import sys | ||
from pathlib import Path | ||
from vba_precompiler.compiler import Compiler | ||
|
||
|
||
def main() -> None: | ||
parser = argparse.ArgumentParser() | ||
parser.add_argument("-s", "--system", default="Win16", | ||
help="Mac, Win16, Win32, or Win64") | ||
parser.add_argument("-v", "--version", default="6", | ||
help="VBA version, 6 or 7") | ||
parser.add_argument("-o", "--output", default="./build", | ||
help="Output directory") | ||
parser.add_argument("directory", default='.', | ||
help="The source directory.") | ||
args = parser.parse_args() | ||
path = Path(args.directory).resolve() | ||
file_list = find_files(path) | ||
win16 = False | ||
win32 = False | ||
win64 = False | ||
mac = False | ||
vba6 = False | ||
vba7 = False | ||
if args.system == "Mac": | ||
mac = True | ||
elif args.system == "Win16": | ||
win16 = True | ||
elif args.system == "Win32": | ||
win32 = True | ||
elif args.system == "Win64": | ||
win32 = True | ||
win64 = True | ||
else: | ||
raise Exception("OS Type Unsupported: " + args.system) | ||
if args.version == "6": | ||
if win32 or mac: | ||
vba6 = True | ||
elif args.version == "7": | ||
vba7 = True | ||
else: | ||
raise Exception("VBA Version Unsupported: " + args.version) | ||
|
||
env = {"WIN16": win16, "WIN32": win32, "WIN64": win64, | ||
"MAC": mac, "VBA6": vba6, "VBA7": vba7, "MAC_OFFICE_VERSION": 0} | ||
compiler = Compiler(env) | ||
Path(args.output).mkdir(parents=True, exist_ok=True) | ||
num_errors = 0 | ||
for file_name in file_list: | ||
new_file_rel_path = Path(file_name).relative_to(path) | ||
output_path = Path(args.output).resolve() | ||
new_path = output_path.joinpath(new_file_rel_path) | ||
try: | ||
result = compiler.compile(file_name) | ||
except Exception as e: | ||
print("File Failed: " + str(file_name), file=sys.stderr) | ||
print(str(e), file=sys.stderr) | ||
num_errors += 1 | ||
else: | ||
new_path.parent.mkdir(parents=True, exist_ok=True) | ||
with new_path.open(mode='a') as fi: | ||
fi.write(result) | ||
if num_errors > 0: | ||
exit_code = 1 | ||
sys.exit(exit_code) | ||
|
||
|
||
def find_files(path: Path) -> list: | ||
""" | ||
Find all gives of given types within a directory | ||
""" | ||
files = [] | ||
for child in path.rglob("*"): | ||
if child.suffix in [".bas", ".cls", ".frm"]: | ||
files.append(child) | ||
return files | ||
|
||
|
||
if __name__ == '__main__': | ||
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
from antlr4 import CommonTokenStream, FileStream | ||
from pathlib import Path | ||
from typing import TypeVar | ||
from antlr4_vba.vba_ccLexer import vba_ccLexer as Lexer | ||
from antlr4_vba.vba_ccParser import vba_ccParser as Parser | ||
from vba_precompiler.precompiler_visitor import PrecompilerVisitor as Visitor | ||
|
||
|
||
T = TypeVar('T', bound='Compiler') | ||
|
||
|
||
class Compiler: | ||
# class default constructor | ||
def __init__(self: T, environment: dict) -> None: | ||
self.environment = environment | ||
|
||
def compile(self: T, path: str) -> str: | ||
if Path(path).exists(): | ||
input_stream = FileStream(path) | ||
lexer = Lexer(input_stream) | ||
else: | ||
raise Exception('file does not exist: ' + path) | ||
ts = CommonTokenStream(lexer) | ||
parser = Parser(ts) | ||
program = parser.startRule() | ||
visitor = Visitor() | ||
visitor.env = self.environment.copy() | ||
lines = visitor.visit(program) | ||
i = 1 | ||
f = open(path, 'r') | ||
code = "" | ||
while True: | ||
line = f.readline() | ||
if not line: | ||
break | ||
if i in lines: | ||
code += "'" | ||
code += line | ||
i += 1 | ||
return code |
Oops, something went wrong.