Skip to content

Commit

Permalink
Fix tokenization of attributes without value (#144)
Browse files Browse the repository at this point in the history
Resolves: #142 

Backports:
- Release 2.2.2
- Release 2.1.2
- Release 2.0.3 (?)
  • Loading branch information
Argmaster authored Feb 11, 2024
2 parents 1d766b5 + 6a0c258 commit 0da49dd
Show file tree
Hide file tree
Showing 7 changed files with 30 additions and 14 deletions.
14 changes: 8 additions & 6 deletions src/pygerber/gerberx3/tokenizer/grammar.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""GerberX3 grammar."""

from __future__ import annotations

from dataclasses import dataclass
Expand Down Expand Up @@ -887,29 +888,30 @@ def _build_attribute_tokens(self, *, statement: bool = False) -> ParserElement:
aperture_attribute_name = self._build_name().set_name("aperture attribute name")
object_attribute_name = self._build_name().set_name("object attribute name")

comma = Literal(",")
comma_with_field = comma + self._build_field()
maybe_comma_or_maybe_comma_with_field = Opt(comma_with_field | comma)

# Set a file attribute.
tf = wrapper(
self.options.tf_file_attribute_token_cls,
Literal("TF")
+ file_attribute_name.set_results_name("attribute_name")
+ Literal(",")
+ Opt(self._build_field()),
+ maybe_comma_or_maybe_comma_with_field,
)
# Add an aperture attribute to the dictionary or modify it.
ta = wrapper(
self.options.ta_aperture_attribute_token_cls,
Literal("TA")
+ aperture_attribute_name.set_results_name("attribute_name")
+ Literal(",")
+ Opt(self._build_field()),
+ maybe_comma_or_maybe_comma_with_field,
)
# Add an object attribute to the dictionary or modify it.
to = wrapper(
self.options.to_object_attribute_token_cls,
Literal("TO")
+ object_attribute_name.set_results_name("attribute_name")
+ Literal(",")
+ Opt(self._build_field()),
+ maybe_comma_or_maybe_comma_with_field,
)
# Delete one or all attributes in the dictionary.
td = wrapper(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
"""Comment token."""


from __future__ import annotations

from typing import TYPE_CHECKING
Expand Down Expand Up @@ -39,7 +38,8 @@ def get_gerber_code(
endline: str = "\n", # noqa: ARG002
) -> str:
"""Get gerber code represented by this token."""
return f"TA{self.name},{self.value}"
value = f",{self.value}" if self.value else ""
return f"TA{self.name}{value}"

def __str__(self) -> str:
return f"{super().__str__()}::[{self.name} -> {self.value}]"
4 changes: 2 additions & 2 deletions src/pygerber/gerberx3/tokenizer/tokens/td_delete_attribute.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
"""Comment token."""


from __future__ import annotations

from typing import TYPE_CHECKING, Optional
Expand Down Expand Up @@ -55,7 +54,8 @@ def get_gerber_code(
endline: str = "\n", # noqa: ARG002
) -> str:
"""Get gerber code represented by this token."""
return f"TD{self.name if self.name is not None else ''}"
name = self.name if self.name is not None else ""
return f"TD{name}"

def __str__(self) -> str:
return f"{super().__str__()}::[{self.name}]"
4 changes: 2 additions & 2 deletions src/pygerber/gerberx3/tokenizer/tokens/tf_file_attribute.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
"""Comment token."""


from __future__ import annotations

from typing import TYPE_CHECKING
Expand Down Expand Up @@ -35,7 +34,8 @@ def get_gerber_code(
endline: str = "\n", # noqa: ARG002
) -> str:
"""Get gerber code represented by this token."""
return f"TF{self.name},{self.value}"
value = f",{self.value}" if self.value else ""
return f"TF{self.name}{value}"

def __str__(self) -> str:
return f"{super().__str__()}::[{self.name} -> {self.value}]"
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ def get_gerber_code(
endline: str = "\n", # noqa: ARG002
) -> str:
"""Get gerber code represented by this token."""
return f"TO{self.name},{self.value}"
value = f",{self.value}" if self.value else ""
return f"TO{self.name}{value}"

def __str__(self) -> str:
return f"{super().__str__()}::[{self.name} -> {self.value}]"
5 changes: 5 additions & 0 deletions test/examples/test_examples.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@
renderer_2_svg_render,
)

import pytest

from pygerber.gerberx3.renderer2.svg import IS_SVG_BACKEND_AVAILABLE

DIRECTORY = Path(__file__).parent


Expand Down Expand Up @@ -61,5 +65,6 @@ def test_renderer_2_raster_render() -> None:
renderer_2_raster_render.render()


@pytest.mark.skipif(not IS_SVG_BACKEND_AVAILABLE, reason="SVG backend required")
def test_renderer_2_svg_render() -> None:
renderer_2_svg_render.render()
10 changes: 9 additions & 1 deletion test/gerberx3/test_renderer2/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@
from pygerber.gerberx3.parser2.parser2 import Parser2
from pygerber.gerberx3.renderer2.abstract import ImageRef
from pygerber.gerberx3.renderer2.raster import RasterRenderer2, RasterRenderer2Hooks
from pygerber.gerberx3.renderer2.svg import SvgRenderer2, SvgRenderer2Hooks
from pygerber.gerberx3.renderer2.svg import (
IS_SVG_BACKEND_AVAILABLE,
SvgRenderer2,
SvgRenderer2Hooks,
)
from pygerber.gerberx3.tokenizer.tokenizer import Tokenizer

if TYPE_CHECKING:
Expand Down Expand Up @@ -72,6 +76,10 @@ def make_svg_renderer2_test(
"""
debug_output_directory = Path(test_file_path).parent / ".output" / "svg"

@pytest.mark.skipif(
IS_SVG_BACKEND_AVAILABLE is False,
reason="No SVG backend available",
)
@pytest.mark.parametrize(
("directory", "file_name"),
sorted(find_gerberx3_asset_files(path_to_assets)),
Expand Down

0 comments on commit 0da49dd

Please sign in to comment.