261 lines
5.6 KiB
Python
261 lines
5.6 KiB
Python
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
# SPDX-FileCopyrightText: 2023 Wojtek Porczyk <woju@hackerspace.pl>
|
|
|
|
import abc
|
|
import dataclasses
|
|
import enum
|
|
|
|
from abc import abstractmethod
|
|
from dataclasses import dataclass
|
|
from typing import (
|
|
Callable,
|
|
List,
|
|
Tuple,
|
|
Union,
|
|
)
|
|
|
|
|
|
class Direction(enum.Enum):
|
|
# fmt: off
|
|
HERE = 'TUTAJ'
|
|
IN_FRONT = 'Z PRZODU'
|
|
ON_THE_LEFT = 'Z LEWEJ'
|
|
ON_THE_RIGHT = 'Z PRAWEJ'
|
|
BEHIND = 'Z TYŁU'
|
|
# fmt: on
|
|
|
|
def __repr__(self):
|
|
return f'{type(self).__name__}.{self.name}'
|
|
|
|
|
|
class Object(enum.Enum):
|
|
# fmt: off
|
|
OBSTACLE = 'PRZESZKODA'
|
|
ELEVATED = 'WZNIESIENIE'
|
|
MARK_1 = 'KOPERTA: KWADRAT'
|
|
MARK_2 = 'KOPERTA: KRZYŻYK'
|
|
COLLECTABLE = 'PRZEDMIOT DO ZEBRANIA'
|
|
ACTION = 'POLE AKCJI'
|
|
EMPTY = 'PUSTE POLE'
|
|
# fmt: on
|
|
|
|
def __repr__(self):
|
|
return f'{type(self).__name__}.{self.name}'
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class Parsed(metaclass=abc.ABCMeta):
|
|
line: int = dataclasses.field(repr=False)
|
|
cols: Tuple[int, int] = dataclasses.field(repr=False)
|
|
|
|
|
|
class Expression(Parsed):
|
|
@abstractmethod
|
|
def eval(self, engine) -> int:
|
|
raise NotImplementedError()
|
|
|
|
|
|
class Forever(Parsed):
|
|
pass
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class Number(Expression):
|
|
value: int
|
|
|
|
def eval(self, engine):
|
|
return engine.eval_Number(self)
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class Variable(Expression):
|
|
name: str
|
|
|
|
def eval(self, engine):
|
|
return engine.eval_Variable(self)
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class BinOp(Expression):
|
|
op: Callable[[int, int], int]
|
|
left: Expression
|
|
right: Expression
|
|
|
|
def eval(self, engine):
|
|
return engine.eval_BinOp(self)
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class Condition(Parsed):
|
|
direction: Direction
|
|
object: Object
|
|
|
|
def eval(self, engine):
|
|
return engine.eval_Condition(self)
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class Statement(Parsed):
|
|
#: Range of lines. For simple statement this is (self.line, self.line).
|
|
lines: Tuple[int, int] = dataclasses.field(init=False, repr=False)
|
|
|
|
def __post_init__(self):
|
|
# in case a child class overrode this attribute, we don't overwrite
|
|
try:
|
|
self.lines
|
|
except AttributeError:
|
|
# we're dataclass(frozen=True), so we need to resort to tricks
|
|
object.__setattr__(self, 'lines', (self.line, self.line))
|
|
|
|
@abstractmethod
|
|
def execute(self, engine: 'BaseEngine'):
|
|
raise NotImplementedError()
|
|
|
|
|
|
class TurnLeft(Statement):
|
|
def execute(self, engine):
|
|
return engine.execute_TurnLeft(self)
|
|
|
|
|
|
class TurnRight(Statement):
|
|
def execute(self, engine):
|
|
return engine.execute_TurnRight(self)
|
|
|
|
|
|
class Jump(Statement):
|
|
def execute(self, engine):
|
|
return engine.execute_Jump(self)
|
|
|
|
|
|
class PickUp(Statement):
|
|
def execute(self, engine):
|
|
return engine.execute_PickUp(self)
|
|
|
|
|
|
class Activate(Statement):
|
|
def execute(self, engine):
|
|
return engine.execute_Activate(self)
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class Draw(Statement):
|
|
def execute(self, engine):
|
|
return engine.execute_Draw(self)
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class Step(Statement):
|
|
counter: Expression
|
|
|
|
def execute(self, engine):
|
|
return engine.execute_Step(self)
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class Place(Statement):
|
|
direction: Direction
|
|
|
|
def execute(self, engine):
|
|
return engine.execute_Place(self)
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class Assign(Statement):
|
|
name: str
|
|
expression: Expression
|
|
|
|
def execute(self, engine):
|
|
return engine.execute_Assign(self)
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class CallFunction(Statement):
|
|
name: str
|
|
|
|
def execute(self, engine):
|
|
return engine.execute_CallFunction(self)
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class Break(Statement):
|
|
def execute(self, engine):
|
|
return engine.execute_Break(self)
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class BreakFunction(Statement):
|
|
def execute(self, engine):
|
|
return engine.execute_BreakFunction(self)
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class Block(Statement):
|
|
# pylint: disable=abstract-method
|
|
lines: Tuple[int, int] = dataclasses.field(repr=False)
|
|
body: List[Statement]
|
|
body_lines: Tuple[int, int] = dataclasses.field(init=False, repr=False)
|
|
|
|
def __post_init__(self):
|
|
try:
|
|
self.body_lines
|
|
except AttributeError:
|
|
object.__setattr__(
|
|
self,
|
|
'body_lines',
|
|
(
|
|
min(stmt.lines[0] for stmt in self.body),
|
|
max(stmt.lines[1] for stmt in self.body),
|
|
)
|
|
if self.body
|
|
else (None, None),
|
|
)
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class Repeat(Block):
|
|
counter: Union[Expression, Forever]
|
|
|
|
def execute(self, engine):
|
|
return engine.execute_Repeat(self)
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class ConditionStatement(Block):
|
|
# pylint: disable=abstract-method
|
|
condition: Condition
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class RepeatWhile(ConditionStatement):
|
|
def execute(self, engine):
|
|
return engine.execute_RepeatWhile(self)
|
|
|
|
|
|
class Else(Block):
|
|
def execute(self, engine):
|
|
return engine.execute_Else(self)
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class If(ConditionStatement):
|
|
orelse: Union['If', Else]
|
|
|
|
def execute(self, engine):
|
|
return engine.execute_If(self)
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class Programme(Block):
|
|
def execute(self, engine):
|
|
return engine.execute_Programme(self)
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class Function(Block):
|
|
name: str
|
|
|
|
def execute(self, engine):
|
|
return engine.execute_Function(self)
|
|
|
|
|
|
# vim: tw=80 ts=4 sts=4 sw=4 et
|