Kompletny program: interpreter Logo¶
Logo jest językiem programowania stworzonym w latach 60. XX wieku. Jego głównym celem było umożliwienie dzieciom nauki podstaw programowania poprzez interakcję z komputerem. Programowanie w Logo sprowadza się do wydawania komend żółwiowi, kierując jego ruchem po ekranie. Wynikiem programu jest obrazek - ślad narysowany przez poruszającego się żółwia.
Poniższej znajdziesz opis prostego interpretera tego języka. Rozróżnia on następujące komendy:
L n - obróć żółwia o n stopni w lewo, np
L 10obraca żółwia w lewo o 10 stopniR n - obróć żółwia o n stopni w prawo, np
R 15obraca żółwia w prawo o 15 stopniUP - podnieś rysik
DOWN - opuść rysik
F n - przesuń żółwia naprzód o n; żółw narysuje linię, o ile rysik jest aktualnie opuszczony
JUMP x y - przenieś żółwia w położenie x, y na ekranie, np
JUMP 100 100przenosi żółwia w położenie 100, 100REPEAT n [commands] – powtarza n razy komendy podane w nawiasie, np
REPEAT 4 [F 10; R90]rysuje kwadrat o boku 10DEF name := [commands] – definiuje procedurę o podanej nazwie, np
DEF kwadrat := [ REPEAT 4 [F 10; R90] ]FASTER n - dodaje n do długości kroku żółwia; początkowa długość kroku to 1.0
SLOWER n - odejmuje n od długości kroku żółwia
Interpreter pozwala na zagnieżdżanie procedur w pętlach; nie jest jednak możliwe deklarowanie zmiennych.
Zauważ, że jedynie komenda F (forward) powoduje rysowanie, i to o ile rysik jest opuszczony. Poniższy program w Logo
rysuje kwadrat o boku 10:
DOWN
F 10
R 90
F 10
R 90
F 10
R 90
F 10
Żółw rysuje cztery odcinki, każdy o długości 10 (F 10), za każdym razem odpowiednio obraca się w prawo o 90 stopni R 90.
Ten sam program można zapisać, wykorzystując pętlę:
DOWN
REPEAT [ F 10; R 90 ]
Kompletny program¶
from __future__ import annotations
import math
import re
from abc import ABC, abstractmethod
class GraphicsDevice(ABC):
@abstractmethod
def line(self, xb, yb, xe, ye): pass
class EchoDevice(GraphicsDevice):
def line(self, xb, yb, xe, ye):
print(f"L {xb:.2f},{yb:.2f} : {xe:.2f},{ye:.2f}")
class SVGDevice:
def __init__(self, width, height, fname:str):
self.__width = width
self.__height = height
self.__file = open(fname, "w")
self.__file.write(f"""<svg viewBox="0 0 {width} {height}" xmlns="http://www.w3.org/2000/svg">\n""")
def line(self, xb, yb, xe, ye):
print(f"<line x1='{xb}' y1='{yb}' x2='{xe}' y2='{ye}' stroke='black' />", file=self.__file)
def __del__(self):
self.__file.write("</svg>\n")
self.__file.close()
class Turtle:
def __init__(self, gd: GraphicsDevice):
""" Creates a new turtle """
self.__device = gd
self.__x = 0
self.__y = 0
self.__a = 0
self.__step = 1.0
self.__if_drawing = False
def up(self):
self.__if_drawing = False
def down(self):
self.__if_drawing = True
def jump(self, new_x, new_y):
self.__x = new_x
self.__y = new_y
def right(self, d_alpha_deg):
self.__a -= d_alpha_deg
def left(self, d_alpha_deg):
self.__a += d_alpha_deg
def angle(self, alpha_deg):
self.__a = alpha_deg
@property
def step_size(self): return self.__step
@step_size.setter
def step_size(self, new_step): self.__step = new_step
def forward(self, displacement):
new_x = self.__step * displacement * math.cos(self.__a/180.0 * math.pi) + self.__x
new_y = self.__step * displacement * math.sin(self.__a/180.0 * math.pi) + self.__y
if self.__if_drawing:
self.__device.line(self.__x, self.__y, new_x, new_y)
self.__x = new_x
self.__y = new_y
class Command(ABC):
def __init__(self, trtl: Turtle):
self._turtle = trtl
@abstractmethod
def execute(self): pass
class JumpCommand(Command):
def __init__(self, trtl: Turtle):
super().__init__(trtl)
def execute(self, *params):
x = int(params[0][0])
y = int(params[0][0])
self._turtle.jump(x, y)
class UpCommand(Command):
def __init__(self, trtl: Turtle):
super().__init__(trtl)
def execute(self, *params): self._turtle.up()
class DownCommand(Command):
def __init__(self, trtl: Turtle):
super().__init__(trtl)
def execute(self, *params): self._turtle.down()
class ForwardCommand(Command):
def __init__(self, trtl: Turtle):
super().__init__(trtl)
def execute(self, *params):
self._turtle.forward(float(params[0][0]))
class FasterCommand(Command):
def __init__(self, trtl: Turtle):
super().__init__(trtl)
def execute(self, *params):
self._turtle.step_size += float(params[0][0])
class SlowerCommand(Command):
def __init__(self, trtl: Turtle):
super().__init__(trtl)
def execute(self, *params):
self._turtle.step_size -= float(params[0][0])
class LeftCommand(Command):
def __init__(self, trtl: Turtle):
super().__init__(trtl)
def execute(self, *params):
self._turtle.left(float(params[0][0]))
class RightCommand(Command):
def __init__(self, trtl: Turtle):
super().__init__(trtl)
def execute(self, *params):
self._turtle.right(float(params[0][0]))
class AngleCommand(Command):
def __init__(self, trtl: Turtle):
super().__init__(trtl)
def execute(self, *params):
self._turtle.angle(float(params[0][0]))
class Multicommand(Command):
def __init__(self, trtl: Turtle, commands: str, interpreter: Interpreter):
super().__init__(trtl)
self.__commands = commands
self.__interpreter = interpreter
def execute(self, *params):
self.__interpreter.run(self.__commands)
class DefCommand(Command):
def __init__(self, trtl: Turtle, interpreter: Interpreter):
super().__init__(trtl)
self.__interpreter = interpreter
def execute(self, *params):
arguments = params[0]
func_name = arguments[0]
l = " ".join(arguments[2:]).strip("[").strip("]")
l = re.sub(r'(?<!\[);(?![^\[]*])', '\n', l )
multicmd = Multicommand(self._turtle, l, self.__interpreter)
self.__interpreter.add_command(func_name, multicmd)
class LoopCommand(Command):
def __init__(self, trtl: Turtle, interpreter: Interpreter):
super().__init__(trtl)
self.__interpreter = interpreter
def execute(self, *params):
arguments = params[0]
n_repeat = int(arguments[0])
l = " ".join(arguments[1:]).strip("[").strip("]").replace(";","\n")
for _ in range(n_repeat):
self.__interpreter.run(l)
class Interpreter:
def __init__(self, turtle):
self.__my_turtle = turtle
self.__command_dispatch = {}
self.add_command("F", ForwardCommand(self.__my_turtle))
self.add_command("JUMP", JumpCommand(self.__my_turtle))
self.add_command("DOWN", DownCommand(self.__my_turtle))
self.add_command("FASTER", FasterCommand(self.__my_turtle))
self.add_command("SLOWER", SlowerCommand(self.__my_turtle))
self.add_command("UP", UpCommand(self.__my_turtle))
self.add_command("L", LeftCommand(self.__my_turtle))
self.add_command("R", RightCommand(self.__my_turtle))
self.add_command("A", AngleCommand(self.__my_turtle))
self.add_command("REPEAT", LoopCommand(self.__my_turtle, self))
self.add_command("DEF", DefCommand(self.__my_turtle, self))
def add_command(self, key, command):
self.__command_dispatch[key] = command
def run(self, program_text):
for line in program_text.split("\n"):
tokens = line.strip().split()
if len(tokens) > 0:
cmd_to_run = self.__command_dispatch[tokens[0]]
cmd_to_run.execute(tokens[1:])
if __name__ == "__main__":
moj_program = """
DEF square_50 := [DOWN; REPEAT 4 [F 50; R 90]; UP]
JUMP 100 100
REPEAT 18 [R 20; square_50]
"""
svg_gd = SVGDevice(200, 200, "plik.svg")
# gd = EchoDevice()
trtl = Turtle(svg_gd)
interpreter = Interpreter(trtl)
interpreter.run(moj_program)