Implementação de uma Linguagem de Domínio Específico (DSL), chamada PaintCode, utilizando a ferramenta voltada para o parse de qualquer gramática livre de contexto chamada Lark.
- Autores: David P. Vilaça, Lucas Gomes Flegler, Tadeu P. M. Junior
- Linguagem de programação: Python (versão 3.6.5)
- Ambiente de desenvolvimento: Visual Studio Code (versão 1.33.1)
Lark é um analisador de gramática livre de contexto. Segundo sua documentação, ele pode analisar qualquer gramática que você lançar nele, não importa o quão complicado ou ambíguo, e fazê-lo de forma eficiente.
Turtle é um módulo Python que oferece funcionalidades para fazermos desenhos na tela, com comandos muito simples. Esse módulo segue a idéia da linguagem de programação Logo, que é muito utilizada em escolas como apoio ao ensino de disciplinas regulares e também para introdução a programação para crianças. A linguagem Logo segue a ideia de um robô que o usuário pode controlar através de comandos simples de movimentação.
Este trabalho consiste na implementação de uma Linguagem de Domínio Específico para trabalhar com o módulo Turtle. O PaintCode permite o desenvolvedor criar desenhos através de códigos.
O código fonte está estruturado da seguinte maneira:
src
|_ main.py
|_ paint_code.py
|_ paint_code.sh
|_ grammar
|_ grammar.lark
|_ testes
|_ example-circle.pc
|_ example-frac.pc
|_ example-frac2.pc
|_ example-if.pc
|_ example-square.pc
|_ example_star.pc
O arquivo grammar.lark contém a gramática escrita no formato EBNF, posteriomente interpretada pela biblioteca Lark
.
start: instruction
instruction: action -> action
|assign -> assign
|assign_function -> assign_function
|loop -> loop
|if -> if
action: movement -> movement
|custom_color -> custom_color
|custom_background -> custom_background
|clear -> clear
|reset -> reset
|fill -> fill
|call_function -> call_function
|speed -> speed
speed: "speed" NUMBER
movement: "move" (DIRECTION (NUMBER | variable))+
fill: BEGINFILL | ENDFILL
custom_color: "color" (COLOR | rgb)
custom_background: "bg" (COLOR | rgb)
rgb: "rgb" (("0".."9")~3 | variable) " " (("0".."9")~3 | variable) " " (("0".."9")~3 | variable)
clear: "clear"
reset: "reset"
assign: "var" NAME "=" NUMBER
assign_function: "def" NAME "{" instruction (";" instruction)* "}"
variable: NAME
call_function: NAME "(" ")"
loop: "repeat" (NUMBER | variable) code_block
code_block: "{" action (";" action)* "}"
if: "if" (NUMBER|variable) CONDITION (NUMBER|variable) "{" instruction (";" instruction)* "}"
COLOR: "red" | "green" | "blue" | "white" | "black"
DIRECTION: "f"|"b"|"l"|"r"
NUMBER: ("0".."9")+
NAME: ("a".."z")+
BEGINFILL: "begin-fill"
ENDFILL: "end-fill"
CONDITION: ">=" | "<=" | "!=" | "==" | ">" | "<"
%import common.WS_INLINE
%ignore WS_INLINE
Esta gramática se encontra dentro do diretório grammar
com o nome grammar.lark
. A extensão de arquivo .lark
é unica da biblioteca, somente arquivos com esta extensão são analisadas e interpretadas.
É um módulo que contém uma classe única chamada paint_code
, com a responsabilidade de manipular as instruções de desenho e .
A seguir apresentarei algumas explicações sobre a funcionalidade da biblioteca.
Responsável por analisar e interpretar a gramática livre de contexto desenvolvida. Mostrado no trecho de código abaixo:
self._grammar = Lark(open('grammar/grammar.lark'), start='expr')
Após a análise feita pela função "lark", a função "parse" retorna uma árvore de análise completa. Veja o trecho abaixo:
def parser(self, expression):
grammar_parse = self._grammar
try:
parse_tree = grammar_parse.parse(expression)
print("expressao aceita: ", parse_tree)
except:
print("expressao invalida")
É o módulo principal do programa, que tem como objetivo lê a expressão digitada ou um arquivo com a extensão .pc
e posteriomente envia para o arquivo paint_code.py
. Segue o código abaixo:
from paint_code import PaintCode
import argparse
try:
input = raw_input
except NameError:
pass
def rpl():
while True:
# ignorando espacos
expr = input('insira a expressao -> ')
if len(expr) == 0:
print("Favor inserir uma expressao")
else:
PaintCode().parser(expr)
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--file", help="input file")
args = parser.parse_args()
paintCode = PaintCode()
if args.file:
file = open(args.file, 'r')
for line in file:
paintCode.parser(line.strip())
input('\nPress any key to exit\n')
else:
rpl()
return 0
if __name__ == '__main__':
main()
Para execução do projeto é recomendado utilizar uma máquina virtual do python, mas não é obrigatório para o funcionamento da linguagem.
Dentro do diretório “/src” existe um script básico para execução do programa. O comando abaixo mostra como executar.
source ./paint_code.sh
Caso o script falhe, os comandos a seguir mostram como executar o projeto.
- Insira o comando abaixo para criar uma máquina virtual do python
virtualenv venv --python=python3
- Para ativar a maquina insira o comando:
source venv/bin/activate
- Insira o comando abaixo para instalar a biblioteca do lark-parser
pip install lark-parser
O código do PaintCode pode ser executado de duas maneiras diferentes, executando o arquivo main.py manualmente(esta opção o programador fica livre para escrever seu código) ou informando um arquivo com a extensão do PaintCode “arquivo.pc”.
Insira o comando abaixo dentro do diretório /src.
python main.py
- Após executar o comando acima a seguinte mensagem aparcerá no console:
insira a expressão -> |
OBS: Para desativar o virtual env:
$ deactivate
Todos os arquivos com extensão “.pc” se encontram no diretório “testes/”. Para executar estes arquivos, insira o comando abaixo:
python main.py --file testes/example-frac2.pc
Todo o código fonte está hospedado no GitHub.
https://lark-parser.readthedocs.io/en/latest/
https://github.com/lark-parser/lark/blob/master/README.md