ros-parser 0.2.0__tar.gz → 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {ros_parser-0.2.0 → ros_parser-0.3.0}/PKG-INFO +1 -1
- {ros_parser-0.2.0 → ros_parser-0.3.0}/pyproject.toml +1 -1
- {ros_parser-0.2.0 → ros_parser-0.3.0}/src/ros_parser/message_path/models.py +77 -35
- {ros_parser-0.2.0 → ros_parser-0.3.0}/src/ros_parser/models.py +11 -14
- {ros_parser-0.2.0 → ros_parser-0.3.0}/README.md +0 -0
- {ros_parser-0.2.0 → ros_parser-0.3.0}/src/ros_parser/__init__.py +0 -0
- {ros_parser-0.2.0 → ros_parser-0.3.0}/src/ros_parser/_lark_standalone_runtime.py +0 -0
- {ros_parser-0.2.0 → ros_parser-0.3.0}/src/ros_parser/_utils.py +0 -0
- {ros_parser-0.2.0 → ros_parser-0.3.0}/src/ros_parser/message_path/README.md +0 -0
- {ros_parser-0.2.0 → ros_parser-0.3.0}/src/ros_parser/message_path/__init__.py +0 -0
- {ros_parser-0.2.0 → ros_parser-0.3.0}/src/ros_parser/message_path/_standalone_parser.py +0 -0
- {ros_parser-0.2.0 → ros_parser-0.3.0}/src/ros_parser/message_path/_standalone_parser.pyi +0 -0
- {ros_parser-0.2.0 → ros_parser-0.3.0}/src/ros_parser/message_path/grammar.lark +0 -0
- {ros_parser-0.2.0 → ros_parser-0.3.0}/src/ros_parser/message_path/parser.py +0 -0
- {ros_parser-0.2.0 → ros_parser-0.3.0}/src/ros_parser/py.typed +0 -0
- {ros_parser-0.2.0 → ros_parser-0.3.0}/src/ros_parser/ros1_msg/__init__.py +0 -0
- {ros_parser-0.2.0 → ros_parser-0.3.0}/src/ros_parser/ros1_msg/_standalone_parser.py +0 -0
- {ros_parser-0.2.0 → ros_parser-0.3.0}/src/ros_parser/ros1_msg/_standalone_parser.pyi +0 -0
- {ros_parser-0.2.0 → ros_parser-0.3.0}/src/ros_parser/ros1_msg/grammar.lark +0 -0
- {ros_parser-0.2.0 → ros_parser-0.3.0}/src/ros_parser/ros1_msg/parser.py +0 -0
- {ros_parser-0.2.0 → ros_parser-0.3.0}/src/ros_parser/ros1_msg/schema_parser.py +0 -0
- {ros_parser-0.2.0 → ros_parser-0.3.0}/src/ros_parser/ros2_msg/__init__.py +0 -0
- {ros_parser-0.2.0 → ros_parser-0.3.0}/src/ros_parser/ros2_msg/_standalone_parser.py +0 -0
- {ros_parser-0.2.0 → ros_parser-0.3.0}/src/ros_parser/ros2_msg/_standalone_parser.pyi +0 -0
- {ros_parser-0.2.0 → ros_parser-0.3.0}/src/ros_parser/ros2_msg/grammar.lark +0 -0
- {ros_parser-0.2.0 → ros_parser-0.3.0}/src/ros_parser/ros2_msg/parser.py +0 -0
- {ros_parser-0.2.0 → ros_parser-0.3.0}/src/ros_parser/ros2_msg/schema_parser.py +0 -0
|
@@ -5,9 +5,9 @@ from collections.abc import Callable, Mapping, Sequence
|
|
|
5
5
|
from dataclasses import dataclass
|
|
6
6
|
from enum import Enum
|
|
7
7
|
from operator import neg
|
|
8
|
-
from typing import Any, Literal
|
|
8
|
+
from typing import Any, Literal
|
|
9
9
|
|
|
10
|
-
from ros_parser.models import MessageDefinition, Type
|
|
10
|
+
from ros_parser.models import Field, MessageDefinition, Type
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
class MessagePathError(Exception):
|
|
@@ -490,6 +490,15 @@ def _wrap_angle(value: float) -> float:
|
|
|
490
490
|
return (value + math.pi) % (2 * math.pi) - math.pi
|
|
491
491
|
|
|
492
492
|
|
|
493
|
+
def _sign(value: float) -> int:
|
|
494
|
+
"""Return the sign of a numeric value: 1, -1, or 0."""
|
|
495
|
+
if value > 0:
|
|
496
|
+
return 1
|
|
497
|
+
if value < 0:
|
|
498
|
+
return -1
|
|
499
|
+
return 0
|
|
500
|
+
|
|
501
|
+
|
|
493
502
|
_FUNCTIONS_NO_ARGS: dict[str, Callable[..., Any]] = {
|
|
494
503
|
"abs": abs,
|
|
495
504
|
"acos": math.acos,
|
|
@@ -503,7 +512,7 @@ _FUNCTIONS_NO_ARGS: dict[str, Callable[..., Any]] = {
|
|
|
503
512
|
"log2": math.log2,
|
|
504
513
|
"log10": math.log10,
|
|
505
514
|
"negative": neg,
|
|
506
|
-
"sign":
|
|
515
|
+
"sign": _sign,
|
|
507
516
|
"sin": math.sin,
|
|
508
517
|
"sqrt": math.sqrt,
|
|
509
518
|
"tan": math.tan,
|
|
@@ -520,6 +529,8 @@ _FUNCTIONS_NO_ARGS: dict[str, Callable[..., Any]] = {
|
|
|
520
529
|
"wrap_angle": _wrap_angle,
|
|
521
530
|
}
|
|
522
531
|
|
|
532
|
+
TIMESERIES_OPS: set[str] = {"delta", "derivative", "timedelta"}
|
|
533
|
+
|
|
523
534
|
|
|
524
535
|
def _timeseries_sentinel(value: float) -> float: # noqa: ARG001
|
|
525
536
|
"""Sentinel for time-series functions. Raises when called without TransformContext."""
|
|
@@ -529,22 +540,33 @@ def _timeseries_sentinel(value: float) -> float: # noqa: ARG001
|
|
|
529
540
|
)
|
|
530
541
|
|
|
531
542
|
|
|
532
|
-
|
|
543
|
+
for _op in TIMESERIES_OPS:
|
|
544
|
+
_FUNCTIONS_NO_ARGS[_op] = _timeseries_sentinel
|
|
533
545
|
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
546
|
+
|
|
547
|
+
def _get_field(obj: Any, name: str) -> Any:
|
|
548
|
+
"""Get a field from an object, trying mapping access first, then attribute access."""
|
|
549
|
+
if isinstance(obj, Mapping):
|
|
550
|
+
try:
|
|
551
|
+
return obj[name]
|
|
552
|
+
except KeyError:
|
|
553
|
+
raise MessagePathError(f"Field '{name}' not found in mapping") from None
|
|
554
|
+
try:
|
|
555
|
+
return getattr(obj, name)
|
|
556
|
+
except AttributeError:
|
|
557
|
+
raise MessagePathError(
|
|
558
|
+
f"Field '{name}' not found on object of type '{type(obj).__name__}'"
|
|
559
|
+
) from None
|
|
538
560
|
|
|
539
561
|
|
|
540
562
|
def _norm(obj: Any) -> float:
|
|
541
563
|
"""Euclidean norm of object with x/y/z fields."""
|
|
542
564
|
try:
|
|
543
|
-
x =
|
|
544
|
-
y =
|
|
545
|
-
z =
|
|
546
|
-
except
|
|
547
|
-
raise MessagePathError("norm requires an object with x, y, z fields") from
|
|
565
|
+
x = _get_field(obj, "x")
|
|
566
|
+
y = _get_field(obj, "y")
|
|
567
|
+
z = _get_field(obj, "z")
|
|
568
|
+
except MessagePathError:
|
|
569
|
+
raise MessagePathError("norm requires an object with x, y, z fields") from None
|
|
548
570
|
return math.sqrt(x * x + y * y + z * z)
|
|
549
571
|
|
|
550
572
|
|
|
@@ -570,12 +592,12 @@ class Quaternion:
|
|
|
570
592
|
def _quaternion_to_euler(obj: Any) -> EulerAngles:
|
|
571
593
|
"""Convert quaternion (x,y,z,w) to EulerAngles(roll, pitch, yaw)."""
|
|
572
594
|
try:
|
|
573
|
-
x =
|
|
574
|
-
y =
|
|
575
|
-
z =
|
|
576
|
-
w =
|
|
577
|
-
except
|
|
578
|
-
raise MessagePathError("rpy requires an object with x, y, z, w fields") from
|
|
595
|
+
x = _get_field(obj, "x")
|
|
596
|
+
y = _get_field(obj, "y")
|
|
597
|
+
z = _get_field(obj, "z")
|
|
598
|
+
w = _get_field(obj, "w")
|
|
599
|
+
except MessagePathError:
|
|
600
|
+
raise MessagePathError("rpy requires an object with x, y, z, w fields") from None
|
|
579
601
|
|
|
580
602
|
t0 = 2.0 * (w * x + y * z)
|
|
581
603
|
t1 = 1.0 - 2.0 * (x * x + y * y)
|
|
@@ -594,13 +616,13 @@ def _quaternion_to_euler(obj: Any) -> EulerAngles:
|
|
|
594
616
|
def _euler_to_quaternion(obj: Any) -> Quaternion:
|
|
595
617
|
"""Convert (roll, pitch, yaw) stored as (x, y, z) to Quaternion(x, y, z, w)."""
|
|
596
618
|
try:
|
|
597
|
-
roll =
|
|
598
|
-
pitch =
|
|
599
|
-
yaw =
|
|
600
|
-
except
|
|
619
|
+
roll = _get_field(obj, "x")
|
|
620
|
+
pitch = _get_field(obj, "y")
|
|
621
|
+
yaw = _get_field(obj, "z")
|
|
622
|
+
except MessagePathError:
|
|
601
623
|
raise MessagePathError(
|
|
602
624
|
"quat requires an object with x, y, z fields (roll, pitch, yaw)"
|
|
603
|
-
) from
|
|
625
|
+
) from None
|
|
604
626
|
|
|
605
627
|
cr, sr = math.cos(roll / 2), math.sin(roll / 2)
|
|
606
628
|
cp, sp = math.cos(pitch / 2), math.sin(pitch / 2)
|
|
@@ -633,6 +655,28 @@ _OBJECT_FUNCTIONS: dict[str, Callable[..., Any]] = {
|
|
|
633
655
|
"magnitude": _magnitude,
|
|
634
656
|
}
|
|
635
657
|
|
|
658
|
+
_FLOAT64_TYPE = Type(type_name="float64", package_name=None)
|
|
659
|
+
|
|
660
|
+
_OBJECT_FUNCTION_RETURN_TYPES: dict[str, MessageDefinition] = {
|
|
661
|
+
"rpy": MessageDefinition(
|
|
662
|
+
name="EulerAngles",
|
|
663
|
+
fields_all=[
|
|
664
|
+
Field(type=_FLOAT64_TYPE, name="roll"),
|
|
665
|
+
Field(type=_FLOAT64_TYPE, name="pitch"),
|
|
666
|
+
Field(type=_FLOAT64_TYPE, name="yaw"),
|
|
667
|
+
],
|
|
668
|
+
),
|
|
669
|
+
"quat": MessageDefinition(
|
|
670
|
+
name="Quaternion",
|
|
671
|
+
fields_all=[
|
|
672
|
+
Field(type=_FLOAT64_TYPE, name="x"),
|
|
673
|
+
Field(type=_FLOAT64_TYPE, name="y"),
|
|
674
|
+
Field(type=_FLOAT64_TYPE, name="z"),
|
|
675
|
+
Field(type=_FLOAT64_TYPE, name="w"),
|
|
676
|
+
],
|
|
677
|
+
),
|
|
678
|
+
}
|
|
679
|
+
|
|
636
680
|
|
|
637
681
|
@dataclass
|
|
638
682
|
class MathModifier(Action):
|
|
@@ -688,7 +732,8 @@ class MathModifier(Action):
|
|
|
688
732
|
|
|
689
733
|
try:
|
|
690
734
|
if func := _FUNCTIONS_NO_ARGS.get(self.operation):
|
|
691
|
-
|
|
735
|
+
result: int | float = func(value, *args)
|
|
736
|
+
return result
|
|
692
737
|
|
|
693
738
|
# Unknown operation
|
|
694
739
|
raise MessagePathError(f"Unknown math modifier '{self.operation}'")
|
|
@@ -722,16 +767,13 @@ class MathModifier(Action):
|
|
|
722
767
|
|
|
723
768
|
# Object-level functions work on complex types
|
|
724
769
|
if self.operation in _OBJECT_FUNCTIONS:
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
type_name="
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
string_upper_bound=None,
|
|
733
|
-
)
|
|
734
|
-
return float_type, None
|
|
770
|
+
result_def = _OBJECT_FUNCTION_RETURN_TYPES.get(self.operation)
|
|
771
|
+
if result_def is not None:
|
|
772
|
+
result_type = Type(type_name=result_def.name or "unknown", package_name=None)
|
|
773
|
+
return result_type, result_def
|
|
774
|
+
|
|
775
|
+
# norm, magnitude → scalar float
|
|
776
|
+
return _FLOAT64_TYPE, None
|
|
735
777
|
|
|
736
778
|
# Time-series functions work on numeric types, preserve type
|
|
737
779
|
if self.operation in TIMESERIES_OPS:
|
|
@@ -106,8 +106,6 @@ class Field:
|
|
|
106
106
|
if self.default_value is not None:
|
|
107
107
|
if isinstance(self.default_value, str):
|
|
108
108
|
result += f" '{self.default_value}'"
|
|
109
|
-
elif isinstance(self.default_value, list):
|
|
110
|
-
result += f" {self.default_value}"
|
|
111
109
|
else:
|
|
112
110
|
result += f" {self.default_value}"
|
|
113
111
|
return result
|
|
@@ -182,11 +180,7 @@ class ServiceDefinition:
|
|
|
182
180
|
|
|
183
181
|
def __str__(self) -> str:
|
|
184
182
|
"""Return the string representation of the service."""
|
|
185
|
-
|
|
186
|
-
lines.append(str(self.request))
|
|
187
|
-
lines.append("---")
|
|
188
|
-
lines.append(str(self.response))
|
|
189
|
-
return "\n".join(lines)
|
|
183
|
+
return "\n".join([f"# {self.name}", str(self.request), "---", str(self.response)])
|
|
190
184
|
|
|
191
185
|
|
|
192
186
|
@dataclass
|
|
@@ -200,10 +194,13 @@ class ActionDefinition:
|
|
|
200
194
|
|
|
201
195
|
def __str__(self) -> str:
|
|
202
196
|
"""Return the string representation of the action."""
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
197
|
+
return "\n".join(
|
|
198
|
+
[
|
|
199
|
+
f"# {self.name}",
|
|
200
|
+
str(self.goal),
|
|
201
|
+
"---",
|
|
202
|
+
str(self.result),
|
|
203
|
+
"---",
|
|
204
|
+
str(self.feedback),
|
|
205
|
+
]
|
|
206
|
+
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|