diff --git a/docs/40_gerber/00_introduction.md b/docs/40_gerber/00_introduction.md index f68329fe..f16dcd04 100644 --- a/docs/40_gerber/00_introduction.md +++ b/docs/40_gerber/00_introduction.md @@ -1,4 +1,4 @@ -# Introduction +# 🧭 Introduction ## What you will find here diff --git a/docs/40_gerber/10_migrate_from_2_4_x_to_3_x_x.md b/docs/40_gerber/10_migrate_from_2_4_x_to_3_x_x.md index 67a07417..0390fd53 100644 --- a/docs/40_gerber/10_migrate_from_2_4_x_to_3_x_x.md +++ b/docs/40_gerber/10_migrate_from_2_4_x_to_3_x_x.md @@ -1,4 +1,4 @@ -# Migrating from 2.4.x to 3.x.x +# 🤧 Migrating from 2.4.x to 3.x.x PyGerber 3.0.0 is a major rewrite of the library. Most of the library was rewritten from scratch, a lot of nomenclature has changes and layout of the library has been diff --git a/docs/40_gerber/20_quick_start/00_introduction.md b/docs/40_gerber/20_quick_start/00_introduction.md index adf71eeb..0a4126c3 100644 --- a/docs/40_gerber/20_quick_start/00_introduction.md +++ b/docs/40_gerber/20_quick_start/00_introduction.md @@ -1,4 +1,4 @@ -# Introduction +# 🧭 Introduction ## Overview diff --git a/docs/40_gerber/20_quick_start/01_single_file.md b/docs/40_gerber/20_quick_start/01_single_file.md index 971e2007..19213276 100644 --- a/docs/40_gerber/20_quick_start/01_single_file.md +++ b/docs/40_gerber/20_quick_start/01_single_file.md @@ -1,4 +1,4 @@ -# Single file guide +# 🥐 Single file guide Welcome to the PyGerber single file guide. This guide focuses on tools which take single Gerber file as opposed to [Multi file guide](./02_multi_file.md) guide which is diff --git a/docs/40_gerber/20_quick_start/02_multi_file.md b/docs/40_gerber/20_quick_start/02_multi_file.md index c94642bf..2645b0cf 100644 --- a/docs/40_gerber/20_quick_start/02_multi_file.md +++ b/docs/40_gerber/20_quick_start/02_multi_file.md @@ -1,4 +1,4 @@ -# Multi file guide +# 🥞 Multi file guide Welcome to the multi file guide. This guide shows how to use `CompositeView` class API to handle multiple Gerber files at once. `CompositeView` relies on `GerberFile` diff --git a/docs/40_gerber/20_quick_start/03_project_gerber_job.md b/docs/40_gerber/20_quick_start/03_project_gerber_job.md index 9921f74d..4b67b7d0 100644 --- a/docs/40_gerber/20_quick_start/03_project_gerber_job.md +++ b/docs/40_gerber/20_quick_start/03_project_gerber_job.md @@ -1,4 +1,4 @@ -# GerberJobFile and Project +# 🥧 GerberJobFile and Project This guide shows how to use `GerberJobFile` class and `Project` to represent PCB projects. diff --git a/docs/40_gerber/20_quick_start/10_custom_color_maps.md b/docs/40_gerber/20_quick_start/10_custom_color_maps.md index b8ab5121..018d33be 100644 --- a/docs/40_gerber/20_quick_start/10_custom_color_maps.md +++ b/docs/40_gerber/20_quick_start/10_custom_color_maps.md @@ -1,4 +1,4 @@ -# Custom color maps +# 🧁 Custom color maps ## Introduction diff --git a/docs/40_gerber/30_analysis/00_introduction.md b/docs/40_gerber/30_analysis/00_introduction.md index cd3f911f..c99dff45 100644 --- a/docs/40_gerber/30_analysis/00_introduction.md +++ b/docs/40_gerber/30_analysis/00_introduction.md @@ -1,4 +1,4 @@ -# Introduction +# 🧭 Introduction Welcome to the Gerber code analysis documentation. This documentation is intended to provide a friendly guide to the APIs within PyGerber which can be used to analyze Gerber diff --git a/docs/40_gerber/30_analysis/10_stateless_introspection.md b/docs/40_gerber/30_analysis/10_stateless_introspection.md index 053adaa4..5ada84ea 100644 --- a/docs/40_gerber/30_analysis/10_stateless_introspection.md +++ b/docs/40_gerber/30_analysis/10_stateless_introspection.md @@ -1,4 +1,4 @@ -# Stateless Introspection +# 🧪 Stateless Introspection Unfortunately, this documentation is yet to be written. I am doing my best to provide it before release 3.0.0 so stay tuned! diff --git a/docs/40_gerber/30_analysis/20_stateful_introspection.md b/docs/40_gerber/30_analysis/20_stateful_introspection.md index 68be146b..e05fcc24 100644 --- a/docs/40_gerber/30_analysis/20_stateful_introspection.md +++ b/docs/40_gerber/30_analysis/20_stateful_introspection.md @@ -1,4 +1,4 @@ -# Stateful Introspection +# 🧬 Stateful Introspection Unfortunately, this documentation is yet to be written. I am doing my best to provide it before release 3.0.0 so stay tuned! diff --git a/docs/40_gerber/40_modify_optimize/00_introduction.md b/docs/40_gerber/40_modify_optimize/00_introduction.md index 986b3f30..c51c72a7 100644 --- a/docs/40_gerber/40_modify_optimize/00_introduction.md +++ b/docs/40_gerber/40_modify_optimize/00_introduction.md @@ -1,4 +1,4 @@ -# Introduction +# 🧭 Introduction Welcome to the Gerber Optimizer documentation. This documentation is intended to provide a comprehensive overview of the Gerber Optimizer including usage, options and examples. diff --git a/docs/40_gerber/40_modify_optimize/30_code_optimization.md b/docs/40_gerber/40_modify_optimize/30_code_optimization.md index bb244ff8..cfaaa976 100644 --- a/docs/40_gerber/40_modify_optimize/30_code_optimization.md +++ b/docs/40_gerber/40_modify_optimize/30_code_optimization.md @@ -1,4 +1,4 @@ -# Code optimization +# 🧴 Code optimization Unfortunately, this documentation is yet to be written. I am doing my best to provide it before release 3.0.0 so stay tuned! diff --git a/docs/40_gerber/60_formatter/00_introduction.md b/docs/40_gerber/60_formatter/00_introduction.md index df8f2f57..0da5fdbe 100644 --- a/docs/40_gerber/60_formatter/00_introduction.md +++ b/docs/40_gerber/60_formatter/00_introduction.md @@ -1,4 +1,4 @@ -# Introduction +# 🧭 Introduction Welcome to the Gerber Formatter documentation. This documentation is intended to provide a comprehensive overview of the Gerber Formatter including usage, options and examples. diff --git a/docs/40_gerber/60_formatter/05_api_usage.md b/docs/40_gerber/60_formatter/05_api_usage.md index 91662692..4dde2644 100644 --- a/docs/40_gerber/60_formatter/05_api_usage.md +++ b/docs/40_gerber/60_formatter/05_api_usage.md @@ -1,4 +1,4 @@ -# API usage +# ☕ API usage The Gerber Formatter can be used as a library. The main entry point is the `pygerber.gerber.formatter` module. diff --git a/docs/40_gerber/60_formatter/20_extending_formatter.md b/docs/40_gerber/60_formatter/20_extending_formatter.md index 67640e95..957168dd 100644 --- a/docs/40_gerber/60_formatter/20_extending_formatter.md +++ b/docs/40_gerber/60_formatter/20_extending_formatter.md @@ -1 +1 @@ -# Extending Formatter +# 🥠 Extending Formatter diff --git a/docs/40_gerber/70_diagnostics/00_introduction.md b/docs/40_gerber/70_diagnostics/00_introduction.md index 0d0ea3f6..b6082886 100644 --- a/docs/40_gerber/70_diagnostics/00_introduction.md +++ b/docs/40_gerber/70_diagnostics/00_introduction.md @@ -1,4 +1,26 @@ -# Introduction +# 🧭 Introduction -Unfortunately, this documentation is yet to be written. I am doing my best to provide it -before release 3.0.0 so stay tuned! +## Overview + +PyGerber provides a linter (diagnostic tool) for analyzing Gerber code. The linter is a +tool that focuses on checking the code itself, not the design of the PCB. Therefore it +includes checks for deprecated features, common deviations from Gerber standard and +other Gerber focused checks, but does not contain any check that verify the quality of +the PCB design, like connectivity, trace width, etc. + +The Gerber linter is a tool that has limited usefulness for the average user, but can be +very handy for verifying the quality of generated Gerber code, especially while working +on solutions that modify or generate Gerber code, to ensure compliance with latest +Gerber standard. + +## API + +Gerber linter API is available in the `pygerber.gerber.linter` module. Simplified +interface consists of a single function `lint` that takes a Gerber AST as input and +returns a list of rule violations detected in AST. + +Here is a simple example of how to use the linter: + +{{ include_code("test/examples/gerberx3/linter/_00_linter.example.py", "docspygerberlexer", title="custom_color_map.py", linenums="1") }} + +{{ run_capture_stdout("python test/examples/gerberx3/linter/_00_linter.example.py", "python example.py") }} diff --git a/docs/40_gerber/70_diagnostics/01_rule_list.md b/docs/40_gerber/70_diagnostics/01_rule_list.md new file mode 100644 index 00000000..c44e5457 --- /dev/null +++ b/docs/40_gerber/70_diagnostics/01_rule_list.md @@ -0,0 +1,6 @@ +# 📄 Rule list + +Below you can find full list of all rules implemented in PyGerber for Gerber format +diagnostics. + +{{ gerber_linter_rule_list() }} diff --git a/docs/40_gerber/80_language_server/00_introduction.md b/docs/40_gerber/80_language_server/00_introduction.md index 0d0ea3f6..cc3eae45 100644 --- a/docs/40_gerber/80_language_server/00_introduction.md +++ b/docs/40_gerber/80_language_server/00_introduction.md @@ -1,4 +1,4 @@ -# Introduction +# 🧭 Introduction Unfortunately, this documentation is yet to be written. I am doing my best to provide it before release 3.0.0 so stay tuned! diff --git a/docs/40_gerber/80_language_server/10_vsc_integration.md b/docs/40_gerber/80_language_server/10_vsc_integration.md index ca5f92a8..f5c2ab4a 100644 --- a/docs/40_gerber/80_language_server/10_vsc_integration.md +++ b/docs/40_gerber/80_language_server/10_vsc_integration.md @@ -1,4 +1,4 @@ -# Visual Studio Code Integration +# 🥨 Visual Studio Code Integration Unfortunately, this documentation is yet to be written. I am doing my best to provide it before release 3.0.0 so stay tuned! diff --git a/docs/40_gerber/80_language_server/50_features.md b/docs/40_gerber/80_language_server/50_features.md index 0fb4dea7..82c63db0 100644 --- a/docs/40_gerber/80_language_server/50_features.md +++ b/docs/40_gerber/80_language_server/50_features.md @@ -1,4 +1,4 @@ -# Features +# 🧰 Features Unfortunately, this documentation is yet to be written. I am doing my best to provide it before release 3.0.0 so stay tuned! diff --git a/docs/40_gerber/90_extend/00_introduction.md b/docs/40_gerber/90_extend/00_introduction.md index 0d0ea3f6..cc3eae45 100644 --- a/docs/40_gerber/90_extend/00_introduction.md +++ b/docs/40_gerber/90_extend/00_introduction.md @@ -1,4 +1,4 @@ -# Introduction +# 🧭 Introduction Unfortunately, this documentation is yet to be written. I am doing my best to provide it before release 3.0.0 so stay tuned! diff --git a/docs/40_gerber/90_extend/40_extending_ast_nodes.md b/docs/40_gerber/90_extend/40_extending_ast_nodes.md index ec2eee22..82ea7723 100644 --- a/docs/40_gerber/90_extend/40_extending_ast_nodes.md +++ b/docs/40_gerber/90_extend/40_extending_ast_nodes.md @@ -1,4 +1,4 @@ -# Extending AST Nodes +# 🥥 Extending AST Nodes Unfortunately, this documentation is yet to be written. I am doing my best to provide it before release 3.0.0 so stay tuned! diff --git a/docs/40_gerber/90_extend/50_extending_parser.md b/docs/40_gerber/90_extend/50_extending_parser.md index 45c88778..5d4f0e89 100644 --- a/docs/40_gerber/90_extend/50_extending_parser.md +++ b/docs/40_gerber/90_extend/50_extending_parser.md @@ -1 +1,4 @@ -# Extending Parser +# 🧽 Extending Parser + +Unfortunately, this documentation is yet to be written. I am doing my best to provide it +before release 3.0.0 so stay tuned! diff --git a/docs/40_gerber/90_extend/60_extending_compiler.md b/docs/40_gerber/90_extend/60_extending_compiler.md index bb858def..36db2ac0 100644 --- a/docs/40_gerber/90_extend/60_extending_compiler.md +++ b/docs/40_gerber/90_extend/60_extending_compiler.md @@ -1 +1,4 @@ -# Extending Compiler +# 🧶 Extending Compiler + +Unfortunately, this documentation is yet to be written. I am doing my best to provide it +before release 3.0.0 so stay tuned! diff --git a/docs/40_gerber/90_extend/70_extending_virtual_machines.md b/docs/40_gerber/90_extend/70_extending_virtual_machines.md index f1ece3b0..0ed515bd 100644 --- a/docs/40_gerber/90_extend/70_extending_virtual_machines.md +++ b/docs/40_gerber/90_extend/70_extending_virtual_machines.md @@ -1 +1,4 @@ -# Extending Virtual Machines +# 🧩 Extending Virtual Machines + +Unfortunately, this documentation is yet to be written. I am doing my best to provide it +before release 3.0.0 so stay tuned! diff --git a/docs/50_command_line/00_introduction.md b/docs/50_command_line/00_introduction.md index a859ab5f..be07b1f6 100644 --- a/docs/50_command_line/00_introduction.md +++ b/docs/50_command_line/00_introduction.md @@ -1,4 +1,4 @@ -# Introduction +# 🧭 Introduction Welcome to the PyGerber command line interface documentation. This documentation is intended to guide you through the usage of the command line interface of PyGerber. diff --git a/docs/50_command_line/20_gerber.md b/docs/50_command_line/20_gerber.md index 72edc48a..b4ba254f 100644 --- a/docs/50_command_line/20_gerber.md +++ b/docs/50_command_line/20_gerber.md @@ -1,4 +1,4 @@ -# Gerber +# 🥞 Gerber Welcome to the Gerber focused part of PyGerber command line interface documentation. This documentation is intended to show you how to use Gerber related command line tools diff --git a/docs/60_rendering_backends/00_introduction.md b/docs/60_rendering_backends/00_introduction.md index e10b99d0..39b9cd6c 100644 --- a/docs/60_rendering_backends/00_introduction.md +++ b/docs/60_rendering_backends/00_introduction.md @@ -1 +1 @@ -# Introduction +# 🧭 Introduction diff --git a/docs/80_code_generation/00_introduction.md b/docs/80_code_generation/00_introduction.md index e10b99d0..42b048c0 100644 --- a/docs/80_code_generation/00_introduction.md +++ b/docs/80_code_generation/00_introduction.md @@ -1 +1,15 @@ -# Introduction +# 🧭 Introduction + +## Overview + +Welcome to the documentation of the code generation feature of PyGerber library! +Considering that to develop PyGerber core functionality of parsing Gerber files it was +necessary to design a lot of tools to verify the correctness of the implementation, it +became apparent that is will be much easier to compliment existing tool set with a code +generation tools rather than develop separate package for that purpose. Therefore, the +decision was made to develop a code generation feature as a part of PyGerber library. + +Currently, there are two code generation APIs, one for generation of Gerber code and the +second one for generation of RVMC code (internal geometry representation used by +PyGerber). Those APIs are available in the `pygerber.builder.gerber` and +`pygerber.builder.rvmc` respectively. diff --git a/docs/90_development/00_introduction.md b/docs/90_development/00_introduction.md index e10b99d0..166d6673 100644 --- a/docs/90_development/00_introduction.md +++ b/docs/90_development/00_introduction.md @@ -1 +1,7 @@ -# Introduction +# 🧭 Introduction + +## Overview + +Welcome to collection of development related guides for PyGerber project. Those guides +will be helpful if you are considering contributing to the project or if you want to +review the code in more detail. diff --git a/docs/macros/main.py b/docs/macros/main.py index c63e0f5c..31c7c106 100644 --- a/docs/macros/main.py +++ b/docs/macros/main.py @@ -91,6 +91,21 @@ def run_render_gerber_from_stdout(command: str, dpmm: int) -> str: return _base64_image_tag(out.get_image()) + @env.macro + def gerber_linter_rule_list() -> str: + """Generate markdown of list of rules used by the Gerber linter.""" + from pygerber.gerber.linter import RULE_REGISTRY + + def _(): + for rule_type in RULE_REGISTRY.values(): + rule = rule_type() + yield ( + f"---\n## {rule.rule_id}\n\n{rule.get_violation_title()}\n\n" + f"{rule.get_violation_description()}\n" + ) + + return "\n".join(_()) + def _base64_image_tag(image: Image.Image) -> str: buffered = io.BytesIO() diff --git a/src/pygerber/console/gerber.py b/src/pygerber/console/gerber.py index fea33273..da1e2ef9 100644 --- a/src/pygerber/console/gerber.py +++ b/src/pygerber/console/gerber.py @@ -531,6 +531,9 @@ def _project(files: str, output: str, dpmm: int) -> None: ) def lint(files: str, rules: list[str]) -> None: """Lint Gerber files with specified rules.""" + from pygerber.gerber.linter import lint + from pygerber.gerber.parser import parse + if len(files) == 0: msg = "At least one file must be specified." raise click.UsageError(msg) @@ -539,21 +542,13 @@ def _parse_rules(rules: list[str]) -> Iterable[str]: for rule in rules: yield from rule.split(",") - from pygerber.gerber.linter import RULE_REGISTRY, Linter - from pygerber.gerber.parser import parse - - if len(rules) != 0: - rule_objects = [RULE_REGISTRY[rule_id]() for rule_id in _parse_rules(rules)] - else: - rule_objects = [r() for r in RULE_REGISTRY.values()] - - linter = Linter(rule_objects) + rules = list(_parse_rules(rules)) for file in files: path = Path(file).expanduser().resolve() ast = parse(path.read_text()) - violations = linter.lint(ast) + violations = lint(ast, rules=rules) for violation in violations: click.echo( diff --git a/src/pygerber/examples/__init__.py b/src/pygerber/examples/__init__.py index 16b13491..647ca7d7 100644 --- a/src/pygerber/examples/__init__.py +++ b/src/pygerber/examples/__init__.py @@ -29,6 +29,8 @@ class ExamplesEnum(Enum): carte_test_gbrjob = "carte_test-job.gbrjob" + fc_poly_b_cu = "fc_poly_b_cu.grb" + DIRECTORY = Path(__file__).parent diff --git a/src/pygerber/examples/fc_poly_b_cu.grb b/src/pygerber/examples/fc_poly_b_cu.grb new file mode 100644 index 00000000..f1cc4941 --- /dev/null +++ b/src/pygerber/examples/fc_poly_b_cu.grb @@ -0,0 +1,686 @@ +G04 FreePCB version 1.355* +G04 C:\FcPoly-Ttest\top_copper.grb* +G04 top copper layer * +G04 Scale: 100 percent, Rotated: No, Reflected: No * +%FSLAX24Y24*% +%MOIN*% +%LN top copper *% +G04 Rounded Rectangle Macro, params: W/2, H/2, R * +%AMRNDREC* +21,1,$1+$1,$2+$2-$3-$3,0,0,0* +21,1,$1+$1-$3-$3,$2+$2,0,0,0* +1,1,$3+$3,$1-$3,$2-$3* +1,1,$3+$3,$3-$1,$2-$3* +1,1,$3+$3,$1-$3,$3-$2* +1,1,$3+$3,$3-$1,$3-$2*% +G04 Rectangular Thermal Macro, params: W/2, H/2, T/2 * +%AMRECTHERM* +$4=$3/2* +21,1,$1-$3,$2-$3,0-$1/2-$4,0-$2/2-$4,0* +21,1,$1-$3,$2-$3,0-$1/2-$4,$2/2+$4,0* +21,1,$1-$3,$2-$3,$1/2+$4,0-$2/2-$4,0* +21,1,$1-$3,$2-$3,$1/2+$4,$2/2+$4,0*% +%ADD10C,0.005000*% +%ADD11C,0.120000*% +%ADD12C,0.001000*% +%ADD13R,0.110000X0.110000*% +%ADD14R,0.130000X0.162000*% +%ADD15R,0.140000X0.084000*% +%ADD16R,0.120000X0.120000*% +%ADD17C,0.075000*% +%ADD18C,0.085000*% +%ADD19C,0.060000*% +%ADD20R,0.050000X0.050000*% +%ADD21R,0.070000X0.102000*% +%ADD22R,0.080000X0.024000*% +%ADD23R,0.060000X0.060000*% +%ADD24C,0.015000*% +%ADD25C,0.025000*% +%ADD26C,0.024000*% +%ADD27C,0.010000*% +G90* +G70D02* + +G04 Step and Repeat for panelization * + +G04 ----------------------- Draw board outline (positive)* +%LPD*% +G54D10* +G01X0Y11800D02* +G01X23250Y11800D01* +G04 end of side 1* +G01X23250Y0D01* +G04 end of side 2* +G01X0Y0D01* +G04 end of side 3* +G01X0Y11800D01* + +G04 ----------------------- Draw copper area (positive)* +G36* +G01X11140Y0D02* +G01X0Y0D01* +G01X0Y11800D01* +G01X11140Y11800D01* +G01X11140Y0D01* +G37* + +G04 ----------------------- Draw copper area (positive)* +G36* +G01X23250Y0D02* +G01X12000Y0D01* +G01X12000Y11800D01* +G01X23250Y11800D01* +G01X23250Y0D01* +G37* + +G04 -------------------- Draw copper area clearances (negative)* +%LPC*% + +G04 Draw clearances for pads* +G54D11* +G01X3960Y7900D03* +G01X3960Y6900D03* +G01X3960Y5900D03* +G54D12* +G54D13* +G01X6280Y9000D03* +G54D12* +G54D14* +G01X8000Y8240D03* +G54D12* +G54D15* +G01X9630Y5200D03* +G54D12* +G54D15* +G01X9630Y4200D03* +G01X9630Y3700D03* +G01X9630Y3200D03* +G01X9630Y2700D03* +G01X9630Y2200D03* +G54D12* +G54D15* +G01X13630Y2200D03* +G01X13630Y2700D03* +G01X13630Y3200D03* +G01X13630Y3700D03* +G01X13630Y4200D03* +G54D12* +G54D15* +G01X13630Y5200D03* +G54D12* +G54D13* +G01X8320Y3950D03* +G01X8320Y6380D03* +G54D12* +G54D13* +G01X14950Y6700D03* +G54D12* +G54D13* +G01X14950Y4200D03* +G54D16* +G01X5440Y5650D03* +G01X5440Y6900D03* +G01X6800Y2200D03* +G01X5550Y2200D03* +G01X17720Y6860D03* +G01X17720Y5610D03* +G01X17740Y2200D03* +G01X16490Y2200D03* +G54D11* +G01X19280Y7860D03* +G01X19280Y6860D03* +G01X19280Y5860D03* +G54D12* +G54D13* +G01X16960Y8980D03* +G54D12* +G54D14* +G01X15100Y8240D03* +G54D12* +G54D11* +G01X16340Y6700D03* +G01X16340Y5700D03* +G54D12* +G54D11* +G01X6920Y6700D03* +G01X6920Y5700D03* +G54D12* +G54D11* +G01X11130Y7000D03* +G54D12* +G54D11* +G01X13130Y7000D03* +G04 Draw clearances for traces* +G54D17* +G01X19280Y5860D02* +G01X19010Y5860D01* +G01X19010Y5860D02* +G01X18450Y5300D01* +G01X18450Y5300D02* +G01X18450Y2910D01* +G01X18450Y2910D02* +G01X17740Y2200D01* +G54D18* +G01X3960Y7900D02* +G01X7990Y7900D01* +G01X7990Y7900D02* +G01X8000Y8240D01* +G01X8320Y6380D02* +G01X9630Y6380D01* +G01X9630Y6380D02* +G01X9630Y5200D01* +G01X8320Y6380D02* +G01X10840Y6380D01* +G01X10840Y6380D02* +G01X11130Y6670D01* +G01X11130Y6670D02* +G01X11130Y7000D01* +G01X8000Y8240D02* +G01X8000Y7900D01* +G01X8000Y7900D02* +G01X6280Y7900D01* +G01X6280Y7900D02* +G01X6280Y9000D01* +G01X6920Y6700D02* +G01X6920Y7900D01* +G01X6920Y7900D02* +G01X8000Y7900D01* +G01X8000Y7900D02* +G01X8000Y8240D01* +G01X8000Y8240D02* +G01X8000Y7310D01* +G01X8000Y7310D02* +G01X8320Y6990D01* +G01X8320Y6990D02* +G01X8320Y6380D01* +G01X13130Y7000D02* +G01X13130Y6310D01* +G01X13130Y6310D02* +G01X13630Y5810D01* +G01X13630Y5810D02* +G01X13630Y5200D01* +G01X15100Y8240D02* +G01X15940Y8240D01* +G01X15940Y8240D02* +G01X16340Y7860D01* +G01X16340Y7860D02* +G01X16340Y6700D01* +G01X15100Y8240D02* +G01X15940Y8240D01* +G01X15940Y8240D02* +G01X16320Y7860D01* +G01X16320Y7860D02* +G01X16960Y7860D01* +G01X16960Y7860D02* +G01X16960Y8980D01* +G01X15100Y8240D02* +G01X14370Y8240D01* +G01X14370Y8240D02* +G01X13130Y7000D01* +G01X15100Y8240D02* +G01X15960Y8240D01* +G01X15960Y8240D02* +G01X16340Y7860D01* +G01X16340Y7860D02* +G01X19280Y7860D01* +G01X14950Y6700D02* +G01X16340Y6700D01* +G54D17* +G01X13630Y2200D02* +G01X16490Y2200D01* +G01X13630Y4200D02* +G01X13070Y4200D01* +G01X13070Y4200D02* +G01X12820Y3950D01* +G01X12820Y3950D02* +G01X12820Y3460D01* +G01X12820Y3460D02* +G01X13080Y3200D01* +G01X13080Y3200D02* +G01X13630Y3200D01* +G01X14950Y4200D02* +G01X13630Y4200D01* +G01X16340Y5700D02* +G01X16640Y5690D01* +G01X16640Y5690D02* +G01X17000Y5330D01* +G01X17000Y5330D02* +G01X17000Y4080D01* +G01X17000Y4080D02* +G01X16620Y3700D01* +G01X16620Y3700D02* +G01X13630Y3700D01* +G54D12* +G54D17* +G01X6920Y5700D02* +G01X6480Y5700D01* +G01X6480Y5700D02* +G01X6120Y5340D01* +G01X6120Y5340D02* +G01X6120Y3910D01* +G01X6120Y3910D02* +G01X6420Y3610D01* +G01X6420Y3610D02* +G01X7450Y3610D01* +G01X7450Y3610D02* +G01X7860Y3200D01* +G01X7860Y3200D02* +G01X9630Y3200D01* +G54D12* +G54D17* +G01X5440Y5650D02* +G01X5440Y3570D01* +G01X5440Y3570D02* +G01X5950Y3060D01* +G01X5950Y3060D02* +G01X7360Y3060D01* +G01X7360Y3060D02* +G01X7720Y2700D01* +G01X7720Y2700D02* +G01X9630Y2700D01* +G01X6800Y2200D02* +G01X9630Y2200D01* +G01X9630Y3700D02* +G01X9630Y4200D01* +G01X9630Y3700D02* +G01X8570Y3700D01* +G01X8570Y3700D02* +G01X8320Y3950D01* +G01X3960Y6900D02* +G01X5440Y6900D01* +G01X3960Y5900D02* +G01X4090Y5900D01* +G01X4090Y5900D02* +G01X4760Y5230D01* +G01X4760Y5230D02* +G01X4760Y2990D01* +G01X4760Y2990D02* +G01X5550Y2200D01* +G01X19280Y6860D02* +G01X17720Y6860D01* +G01X13630Y2700D02* +G01X14885Y2700D01* +G01X14885Y2700D02* +G01X15314Y3120D01* +G01X15314Y3120D02* +G01X16950Y3120D01* +G01X16950Y3120D02* +G01X17720Y3890D01* +G01X17720Y3890D02* +G01X17720Y5610D01* + +G04 Draw clearances for text* + +G04 -------------- Draw Parts, Pads, Traces, Vias and Text (positive)* +%LPD*% +G04 Draw part J1* +G54D19* +G01X3960Y7900D03* +G01X3960Y6900D03* +G01X3960Y5900D03* +G01X3960Y4900D03* +G01X3960Y3900D03* +G04 Draw part C1* +G54D20* +G01X6280Y9000D03* +G01X6280Y9750D03* +G04 Draw part C2* +G54D21* +G01X8000Y8240D03* +G01X8000Y9740D03* +G04 Draw part U1* +G54D22* +G01X9630Y5200D03* +G01X9630Y4700D03* +G01X9630Y4200D03* +G01X9630Y3700D03* +G01X9630Y3200D03* +G01X9630Y2700D03* +G01X9630Y2200D03* +G01X9630Y1700D03* +G01X13630Y1700D03* +G01X13630Y2200D03* +G01X13630Y2700D03* +G01X13630Y3200D03* +G01X13630Y3700D03* +G01X13630Y4200D03* +G01X13630Y4700D03* +G01X13630Y5200D03* +G04 Draw part C3* +G54D20* +G01X8320Y4700D03* +G01X8320Y3950D03* +G04 Draw part C4* +G01X8320Y6380D03* +G01X8320Y5630D03* +G04 Draw part C5* +G01X14950Y6700D03* +G01X14950Y5950D03* +G04 Draw part C6* +G01X14950Y4950D03* +G01X14950Y4200D03* +G04 Draw part R1* +G54D23* +G01X5440Y5650D03* +G01X5440Y6900D03* +G04 Draw part R2* +G01X6800Y2200D03* +G01X5550Y2200D03* +G04 Draw part R3* +G01X17720Y6860D03* +G01X17720Y5610D03* +G04 Draw part R4* +G01X17740Y2200D03* +G01X16490Y2200D03* +G04 Draw part J2* +G54D19* +G01X19280Y7860D03* +G01X19280Y6860D03* +G01X19280Y5860D03* +G01X19280Y4860D03* +G01X19280Y3860D03* +G04 Draw part C7* +G54D20* +G01X16960Y8980D03* +G01X16960Y9730D03* +G04 Draw part C8* +G54D21* +G01X15100Y8240D03* +G01X15100Y9740D03* +G04 Draw part J3* +G54D19* +G01X16340Y6700D03* +G01X16340Y5700D03* +G01X16340Y4700D03* +G04 Draw part J4* +G01X6920Y6700D03* +G01X6920Y5700D03* +G01X6920Y4700D03* +G04 Draw part U2* +G01X10130Y7000D03* +G01X11130Y7000D03* +G01X12130Y7000D03* +G01X13130Y7000D03* + +G04 Draw traces* +G54D24* +G01X19280Y5860D02* +G01X19010Y5860D01* +G01X19010Y5860D02* +G01X18450Y5300D01* +G01X18450Y5300D02* +G01X18450Y2910D01* +G01X18450Y2910D02* +G01X17740Y2200D01* +G54D25* +G01X3960Y7900D02* +G01X7990Y7900D01* +G01X7990Y7900D02* +G01X8000Y8240D01* +G01X8320Y6380D02* +G01X9630Y6380D01* +G01X9630Y6380D02* +G01X9630Y5200D01* +G01X8320Y6380D02* +G01X10840Y6380D01* +G01X10840Y6380D02* +G01X11130Y6670D01* +G01X11130Y6670D02* +G01X11130Y7000D01* +G01X8000Y8240D02* +G01X8000Y7900D01* +G01X8000Y7900D02* +G01X6280Y7900D01* +G01X6280Y7900D02* +G01X6280Y9000D01* +G01X6920Y6700D02* +G01X6920Y7900D01* +G01X6920Y7900D02* +G01X8000Y7900D01* +G01X8000Y7900D02* +G01X8000Y8240D01* +G01X8000Y8240D02* +G01X8000Y7310D01* +G01X8000Y7310D02* +G01X8320Y6990D01* +G01X8320Y6990D02* +G01X8320Y6380D01* +G01X13130Y7000D02* +G01X13130Y6310D01* +G01X13130Y6310D02* +G01X13630Y5810D01* +G01X13630Y5810D02* +G01X13630Y5200D01* +G01X15100Y8240D02* +G01X15940Y8240D01* +G01X15940Y8240D02* +G01X16340Y7860D01* +G01X16340Y7860D02* +G01X16340Y6700D01* +G01X15100Y8240D02* +G01X15940Y8240D01* +G01X15940Y8240D02* +G01X16320Y7860D01* +G01X16320Y7860D02* +G01X16960Y7860D01* +G01X16960Y7860D02* +G01X16960Y8980D01* +G01X15100Y8240D02* +G01X14370Y8240D01* +G01X14370Y8240D02* +G01X13130Y7000D01* +G01X15100Y8240D02* +G01X15960Y8240D01* +G01X15960Y8240D02* +G01X16340Y7860D01* +G01X16340Y7860D02* +G01X19280Y7860D01* +G01X14950Y6700D02* +G01X16340Y6700D01* +G54D24* +G01X13630Y2200D02* +G01X16490Y2200D01* +G01X13630Y4200D02* +G01X13070Y4200D01* +G01X13070Y4200D02* +G01X12820Y3950D01* +G01X12820Y3950D02* +G01X12820Y3460D01* +G01X12820Y3460D02* +G01X13080Y3200D01* +G01X13080Y3200D02* +G01X13630Y3200D01* +G01X14950Y4200D02* +G01X13630Y4200D01* +G01X16340Y5700D02* +G01X16640Y5690D01* +G01X16640Y5690D02* +G01X17000Y5330D01* +G01X17000Y5330D02* +G01X17000Y4080D01* +G01X17000Y4080D02* +G01X16620Y3700D01* +G01X16620Y3700D02* +G01X13630Y3700D01* +G54D26* +G01X9630Y4700D02* +G01X10430Y4700D01* +G01X10430Y4700D02* +G01X10430Y1920D01* +G01X10430Y1920D02* +G01X10210Y1700D01* +G01X10210Y1700D02* +G01X9630Y1700D01* +G01X3960Y3900D02* +G01X3960Y1160D01* +G01X3960Y1160D02* +G01X8720Y1160D01* +G01X8720Y1160D02* +G01X8720Y1700D01* +G01X8720Y1700D02* +G01X9630Y1700D01* +G01X10130Y7000D02* +G01X10130Y9740D01* +G01X10130Y9740D02* +G01X8000Y9740D01* +G01X6280Y9750D02* +G01X2670Y9750D01* +G01X2670Y9750D02* +G01X2670Y4900D01* +G01X2670Y4900D02* +G01X3960Y4900D01* +G01X8000Y9740D02* +G01X6280Y9750D01* +G01X8320Y5630D02* +G01X8320Y4700D01* +G01X8320Y4700D02* +G01X9630Y4700D01* +G01X6920Y4700D02* +G01X8320Y4700D01* +G54D24* +G01X6920Y5700D02* +G01X6480Y5700D01* +G01X6480Y5700D02* +G01X6120Y5340D01* +G01X6120Y5340D02* +G01X6120Y3910D01* +G01X6120Y3910D02* +G01X6420Y3610D01* +G01X6420Y3610D02* +G01X7450Y3610D01* +G01X7450Y3610D02* +G01X7860Y3200D01* +G01X7860Y3200D02* +G01X9630Y3200D01* +G54D26* +G01X12130Y7000D02* +G01X12130Y4700D01* +G01X12130Y4700D02* +G01X13630Y4700D01* +G01X12130Y7000D02* +G01X12130Y9740D01* +G01X12130Y9740D02* +G01X15100Y9740D01* +G01X16960Y9730D02* +G01X20390Y9722D01* +G01X20390Y9722D02* +G01X20390Y4860D01* +G01X20390Y4860D02* +G01X19280Y4860D01* +G01X15100Y9740D02* +G01X16960Y9730D01* +G01X14950Y4950D02* +G01X14950Y5950D01* +G01X13630Y4700D02* +G01X14150Y4700D01* +G01X14150Y4700D02* +G01X14400Y4950D01* +G01X14400Y4950D02* +G01X14950Y4950D01* +G01X14950Y4950D02* +G01X15420Y4950D01* +G01X15420Y4950D02* +G01X15670Y4700D01* +G01X15670Y4700D02* +G01X16340Y4700D01* +G01X13630Y1700D02* +G01X14560Y1700D01* +G01X14560Y1700D02* +G01X14560Y1170D01* +G01X14560Y1170D02* +G01X19280Y1170D01* +G01X19280Y1170D02* +G01X19280Y3860D01* +G01X12130Y7000D02* +G01X12130Y1700D01* +G01X12130Y1700D02* +G01X13630Y1700D01* +G54D24* +G01X5440Y5650D02* +G01X5440Y3570D01* +G01X5440Y3570D02* +G01X5950Y3060D01* +G01X5950Y3060D02* +G01X7360Y3060D01* +G01X7360Y3060D02* +G01X7720Y2700D01* +G01X7720Y2700D02* +G01X9630Y2700D01* +G01X6800Y2200D02* +G01X9630Y2200D01* +G01X9630Y3700D02* +G01X9630Y4200D01* +G01X9630Y3700D02* +G01X8570Y3700D01* +G01X8570Y3700D02* +G01X8320Y3950D01* +G01X3960Y6900D02* +G01X5440Y6900D01* +G01X3960Y5900D02* +G01X4090Y5900D01* +G01X4090Y5900D02* +G01X4760Y5230D01* +G01X4760Y5230D02* +G01X4760Y2990D01* +G01X4760Y2990D02* +G01X5550Y2200D01* +G01X19280Y6860D02* +G01X17720Y6860D01* +G01X13630Y2700D02* +G01X14885Y2700D01* +G01X14885Y2700D02* +G01X15314Y3120D01* +G01X15314Y3120D02* +G01X16950Y3120D01* +G01X16950Y3120D02* +G01X17720Y3890D01* +G01X17720Y3890D02* +G01X17720Y5610D01* + +G04 Draw Text* + +G04 ----------------------- Draw Pilot Holes (scratch)* +%LPC*% +G04 draw pilot holes for part J1* +G54D27* +G01X3960Y7900D03* +G01X3960Y6900D03* +G01X3960Y5900D03* +G01X3960Y4900D03* +G01X3960Y3900D03* +G04 draw pilot holes for part C1* +G04 draw pilot holes for part C2* +G04 draw pilot holes for part U1* +G04 draw pilot holes for part C3* +G04 draw pilot holes for part C4* +G04 draw pilot holes for part C5* +G04 draw pilot holes for part C6* +G04 draw pilot holes for part R1* +G04 draw pilot holes for part R2* +G04 draw pilot holes for part R3* +G04 draw pilot holes for part R4* +G04 draw pilot holes for part J2* +G01X19280Y7860D03* +G01X19280Y6860D03* +G01X19280Y5860D03* +G01X19280Y4860D03* +G01X19280Y3860D03* +G04 draw pilot holes for part C7* +G04 draw pilot holes for part C8* +G04 draw pilot holes for part J3* +G01X16340Y6700D03* +G01X16340Y5700D03* +G01X16340Y4700D03* +G04 draw pilot holes for part J4* +G01X6920Y6700D03* +G01X6920Y5700D03* +G01X6920Y4700D03* +G04 draw pilot holes for part U2* +G01X10130Y7000D03* +G01X11130Y7000D03* +G01X12130Y7000D03* +G01X13130Y7000D03* + +G04 Draw pilot holes for vias* +M00* +M02* diff --git a/src/pygerber/gerber/linter/__init__.py b/src/pygerber/gerber/linter/__init__.py index d46debc6..8ad87684 100644 --- a/src/pygerber/gerber/linter/__init__.py +++ b/src/pygerber/gerber/linter/__init__.py @@ -2,6 +2,8 @@ from __future__ import annotations +from typing import TYPE_CHECKING, Iterable, Optional + from pygerber.gerber.linter.event_ast_visitor import EventAstVisitor from pygerber.gerber.linter.linter import Linter from pygerber.gerber.linter.rule_violation import RuleViolation @@ -14,6 +16,35 @@ StaticRule, ) +if TYPE_CHECKING: + from pygerber.gerber.ast.nodes import File + + +def lint(ast: File, rules: Optional[list[str]] = None) -> Iterable[RuleViolation]: + """Lint the AST using the provided rules. + + Parameters + ---------- + ast : File + Abstract syntax tree to lint. + rules : list[str] + List of rule names to apply, if empty or None, all rules are applied. + + Returns + ------- + list[RuleViolation] + List containing rule violations found in the AST. + + """ + if rules is not None and len(rules) != 0: + rule_objects = [RULE_REGISTRY[rule_id]() for rule_id in rules] + else: + rule_objects = [r() for r in RULE_REGISTRY.values()] + + linter = Linter(rule_objects) + return linter.lint(ast) + + __all__ = [ "DEP001", "DEP002", @@ -24,4 +55,5 @@ "Rule", "RuleViolation", "StaticRule", + "lint", ] diff --git a/src/pygerber/gerber/linter/rules/GRB001.py b/src/pygerber/gerber/linter/rules/GRB001.py index b8a77e28..bb2b2e69 100644 --- a/src/pygerber/gerber/linter/rules/GRB001.py +++ b/src/pygerber/gerber/linter/rules/GRB001.py @@ -15,15 +15,20 @@ class GRB001(Rule): def get_violation_title(self) -> str: """Return a title of message that describes the rule violation.""" + node_name = "G01/G02/G03" if self.last_node is not None: - return f"""Redundant {self.last_node.__qualname__} command.""" - return "Redundant G01/G02/G03 command." + node_name = self.last_node.__qualname__ + + return f"Redundant {node_name} command." def get_violation_description(self) -> str: """Return a description of the rule violation.""" - assert self.last_node is not None + node_name = "G01/G02/G03" + if self.last_node is not None: + node_name = self.last_node.__qualname__ + return ( - f"The {self.last_node.__qualname__} command after once issued " + f"The {node_name} command after once issued " "remains in effect until different G01/G02/G03 command changes " " the interpolation mode. Some software tends to paste G01 command " "before each D01 command, significantly increasing the file size." diff --git a/test/examples/gerberx3/linter/_00_linter.example.py b/test/examples/gerberx3/linter/_00_linter.example.py new file mode 100644 index 00000000..e0055823 --- /dev/null +++ b/test/examples/gerberx3/linter/_00_linter.example.py @@ -0,0 +1,10 @@ +from pygerber.gerber.linter import lint +from pygerber.gerber.parser import parse +from pygerber.examples import ExamplesEnum, load_example + +gerber_source_code = load_example(ExamplesEnum.fc_poly_b_cu) + +ast = parse(gerber_source_code) + +for rule in lint(ast): + print(rule.line, rule.rule_id, rule.title) diff --git a/test/examples/gerberx3/linter/__init__.py b/test/examples/gerberx3/linter/__init__.py new file mode 100644 index 00000000..e69de29b