import ast
import functools
import inspect
import operator
import sys
import typing
from typing import Any, ForwardRef, Optional, Union
[docs]
class AST(ast.AST):
"""Scenic AST base class.
N.B. The attributes ``_fields`` and ``_field_types`` expected in subclasses
of `ast.AST` are synthesized automatically from a dataclass-like syntax:
see below for many examples.
"""
_attributes = ("lineno", "col_offset", "end_lineno", "end_col_offset")
def __init_subclass__(cls):
super().__init_subclass__()
fts = _getFields(cls)
if sys.version_info[:2] == (3, 13):
# The Python 3.13 ast.AST initializer expects types like X | None, but we
# use Optional[X] for compatibility; convert the latter into the former.
# (In 3.14+, Optional[X] and X | None are identical at runtime.)
optionalTy = type(Optional[str])
for field, ty in fts.items():
if isinstance(ty, optionalTy):
args = typing.get_args(ty)
if any(isinstance(arg, ForwardRef) for arg in args):
# ForwardRef doesn't support | in 3.13, so simplify to Any
newTy = Any | None
else:
newTy = functools.reduce(operator.or_, args)
fts[field] = newTy
cls._field_types = fts
cls._fields = tuple(cls._field_types)
cls.__match_args__ = tuple(cls._fields)
if sys.version_info >= (3, 10):
_getFields = inspect.get_annotations
else:
# We won't bother with un-stringizing annotations: they won't be used anyway
_getFields = lambda cls: cls.__dict__.get("__annotations__", {})
# special statements
[docs]
class TryInterrupt(AST):
"""Scenic AST node that represents try-interrupt statements"""
body: typing.List[ast.stmt]
interrupt_when_handlers: typing.List["InterruptWhenHandler"]
except_handlers: typing.List[ast.ExceptHandler]
orelse: typing.List[ast.stmt]
finalbody: typing.List[ast.AST]
class InterruptWhenHandler(AST):
cond: ast.AST
body: typing.List[ast.AST]
class TrackedAssign(AST):
target: Union["Ego", "Workspace"]
value: ast.AST
[docs]
class Ego(AST):
"""`ego` tracked assign target"""
functionName = "ego"
[docs]
class Workspace(AST):
""":term:`workspace` tracked assign target"""
functionName = "workspace"
class InitialScenario(AST):
pass
class PropertyDef(AST):
property: str
attributes: typing.List[Union["Additive", "Dynamic", "Final"]]
value: ast.AST
class Additive(AST):
keyword = "additive"
class Dynamic(AST):
keyword = "dynamic"
class Final(AST):
keyword = "final"
# behavior / monitor
class BehaviorDef(AST):
name: str
args: ast.arguments
docstring: Optional[str]
header: typing.List[Union["Precondition", "Invariant"]]
body: typing.List[ast.AST]
class MonitorDef(AST):
name: str
args: ast.arguments
docstring: Optional[str]
body: typing.List[ast.AST]
class Precondition(AST):
value: ast.AST
class Invariant(AST):
value: ast.AST
# modular scenarios
class ScenarioDef(AST):
name: str
args: ast.arguments
docstring: Optional[str]
header: Optional[typing.List[Union[Precondition, Invariant]]]
setup: typing.List[ast.AST]
compose: typing.List[ast.AST]
# simple statements
class Model(AST):
name: str
[docs]
class Param(AST):
""":keyword:`param` statements"""
elts: typing.List["parameter"]
[docs]
class parameter(AST):
"""Represents a parameter that is defined with `param` statements"""
identifier: str
value: ast.AST
class Require(AST):
cond: ast.AST
prob: Optional[float] = None
name: Optional[str] = None
class RequireMonitor(AST):
monitor: ast.AST
name: Optional[str] = None
class Always(AST):
value: ast.AST
class Eventually(AST):
value: ast.AST
class Next(AST):
value: ast.AST
class Record(AST):
value: ast.AST
name: Optional[str] = None
recorder: Optional[Any] = None
period: Optional[Union["Seconds", "Steps"]] = None
delay: Optional[Union["Seconds", "Steps"]] = None
class RecordInitial(AST):
value: ast.AST
name: Optional[str] = None
class RecordFinal(AST):
value: ast.AST
name: Optional[str] = None
class Mutate(AST):
elts: typing.List[ast.Name]
scale: Optional[ast.AST] = None
class Override(AST):
target: ast.AST
specifiers: typing.List[ast.AST]
class Abort(AST):
pass
class Take(AST):
elts: typing.List[ast.AST]
class Wait(AST):
pass
class WaitFor(AST):
duration: Union["Seconds", "Steps"]
class WaitUntil(AST):
cond: ast.AST
class Terminate(AST):
pass
class TerminateSimulation(AST):
pass
class TerminateSimulationWhen(AST):
cond: ast.AST
name: Optional[str] = None
class TerminateWhen(AST):
cond: ast.AST
name: Optional[str] = None
class TerminateAfter(AST):
duration: Union["Seconds", "Steps"]
class DoFor(AST):
elts: typing.List[ast.AST]
duration: Union["Seconds", "Steps"]
class Seconds(AST):
unitStr = "seconds"
value: ast.AST
class Steps(AST):
unitStr = "steps"
value: ast.AST
class DoUntil(AST):
elts: typing.List[ast.AST]
cond: ast.AST
class DoChoose(AST):
elts: typing.List[ast.AST]
class DoShuffle(AST):
elts: typing.List[ast.AST]
class Do(AST):
elts: typing.List[ast.AST]
class Simulator(AST):
value: ast.AST
# Instance Creation
class New(AST):
className: str
specifiers: Optional[typing.List[ast.AST]]
def __init__(self, className, specifiers, **kwargs):
specs = specifiers if specifiers is not None else []
super().__init__(className, specs, **kwargs)
# Specifiers
class WithSpecifier(AST):
prop: str
value: ast.AST
class AtSpecifier(AST):
position: ast.AST
class OffsetBySpecifier(AST):
offset: ast.AST
class OffsetAlongSpecifier(AST):
direction: ast.AST
offset: ast.AST
class DirectionOfSpecifier(AST):
direction: Union["LeftOf", "RightOf", "AheadOf", "Behind"]
position: ast.AST
distance: Optional[ast.AST] = None
class LeftOf(AST):
pass
class RightOf(AST):
pass
class AheadOf(AST):
pass
class Behind(AST):
pass
class Above(AST):
pass
class Below(AST):
pass
class BeyondSpecifier(AST):
position: ast.AST
offset: ast.AST
base: Optional[ast.AST] = None
class VisibleSpecifier(AST):
base: Optional[ast.AST] = None
class NotVisibleSpecifier(AST):
base: Optional[ast.AST] = None
class InSpecifier(AST):
region: ast.AST
class OnSpecifier(AST):
region: ast.AST
class ContainedInSpecifier(AST):
region: ast.AST
class FollowingSpecifier(AST):
field: ast.AST
distance: ast.AST
base: Optional[ast.AST] = None
class FacingSpecifier(AST):
heading: ast.AST
class FacingTowardSpecifier(AST):
position: ast.AST
class FacingAwayFromSpecifier(AST):
position: ast.AST
class FacingDirectlyTowardSpecifier(AST):
position: ast.AST
class FacingDirectlyAwayFromSpecifier(AST):
position: ast.AST
class ApparentlyFacingSpecifier(AST):
heading: ast.AST
base: Optional[ast.AST] = None
# Operators
class ImpliesOp(AST):
hypothesis: ast.AST
conclusion: ast.AST
class UntilOp(AST):
left: ast.AST
right: ast.AST
class RelativePositionOp(AST):
target: ast.AST
base: Optional[ast.AST] = None
class RelativeHeadingOp(AST):
target: ast.AST
base: Optional[ast.AST] = None
class ApparentHeadingOp(AST):
target: ast.AST
base: Optional[ast.AST] = None
class DistanceFromOp(AST):
# because `to` and `from` are symmetric, the first operand will be `target` and the second will be `base`
target: ast.AST
base: Optional[ast.AST] = None
class DistancePastOp(AST):
target: ast.AST
base: Optional[ast.AST] = None
class AngleFromOp(AST):
target: Optional[ast.AST] = None
base: Optional[ast.AST] = None
class AltitudeFromOp(AST):
target: Optional[ast.AST] = None
base: Optional[ast.AST] = None
class FollowOp(AST):
target: ast.AST
base: ast.AST
distance: ast.AST
class VisibleOp(AST):
region: ast.AST
class NotVisibleOp(AST):
region: ast.AST
class VisibleFromOp(AST):
region: ast.AST
base: ast.AST
class NotVisibleFromOp(AST):
region: ast.AST
base: ast.AST
class PositionOfOp(AST):
position: Union[
"Front",
"Back",
"Left",
"Right",
"Top",
"Bottom",
"FrontLeft",
"FrontRight",
"BackLeft",
"BackRight",
"TopFrontLeft",
"TopFrontRight",
"TopBackLeft",
"TopBackRight",
"BottomFrontLeft",
"BottomFrontRight",
"BottomBackLeft",
"BottomBackRight",
]
target: ast.AST
[docs]
class Front(AST):
"""Represents position of :scenic:`front of` operator"""
functionName = "Front"
[docs]
class Back(AST):
"""Represents position of :scenic:`back of` operator"""
functionName = "Back"
[docs]
class Left(AST):
"""Represents position of :scenic:`left of` operator"""
functionName = "Left"
[docs]
class Right(AST):
"""Represents position of :scenic:`right of` operator"""
functionName = "Right"
[docs]
class Top(AST):
"""Represents position of :scenic:`top of` operator"""
functionName = "Top"
[docs]
class Bottom(AST):
"""Represents position of :scenic:`bottom of` operator"""
functionName = "Bottom"
[docs]
class FrontLeft(AST):
"""Represents position of :scenic:`front left of` operator"""
functionName = "FrontLeft"
[docs]
class FrontRight(AST):
"""Represents position of :scenic:`front right of` operator"""
functionName = "FrontRight"
[docs]
class BackLeft(AST):
"""Represents position of :scenic:`back left of` operator"""
functionName = "BackLeft"
[docs]
class BackRight(AST):
"""Represents position of :scenic:`back right of` operator"""
functionName = "BackRight"
[docs]
class TopFrontLeft(AST):
"""Represents position of :scenic:`top front left of` operator"""
functionName = "TopFrontLeft"
[docs]
class TopFrontRight(AST):
"""Represents position of :scenic:`top front right of` operator"""
functionName = "TopFrontRight"
[docs]
class TopBackLeft(AST):
"""Represents position of :scenic:`top back left of` operator"""
functionName = "TopBackLeft"
[docs]
class TopBackRight(AST):
"""Represents position of :scenic:`top back right of` operator"""
functionName = "TopBackRight"
[docs]
class BottomFrontLeft(AST):
"""Represents position of :scenic:`bottom front left of` operator"""
functionName = "BottomFrontLeft"
[docs]
class BottomFrontRight(AST):
"""Represents position of :scenic:`bottom front right of` operator"""
functionName = "BottomFrontRight"
[docs]
class BottomBackLeft(AST):
"""Represents position of :scenic:`bottom back left of` operator"""
functionName = "BottomBackLeft"
[docs]
class BottomBackRight(AST):
"""Represents position of :scenic:`bottom back right of` operator"""
functionName = "BottomBackRight"
class DegOp(AST):
operand: ast.AST
class VectorOp(AST):
left: ast.AST
right: ast.AST
class FieldAtOp(AST):
left: ast.AST
right: ast.AST
class RelativeToOp(AST):
left: ast.AST
right: ast.AST
class OffsetAlongOp(AST):
base: ast.AST
direction: ast.AST
offset: ast.AST
class CanSeeOp(AST):
left: ast.AST
right: ast.AST
class IntersectsOp(AST):
left: ast.AST
right: ast.AST