Commit 58a3f8f5 authored by Maxime Perrotin's avatar Maxime Perrotin
Browse files

Refactored PR-generation

parent 3a6a9beb
......@@ -23,6 +23,7 @@ import ogAST
import sdlSymbols
import genericSymbols
import Renderer
import Pr
__all__ = ['copy', 'paste']
......@@ -63,7 +64,9 @@ def copy(selection):
def copy_branch(top_level_item):
''' Copy branches (recursively) '''
res_terminators = []
item_ast, terminators = top_level_item.get_ast()
pr_text = '\n'.join(Pr.generate(top_level_item,
nextstate=False, recursive=True))
item_ast, terminators = top_level_item.get_ast(pr_text)
LOG.debug('COPY ' + str(item_ast))
# Set absolute (scene) coordinates of top level item
......
......@@ -16,13 +16,49 @@
import logging
from collections import deque
from itertools import chain
from singledispatch import singledispatch
import genericSymbols, sdlSymbols
LOG = logging.getLogger(__name__)
__all__ = ['generate']
__all__ = ['parse_scene', 'generate']
class Indent(deque):
''' Extension of the deque class to support automatic indenting '''
indent = 0
def append(self, string):
''' Redefinition of the append to insert the indent pattern '''
super(Indent, self).append(' ' * Indent.indent + string)
def parse_scene(scene):
''' Return the PR string for a complete scene) '''
pr_data = deque()
for each in scene.processes:
pr_data.extend(generate(each))
for each in chain(scene.texts, scene.procs, scene.start):
pr_data.extend(generate(each))
for each in scene.floating_labels:
pr_data.extend(generate(each))
composite = set(scene.composite_states.keys())
for each in scene.states:
if each.is_composite():
try:
composite.remove(unicode(each).lower())
sub_state = generate(each, composite=True, nextstate=False)
if sub_state:
sub_state.reverse()
pr_data.extendleft(sub_state)
except KeyError:
pass
pr_data.extend(generate(each, nextstate=False))
return list(pr_data)
def cif_coord(name, symbol):
......@@ -37,13 +73,14 @@ def cif_coord(name, symbol):
def hyperlink(symbol):
''' PR string for the optional hyperlink associated to a symbol '''
return u"/* CIF Keep Specific Geode HyperLink '{}' */".format(
symbol.hyperlink)
symbol.text.hyperlink)
def common(name, symbol):
''' PR string format that is shared by most symbols '''
result = [cif_coord(name, symbol)]
if symbol.hyperlink:
result = Indent()
result.append(cif_coord(name, symbol))
if symbol.text.hyperlink:
result.append(hyperlink(symbol))
result.append(u'{} {}{}'.format(name, unicode(symbol.text), ';'
if not symbol.comment else ''))
......@@ -54,35 +91,38 @@ def common(name, symbol):
def recursive_aligned(symbol):
''' Get the branch following symbol '''
result = []
result = Indent()
Indent.indent += 1
next_symbol = symbol.next_aligned_symbol()
while next_symbol:
result.extend(generate(next_symbol))
next_symbol = next_symbol.next_aligned_symbol()
Indent.indent -= 1
return result
@singledispatch
def generate(symbol, recursive=True):
def generate(symbol, *args, **kwargs):
''' Generate text for a symbol, recursively or not - return a list of
strings '''
_, _ = symbol, recursive
raise NotImplementedError('[PR Generator] Unsupported AST construct')
return []
return Indent()
@generate.register(genericSymbols.Comment)
def _comment(symbol):
def _comment(symbol, **kwargs):
''' Optional comment linked to a symbol '''
result = [cif_coord('COMMENT', symbol)]
if symbol.hyperlink:
result = Indent()
result.append(cif_coord('COMMENT', symbol))
if symbol.text.hyperlink:
result.append(hyperlink(symbol))
result.append('COMMENT \'{}\';'.format(unicode(symbol.text)))
result.append(u'COMMENT \'{}\';'.format(unicode(symbol.text)))
return result
@generate.register(sdlSymbols.Input)
def _input(symbol, recursive=True):
def _input(symbol, recursive=True, **kwargs):
''' Input symbol or branch if recursive is set '''
result = common('INPUT', symbol)
if recursive:
......@@ -91,7 +131,7 @@ def _input(symbol, recursive=True):
@generate.register(sdlSymbols.Connect)
def _connect(symbol, recursive=True):
def _connect(symbol, recursive=True, **kwargs):
''' Connect symbol or branch if recursive is set '''
result = common('CONNECT', symbol)
if recursive:
......@@ -100,17 +140,18 @@ def _connect(symbol, recursive=True):
@generate.register(sdlSymbols.Output)
def _output(symbol):
def _output(symbol, **kwargs):
''' Output symbol '''
return common('OUTPUT', symbol)
@generate.register(sdlSymbols.Decision)
def _decision(symbol, recursive=True):
def _decision(symbol, recursive=True, **kwargs):
''' Decision symbol or branch if recursive is set '''
result = common('DECISION', symbol)
if recursive:
else_branch = None
Indent.indent += 1
for answer in symbol.branches():
if unicode(answer).lower().strip() == 'else':
else_branch = generate(answer)
......@@ -118,89 +159,171 @@ def _decision(symbol, recursive=True):
result.extend(generate(answer))
if else_branch:
result.extend(else_branch)
result.append('ENDDECISION;')
Indent.indent -= 1
result.append(u'ENDDECISION;')
return result
@generate.register(sdlSymbols.DecisionAnswer)
def _decisionanswer(symbol, recursive=True):
def _decisionanswer(symbol, recursive=True, **kwargs):
''' Decision Answer symbol or branch if recursive is set '''
result = Indent()
Indent.indent += 1
result.append(cif_coord('ANSWER', symbol))
ans = unicode(symbol)
if ans.lower().strip() != u'else':
ans = u'({})'.format(ans)
result = [cif_coord('ANSWER', symbol)]
if symbol.hyperlink:
if symbol.text.hyperlink:
result.append(hyperlink(symbol))
result.append('{}:'.format(ans))
result.append(u'{}:'.format(ans))
if recursive:
result.extend(recursive_aligned(symbol))
Indent.indent -= 1
return result
@generate.register(sdlSymbols.Join)
def _join(symbol):
def _join(symbol, **kwargs):
''' Join symbol '''
return common('JOIN', symbol)
@generate.register(sdlSymbols.ProcedureStop)
def _procedurestop(symbol):
def _procedurestop(symbol, **kwargs):
''' Procedure Stop symbol '''
return common('RETURN', symbol)
@generate.register(sdlSymbols.Label)
def _label(symbol, recursive=True):
''' Label symbol or branch if recursive is set '''
_, _ = symbol, recursive
@generate.register(sdlSymbols.Task)
def _task(symbol):
def _task(symbol, **kwargs):
''' Task symbol '''
return common('TASK', symbol)
@generate.register(sdlSymbols.ProcedureCall)
def _procedurecall(symbol):
def _procedurecall(symbol, **kwargs):
''' Procedure call symbol '''
result = [cif_coord('PROCEDURECALL', symbol)]
if symbol.hyperlink:
result = Indent()
result.append(cif_coord('PROCEDURECALL', symbol))
if symbol.text.hyperlink:
result.append(hyperlink(symbol))
result.append(u'CALL {}{}'.format(unicode(symbol.text), ';'
if not symbol.comment else ''))
if symbol.comment:
result.extend(generate(symbol.comment))
return result
@generate.register(sdlSymbols.TextSymbol)
def _textsymbol(symbol):
def _textsymbol(symbol, **kwargs):
''' Text Area symbol '''
result = [cif_coord('TEXT', symbol)]
if symbol.hyperlink:
result = Indent()
result.append(cif_coord('TEXT', symbol))
if symbol.text.hyperlink:
result.append(hyperlink(symbol))
result.append(unicode(symbol.text))
result.append('/* CIF ENDTEXT */')
result.append(u'/* CIF ENDTEXT */')
return result
@generate.register(sdlSymbols.Label)
def _label(symbol, recursive=True, **kwargs):
''' Label symbol or branch if recursive is set '''
result = Indent()
result.append(cif_coord('LABEL', symbol))
if symbol.text.hyperlink:
result.append(hyperlink(symbol))
if symbol.common_name == 'floating_label':
result.append(u'CONNECTION {}:'.format(unicode(symbol)))
if recursive:
result.extend(recursive_aligned(symbol))
result.append(u'/* CIF End Label */')
result.append(u'ENDCONNECTION;')
else:
result.append(u'{}:'.format(unicode(symbol)))
return result
@generate.register(sdlSymbols.State)
def _state(symbol, recursive=True):
''' State symbol or branch if recursive is set '''
_, _ = symbol, recursive
def _state(symbol, recursive=True, nextstate=True, composite=False, **kwargs):
''' State/Nextstate symbol or branch if recursive is set '''
if nextstate:
result = common('NEXTSTATE', symbol)
elif not composite and symbol.hasParent \
and not [each for each in symbol.childSymbols()
if not isinstance(each, genericSymbols.Comment)]:
# If nextstate has no child, don't generate anything
result = []
elif not composite:
result = common('STATE', symbol)
if recursive:
Indent.indent += 1
# Generate code for INPUT and CONNECT symbols
for each in (symb for symb in symbol.childSymbols()
if isinstance(symb, sdlSymbols.Input)):
result.extend(generate(each))
Indent.indent -= 1
result.append(u'ENDSTATE;')
else:
# Generate code for a nested state
result = Indent()
result.extend(['STATE {};'.format(unicode(symbol)),
'SUBSTRUCTURE'])
Indent.indent += 1
entry_points, exit_points = [], []
for each in symbol.nested_scene.start:
if unicode(each):
entry_points.append(unicode(each))
for each in symbol.nested_scene.returns:
if unicode(each) != u'no_name':
exit_points.append(unicode(each))
if entry_points:
result.append(u'in ({});'.format(','.join(entry_points)))
if exit_points:
result.append(u'out ({});'.format(','.join(exit_points)))
Indent.indent += 1
result.extend(parse_scene(symbol.nested_scene))
Indent.indent -= 1
Indent.indent -= 1
result.append(u'ENDSUBSTRUCTURE;')
return result
@generate.register(sdlSymbols.Process)
def _process(symbol, recursive=True):
''' Process symbol or branch if recursive is set '''
_, _ = symbol, recursive
def _process(symbol, recursive=True, **kwargs):
''' Process symbol and inner content if recursive is set '''
result = common('PROCESS', symbol)
if recursive and symbol.nested_scene:
Indent.indent += 1
result.extend(parse_scene(symbol.nested_scene))
Indent.indent -= 1
result.append(u'ENDPROCESS {};'.format(unicode(symbol)))
return result
@generate.register(sdlSymbols.Procedure)
def _procedure(symbol, recursive=True):
def _procedure(symbol, recursive=True, **kwargs):
''' Procedure symbol or branch if recursive is set '''
_, _ = symbol, recursive
result = common('PROCEDURE', symbol)
if recursive and symbol.nested_scene:
Indent.indent += 1
result.extend(parse_scene(symbol.nested_scene))
Indent.indent -= 1
result.append(u'ENDPROCEDURE;'.format(unicode(symbol)))
return result
@generate.register(sdlSymbols.Start)
def _start(symbol, recursive=True):
def _start(symbol, recursive=True, **kwargs):
''' START symbol or branch if recursive is set '''
_, _ = symbol, recursive
result = Indent()
result.append(cif_coord('START', symbol))
result.append(u'START{via}{comment}'
.format(via=(' ' + unicode(symbol) + ' ')
if unicode(symbol).replace('START', '') else '',
comment=';' if not symbol.comment else ''))
if symbol.comment:
result.extend(generate(symbol.comment))
if recursive:
result.extend(recursive_aligned(symbol))
return result
......@@ -308,6 +308,9 @@ class EditableText(QGraphicsTextItem, object):
self.completion_prefix = text_cursor.selectedText()
completion_count = self.completer.set_completion_prefix(
self.completion_prefix)
if event.key() in (Qt.Key_Period, Qt.Key_Exclam):
# Enable autocompletion of complex types
pass # placeholder to update autocompletion list
if(completion_count > 0 and len(self.completion_prefix) > 1) or(
event.key() == Qt.Key_F8):
# Save the position of the cursor
......@@ -362,9 +365,9 @@ class EditableText(QGraphicsTextItem, object):
if(self.oldSize != self.parentItem().boundingRect() or
self.oldText != unicode(self)):
# Call syntax checker from item containing the text (if any)
self.parentItem().check_syntax()
self.scene().check_syntax(self.parentItem())
# Update class completion list
self.parentItem().update_completion_list()
self.scene().update_completion_list(self.parentItem())
# Create undo command, including possible CAM
with undoCommands.UndoMacro(self.scene().undo_stack, 'Text'):
undo_cmd = undoCommands.ResizeSymbol(
......@@ -397,18 +400,6 @@ class EditableText(QGraphicsTextItem, object):
self.oldSize = self.parentItem().boundingRect()
self.editing = True
def PR(self):
'''
Return the PR notation for the hyperlink
TODO: remove from here, put in SDL symbols
'''
if self.hyperlink:
return(
u"/* CIF Keep Specific Geode HyperLink '{hlink}' */\n".format(
hlink=self.hyperlink))
else:
return u''
def __str__(self):
''' Print the text inside the symbol '''
raise TypeError('Use UNICODE, not string!')
......@@ -601,14 +592,10 @@ class Symbol(QObject, QGraphicsPathItem, object):
''' Return the text inside the symbol '''
return unicode(self.text) or u'no_name'
def parse_gr(self, recursive=True):
''' Parse the graphical representation, return PR form '''
return self.PR()
def get_ast(self):
def get_ast(self, pr_text):
''' Return the symbol in the AST form, as returned by the parser '''
ast, _, ___, ____, terminators = self.parser.parseSingleElement(
self.common_name, self.PR())
ast, _, _, _, terminators = \
self.parser.parseSingleElement(self.common_name, pr_text)
return ast, terminators
def edit_text(self, pos=None):
......@@ -624,7 +611,7 @@ class Symbol(QObject, QGraphicsPathItem, object):
except AttributeError:
return
def update_completion_list(self):
def update_completion_list(self, **kwargs):
'''
Update the text autocompletion list based on the symbol text
This function is typically called when user has typed new text
......@@ -632,18 +619,15 @@ class Symbol(QObject, QGraphicsPathItem, object):
'''
pass
def check_syntax(self):
def check_syntax(self, text):
''' Check the syntax of the text inside the symbol (if any) '''
try:
_, syntax_errors, _, _, _ = (self.parser.parseSingleElement(
self.common_name, self.PR()))
_, syntax_errors, _, _, _ = self.parser.parseSingleElement(
self.common_name, text)
except (AssertionError, AttributeError):
LOG.error('Checker failed - no parser for this construct?')
else:
try:
self.scene().raise_syntax_errors(syntax_errors)
except:
print('[SYNTAX ERROR] ' + '\n'.join(syntax_errors))
return syntax_errors
def paint(self, painter, _, ___):
''' Apply anti-aliasing or not (symbol attribute) '''
......@@ -1161,17 +1145,6 @@ class Comment(Symbol):
'''
pass
def PR(self):
''' Return the text corresponding to the SDL PR notation '''
pos = self.scenePos()
return (u'\n/* CIF COMMENT ({x}, {y}), ({w}, {h}) */\n'
'{hlink}'
'COMMENT \'{comment}\';'.format(hlink=self.text.PR(),
x=int(pos.x()), y=int(pos.y()),
w=int(self.boundingRect().width()),
h=int(self.boundingRect().height()),
comment=unicode(self.text)))
class Cornergrabber(QGraphicsPolygonItem, object):
'''
......
......@@ -78,6 +78,7 @@ import Clipboard
import Statechart
import Lander
import Helper
import Pr
# Try importing graphviz for the SDL to Statechart converter
......@@ -537,6 +538,19 @@ class SDL_Scene(QtGui.QGraphicsScene, object):
msg_box.setDefaultButton(QtGui.QMessageBox.Discard)
msg_box.exec_()
def check_syntax(self, symbol):
''' Create PR representation for a symbol and check its syntax '''
pr_text = '\n'.join(Pr.generate(symbol, recursive=False))
errors = symbol.check_syntax(pr_text)
self.raise_syntax_errors(errors)
def update_completion_list(self, symbol):
''' When text has changed on a symbol, update the data dictionnary '''
pr_text = '\n'.join(Pr.generate(symbol,
recursive=False,
nextstate=False))
symbol.update_completion_list(pr_text=pr_text)
def find_text(self, pattern):
''' Return all symbols with matching text '''
for item in (symbol for symbol in self.items()
......@@ -671,31 +685,31 @@ class SDL_Scene(QtGui.QGraphicsScene, object):
self.undo_stack.endMacro()
self.refresh()
def get_pr_string(self):
''' Parse the graphical items and returns a PR string '''
pr_data = deque()
for each in self.processes:
pr_data.append(each.PR())
for item in chain(self.texts, self.procs, self.start):
pr_data.append(item.PR())
for item in self.floating_labels:
pr_data.append(item.PR_floating())
composite = set(self.composite_states.keys())
for item in self.states:
if item.is_composite():
try:
composite.remove(unicode(item).lower())
pr_data.appendleft(item.parse_composite_state())
except KeyError:
pass
pr_data.append(item.PR_state())
return list(pr_data)
# def get_pr_string(self):
# ''' Parse the graphical items and returns a PR string '''
# pr_data = deque()
# for each in self.processes:
# pr_data.append(each.PR())
#
# for item in chain(self.texts, self.procs, self.start):
# pr_data.append(item.PR())
# for item in self.floating_labels:
# pr_data.append(item.PR_floating())
# composite = set(self.composite_states.keys())
# for item in self.states:
# if item.is_composite():
# try:
# composite.remove(unicode(item).lower())
# pr_data.appendleft(item.parse_composite_state())
# except KeyError:
# pass
# pr_data.append(item.PR_state())
#
# return list(pr_data)
def sdl_to_statechart(self):
''' Create a graphviz representation of the SDL model '''
pr_raw = self.get_pr_string()
pr_raw = Pr.parse_scene(self)
pr_data = unicode('\n'.join(pr_raw))
ast, _, _ = ogParser.parse_pr(string=pr_data)
try:
......@@ -1302,7 +1316,7 @@ class SDL_View(QtGui.QGraphicsView, object):
each.translate_to_origin()
delta_x, delta_y = scene.translate_to_origin()
pr_raw = scene.get_pr_string()
pr_raw = Pr.parse_scene(scene)
# Move items back to original place to avoid scrollbar jumps
for item in scene.floating_symb:
......@@ -1412,7 +1426,7 @@ class SDL_View(QtGui.QGraphicsView, object):
return False
self.need_new_scene.emit()
self.parent_scene = []
#self.scene().undo_stack.clear()
self.scene().undo_stack.clear()
#self.scene().clear()
G_SYMBOLS.clear()
self.scene().process_name = ''
......@@ -1454,7 +1468,7 @@ class SDL_View(QtGui.QGraphicsView, object):
scene = self.parent_scene[0][0]
else:
scene = self.scene()
pr_raw = scene.get_pr_string()
pr_raw = Pr.parse_scene(scene)
pr_data = unicode('\n'.join(pr_raw))
if pr_data:
_, warnings, errors = ogParser.parse_pr(files=self.readonly_pr,
......@@ -1468,7 +1482,7 @@ class SDL_View(QtGui.QGraphicsView, object):
scene = self.parent_scene[0][0]
else:
scene = self.scene()
pr_raw = scene.get_pr_string()
pr_raw = Pr.parse_scene(scene)
pr_data = unicode('\n'.join(pr_raw))
if pr_data:
ast, warnings, errors = ogParser.parse_pr(files=self.readonly_pr,
......@@ -1775,12 +1789,12 @@ def opengeode():
try:
for module in (sdlSymbols, genericSymbols, ogAST, ogParser, Lander,
AdaGenerator, undoCommands, Renderer, Clipboard, Statechart,
Helper, LlvmGenerator, Asn1scc, Connectors):
Helper, LlvmGenerator, Asn1scc, Connectors, Pr):
module.LOG.addHandler(handler_console)
module.LOG.setLevel(level)
except NameError:
# Some modules may not be loaded (like llvm on Windows)
pass;
# Some modules may not be loaded (like llvm on Windows)
pass;
# Initialize the clipboard
Clipboard.CLIPBOARD = SDL_Scene(context='clipboard')
......
This diff is collapsed.
......@@ -3,6 +3,17 @@ TASTE-Dataview DEFINITIONS ::=
BEGIN
MyChoice ::= CHOICE {
a CHOICE {
b CHOICE {
c BOOLEAN,
d BOOLEAN
},
e BOOLEAN
},
f BOOLEAN
}