Skip to content

Instantly share code, notes, and snippets.

@p4bl0-
Created July 16, 2022 22:37
Show Gist options
  • Save p4bl0-/f5ed1e60fdc5e76a2d321bc8708a7817 to your computer and use it in GitHub Desktop.
Save p4bl0-/f5ed1e60fdc5e76a2d321bc8708a7817 to your computer and use it in GitHub Desktop.
Actual example of PLY use
### 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