Created
July 16, 2022 22:37
-
-
Save p4bl0-/f5ed1e60fdc5e76a2d321bc8708a7817 to your computer and use it in GitHub Desktop.
Actual example of PLY use
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
### Pablo Rauzy | |
### Compsci bachelor / 1st semester / fundamental compsci | |
### Lab session 1 : Seven-Segment Display | |
import sys | |
import ply.lex as lex | |
import ply.yacc as yacc | |
import pygame | |
### | |
### Lexer | |
### | |
tokens = ('A', 'B', 'C', 'D', | |
'SEGMENT', 'EQ', 'SC', | |
'AND', 'OR', 'NOT', | |
'OPAR', 'CPAR') | |
def build_lexer (): | |
t_A = r'a' | |
t_B = r'b' | |
t_C = r'c' | |
t_D = r'd' | |
t_EQ = r'=' | |
t_SC = r';' | |
t_AND = r'AND' | |
t_OR = r'OR' | |
t_NOT = r'NOT' | |
t_OPAR = r'\(' | |
t_CPAR = r'\)' | |
def t_SEGMENT (t): | |
r's[0-6]' | |
t.value = int(t.value[1:]) | |
return t | |
t_ignore = ' \t' | |
def t_newline (t): | |
r'\n+' | |
t.lexer.lineno += len(t.value) | |
def t_error (t): | |
print(f'Illegal character "{t.value[0]}" on line {t.lexer.lineno}') | |
sys.exit(1) | |
return lex.lex() | |
### | |
### Parser | |
### | |
def build_parser (): | |
def p_definitions (p): | |
'definitions : segment definitions' | |
p[0] = dict([(p[1][0], p[1][1])] + list(p[2].items())) | |
def p_definitions_end (p): | |
'definitions :' | |
p[0] = {} | |
def p_def_segment (p): | |
'segment : SEGMENT EQ expr SC' | |
p[0] = (p[1], p[3]) | |
def p_expr_and (p): | |
'expr : expr AND expr' | |
p[0] = ('and', [p[1], p[3]]) | |
def p_expr_or (p): | |
'expr : expr OR expr' | |
p[0] = ('or', [p[1], p[3]]) | |
def p_expr_not (p): | |
'expr : NOT expr' | |
p[0] = ('not', [p[2]]) | |
def p_expr_par (p): | |
'expr : OPAR expr CPAR' | |
p[0] = p[2] | |
def p_expr_A (p): | |
'expr : A' | |
p[0] = ('var', ['a']) | |
def p_expr_B (p): | |
'expr : B' | |
p[0] = ('var', ['b']) | |
def p_expr_C (p): | |
'expr : C' | |
p[0] = ('var', ['c']) | |
def p_expr_D (p): | |
'expr : D' | |
p[0] = ('var', ['d']) | |
def p_error (p): | |
print('Syntax error in input!') | |
precedence = ( | |
('left', 'OR'), | |
('left', 'AND'), | |
('right', 'NOT'), | |
) | |
lexer = build_lexer() | |
return yacc.yacc(debug=False, write_tables=False) | |
### | |
### Expression evaluator | |
### | |
def build_evaluator (circuit): | |
evaluators = { | |
'var': lambda a, env: env[a[0]], | |
'not': lambda a, env: not ev(a[0], env), | |
'or' : lambda a, env: ev(a[0], env) or ev(a[1], env), | |
'and': lambda a, env: ev(a[0], env) and ev(a[1], env) | |
} | |
def ev (expr, env): | |
return evaluators[expr[0]](expr[1], env) | |
def ev_circuit (inputs): | |
return ev(circuit, { | |
'a': inputs[0], | |
'b': inputs[1], | |
'c': inputs[2], | |
'd': inputs[3] | |
}) | |
return ev_circuit | |
### | |
### Seven-segment display | |
### | |
class SSD: | |
def __init__ (self, circuits): | |
self.surface = pygame.Surface((250, 440), pygame.SRCALPHA, 32) | |
self.circuits = [build_evaluator(circuits[i]) for i in range(7)] | |
self.s = [False for i in range(7)] | |
self.color_bg = pygame.Color(30, 30, 30) | |
self.color_on = pygame.Color(150, 190, 60) | |
self.color_off = pygame.Color(50, 50, 50) | |
self.begpos = [ (40, 20), (220, 40), (220, 240), (200, 420), (20, 400), (20, 200), (40, 220) ] | |
self.endpos = [ (200, 20), (220, 200), (220, 400), (40, 420), (20, 240), (20, 40), (200, 220) ] | |
self.numpos = [ (115, 10), (215, 105), (215, 305), (115, 410), (15, 305), (15, 105), (115, 210) ] | |
self.color_num = pygame.Color(255, 255, 255) | |
self.font_num = pygame.font.Font(None, 30) | |
def update_state (self, inputs): | |
self.s = [self.circuits[i](inputs) for i in range(7)] | |
def update_display (self): | |
self.surface.fill(self.color_bg) | |
for i in range(7): | |
color = self.color_on if self.s[i] else self.color_off | |
pygame.draw.line(self.surface, color, self.begpos[i], self.endpos[i], 30) | |
self.surface.blit(self.font_num.render(str(i), True, self.color_num, color), self.numpos[i]) | |
return self.surface | |
### | |
### Counter | |
### | |
def binary_counter (size, up_to): | |
n = 0 | |
while True: | |
yield [((n >> i) & 1) == 1 for i in range(size - 1, -1, -1)] | |
n = (n + 1) % up_to | |
### | |
### main | |
### | |
if __name__ == '__main__': | |
if len(sys.argv) != 2: | |
print('Usage: python3 seven_segment_display.py <circuits.txt>') | |
sys.exit(1) | |
# read circuits | |
src = open(sys.argv[1], 'r') | |
parser = build_parser() | |
circuits = parser.parse(src.read()) | |
src.close() | |
# check that all circuits are present | |
for s in range(7): | |
if s not in circuits: | |
print(f'Missing circuit for segment {s}!') | |
sys.exit(1) | |
# counter | |
up_to_10_and_loop = binary_counter(4, 10) | |
# init Pygame | |
pygame.init() | |
screen = pygame.display.set_mode((240, 500), pygame.DOUBLEBUF) | |
pygame.display.set_caption('Seven Segment Display') | |
pygame.event.set_allowed(None) | |
pygame.event.set_allowed([pygame.QUIT, pygame.MOUSEBUTTONDOWN]) | |
pygame.event.clear() | |
font = pygame.font.Font(None, 50) | |
bg_color = pygame.Color(30, 30, 30) | |
text_color = pygame.Color(150, 190, 60) | |
# create our Seven-Segment Display | |
ssd = SSD(circuits) | |
screen.blit(ssd.update_display(), (0, 0)) | |
pygame.display.flip() | |
done = False | |
while not done: | |
event = pygame.event.wait() | |
if event.type == pygame.QUIT: | |
done = True | |
continue | |
elif event.type == pygame.MOUSEBUTTONDOWN: | |
state = next(up_to_10_and_loop) | |
ssd.update_state(state) | |
screen.blit(ssd.update_display(), (0, 0)) | |
bits = ''.join(['1' if b else '0' for b in state]) | |
n = font.render(f'{bits} ({int(bits, 2)})', | |
True, text_color, bg_color) | |
screen.blit(n, (50, 450)) | |
pygame.display.flip() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment