Lephe
a983faedeb
This change adds a [cond] grammar symbol corresponding to binary relations in IF, IFELSE and WHILE conditions. It also adds support for unary functions with parenthesis syntax. Other functions will need specific rules depending on their operator precedence level. Also adds the Window.save() function that implements the --save option to save the output of the program into a bitmap file. This will be used to perform automated unit tests.
156 lines
4.4 KiB
Python
156 lines
4.4 KiB
Python
# fx-92 Scientifique Collège+ language interpreter: On-screen drawing
|
|
|
|
from sdl2 import *
|
|
|
|
class Window:
|
|
"""
|
|
An SDL window with pixel scaling.
|
|
"""
|
|
|
|
BLACK = (0, 0, 0, 255)
|
|
WHITE = (255, 255, 255, 255)
|
|
|
|
def __init__(self, width, height, scale, quiet=False):
|
|
self.width = width
|
|
self.height = height
|
|
self.scale = scale
|
|
self.quiet = quiet
|
|
|
|
def __enter__(self):
|
|
"""
|
|
Enter window context: allocate and create a window.
|
|
"""
|
|
|
|
if SDL_WasInit(SDL_INIT_VIDEO):
|
|
raise Exception("Cannot create two windows")
|
|
|
|
if SDL_Init(SDL_INIT_VIDEO) < 0:
|
|
raise Exception("Failed to initialize SDL")
|
|
|
|
# Create the window
|
|
mode = SDL_WINDOW_HIDDEN if self.quiet else SDL_WINDOW_SHOWN
|
|
self.w = SDL_CreateWindow("fx-92 Scientifique Collège+".encode(),
|
|
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
|
|
self.width*self.scale, self.height*self.scale, mode)
|
|
if self.w is None:
|
|
raise Exception("Failed to create window")
|
|
|
|
# Create the renderer
|
|
self.r = SDL_CreateRenderer(self.w, -1, SDL_RENDERER_ACCELERATED)
|
|
if self.r is None:
|
|
SDL_DestroyWindow(self.w)
|
|
raise Exception("Failed to create renderer")
|
|
|
|
# Create the base texture where things will be rendered at scale 1
|
|
self.t = SDL_CreateTexture(self.r, SDL_PIXELFORMAT_ARGB8888,
|
|
SDL_TEXTUREACCESS_TARGET, self.width, self.height)
|
|
if self.t is None:
|
|
SDL_DestroyRenderer(r)
|
|
SDL_DestroyWindow(w)
|
|
raise Exception("Failed to create texture")
|
|
|
|
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, b'0')
|
|
SDL_SetRenderTarget(self.r, self.t)
|
|
|
|
self.clear(self.WHITE)
|
|
self.update()
|
|
|
|
return self
|
|
|
|
def __exit__(self, *args):
|
|
"""
|
|
Exit context: close and destroy window.
|
|
"""
|
|
|
|
if self.t is not None:
|
|
SDL_SetRenderTarget(self.r, None)
|
|
SDL_DestroyTexture(self.t)
|
|
if self.r is not None:
|
|
SDL_DestroyRenderer(self.r)
|
|
if self.w is not None:
|
|
SDL_DestroyWindow(self.w)
|
|
|
|
SDL_Quit()
|
|
|
|
def clear(self, color):
|
|
"""Clear screen in a uniform color."""
|
|
|
|
SDL_SetRenderDrawColor(self.r, *color)
|
|
SDL_RenderClear(self.r)
|
|
|
|
def line(self, x1, y1, x2, y2, color):
|
|
"""Draw a straight line."""
|
|
|
|
SDL_SetRenderDrawColor(self.r, *color)
|
|
|
|
# Bresenham line drawing algorithm
|
|
sgn = lambda x: -1 if x < 0 else (1 if x > 0 else 0)
|
|
|
|
x, y = x1, y1
|
|
dx, dy = x2 - x1, y2 - y1
|
|
sx, sy = sgn(dx), sgn(dy)
|
|
dx, dy = abs(dx), abs(dy)
|
|
cumul = 0
|
|
|
|
SDL_RenderDrawPoint(self.r, x, y)
|
|
|
|
if dx >= dy:
|
|
cumul = dx // 2
|
|
for i in range(dx):
|
|
x += sx
|
|
cumul += dy
|
|
if cumul > dx:
|
|
cumul -= dx
|
|
y += sy
|
|
SDL_RenderDrawPoint(self.r, x, y)
|
|
else:
|
|
cumul = dy // 2
|
|
for i in range(dy):
|
|
y += sy
|
|
cumul += dx
|
|
if cumul > dy:
|
|
cumul -= dy
|
|
x += sx
|
|
SDL_RenderDrawPoint(self.r, x, y)
|
|
|
|
def wait(self):
|
|
"""Wait for the window to be closed."""
|
|
|
|
if self.quiet:
|
|
return
|
|
|
|
event = SDL_Event()
|
|
while 1:
|
|
SDL_WaitEvent(event)
|
|
|
|
if event.type == SDL_QUIT:
|
|
break
|
|
|
|
def save(self, out):
|
|
"""Save the output into a BMP file."""
|
|
SDL_SetRenderTarget(self.r, self.t)
|
|
|
|
surface = SDL_CreateRGBSurface(0, self.width, self.height, 32,0,0,0,0)
|
|
fmt = surface.contents.format.contents.format
|
|
pixels = surface.contents.pixels
|
|
pitch = surface.contents.pitch
|
|
|
|
SDL_RenderReadPixels(self.r, None, fmt, pixels, pitch);
|
|
SDL_SaveBMP(surface, out.encode())
|
|
SDL_FreeSurface(surface)
|
|
|
|
def update(self):
|
|
"""Push window contents on-screen."""
|
|
|
|
# Target the window with scaling
|
|
SDL_SetRenderTarget(self.r, None)
|
|
SDL_RenderSetScale(self.r, self.scale, self.scale)
|
|
|
|
# Push that to the screen
|
|
SDL_RenderCopy(self.r, self.t, None, None)
|
|
SDL_RenderPresent(self.r)
|
|
|
|
# Go back to the texture
|
|
SDL_SetRenderTarget(self.r, self.t)
|
|
SDL_RenderSetScale(self.r, 1, 1)
|