Commit d0c288e3 authored by Maxime Perrotin's avatar Maxime Perrotin
Browse files

improve syntax checker

parent 5a9fe09e
...@@ -117,6 +117,10 @@ The background pattern was downloaded from www.subtlepatterns.com ...@@ -117,6 +117,10 @@ The background pattern was downloaded from www.subtlepatterns.com
Changelog Changelog
========= =========
3.1.1 (07/2020)
- Reinforce syntax error checking and reporting
Don't allow user escape a symbol syntax error: refocus text until fixed
3.1.0 (06/2020) 3.1.0 (06/2020)
- Add support for mkstring operator to transform an element into an array - Add support for mkstring operator to transform an element into an array
mkstring (a, b, c) is in principle equivalent to ASN.1 Value Notation {a, b, c} mkstring (a, b, c) is in principle equivalent to ASN.1 Value Notation {a, b, c}
......
...@@ -359,7 +359,7 @@ class EditableText(QGraphicsTextItem): ...@@ -359,7 +359,7 @@ class EditableText(QGraphicsTextItem):
if self.force_focus: if self.force_focus:
# when user double-clicks on the Completer, it may be out of # when user double-clicks on the Completer, it may be out of
# the editable text. It is not right to leave the focus in that # the editable text. It is not right to leave the focus in that
# case, as this would generate a synatx check while in fact # case, as this would generate a syntax check while in fact
# user is not done editing text # user is not done editing text
self.setFocus() self.setFocus()
self.force_focus = False self.force_focus = False
...@@ -377,8 +377,8 @@ class EditableText(QGraphicsTextItem): ...@@ -377,8 +377,8 @@ class EditableText(QGraphicsTextItem):
text_cursor.clearSelection() text_cursor.clearSelection()
self.setTextCursor(text_cursor) self.setTextCursor(text_cursor)
# If something has changed, check syntax and create undo command # If something has changed, check syntax and create undo command
if(self.oldSize != self.parent.boundingRect() or if(self.oldSize != self.parent.boundingRect()
self.oldText != str(self)): or self.parent.syntax_error or self.oldText != str(self)):
# Call syntax checker from item containing the text (if any) # Call syntax checker from item containing the text (if any)
self.scene().check_syntax(self.parent) self.scene().check_syntax(self.parent)
# Update class completion list # Update class completion list
......
...@@ -169,6 +169,9 @@ class Symbol(QObject, QGraphicsPathItem): ...@@ -169,6 +169,9 @@ class Symbol(QObject, QGraphicsPathItem):
self.old_rect = self.boundingRect() self.old_rect = self.boundingRect()
# List of visible connection points (that can move) # List of visible connection points (that can move)
self.movable_points = [] self.movable_points = []
# Flag to indicate a detected syntax error, used to force the
# refocus of the text area to make sure user fixes it before saving
self.syntax_error: Boolean = False
def set_valid_pos(self, pos): def set_valid_pos(self, pos):
''' Hook that can be redefined by sub classes to forbid wrong ''' Hook that can be redefined by sub classes to forbid wrong
...@@ -325,8 +328,14 @@ class Symbol(QObject, QGraphicsPathItem): ...@@ -325,8 +328,14 @@ class Symbol(QObject, QGraphicsPathItem):
''' '''
pass pass
def check_syntax(self, text): def check_syntax(self, text, check_last_semi=True):
''' Check the syntax of the text inside the symbol (if any) ''' ''' Check the syntax of the text inside the symbol (if any) '''
# if check_last_semi=True, will raise an error if the text ends
# with a semicolon (inserted by the user). This is the case for
# all symbols except the text boxes. A semi colon there would
# prevent a COMMENT, and would only be deteted when parsing the
# full model - without precise identication of the location.
# By doing it here we can spot the issue immediately
try: try:
_, syntax_errors, _, _, _ = self.parser.parseSingleElement( _, syntax_errors, _, _, _ = self.parser.parseSingleElement(
self.common_name, text) self.common_name, text)
...@@ -335,6 +344,10 @@ class Symbol(QObject, QGraphicsPathItem): ...@@ -335,6 +344,10 @@ class Symbol(QObject, QGraphicsPathItem):
LOG.debug(str(traceback.format_exc())) LOG.debug(str(traceback.format_exc()))
LOG.error('Checker failed - no parser for this construct?') LOG.error('Checker failed - no parser for this construct?')
else: else:
if check_last_semi:
if str(self).strip()[-1] == ';':
syntax_errors.append("Remove the semi-colon at the end"
" of the text.")
return syntax_errors return syntax_errors
def paint(self, painter, _, ___): def paint(self, painter, _, ___):
......
...@@ -140,7 +140,7 @@ except ImportError: ...@@ -140,7 +140,7 @@ except ImportError:
__all__ = ['opengeode', 'SDL_Scene', 'SDL_View', 'parse'] __all__ = ['opengeode', 'SDL_Scene', 'SDL_View', 'parse']
__version__ = '3.1.0' __version__ = '3.1.1'
if hasattr(sys, 'frozen'): if hasattr(sys, 'frozen'):
# Detect if we are running on Windows (py2exe-generated) # Detect if we are running on Windows (py2exe-generated)
...@@ -788,6 +788,7 @@ class SDL_Scene(QGraphicsScene): ...@@ -788,6 +788,7 @@ class SDL_Scene(QGraphicsScene):
errors = self.syntax_errors(symbol) errors = self.syntax_errors(symbol)
if not errors: if not errors:
symbol.syntax_error = False
return return
for view in self.views(): for view in self.views():
...@@ -814,6 +815,9 @@ class SDL_Scene(QGraphicsScene): ...@@ -814,6 +815,9 @@ class SDL_Scene(QGraphicsScene):
msg_box.setStandardButtons(QMessageBox.Discard) msg_box.setStandardButtons(QMessageBox.Discard)
msg_box.setDefaultButton(QMessageBox.Discard) msg_box.setDefaultButton(QMessageBox.Discard)
msg_box.exec_() msg_box.exec_()
# There were syntax errors: force user to fix them
symbol.syntax_error = True
symbol.edit_text()
def global_syntax_check(self, ignore=set()): def global_syntax_check(self, ignore=set()):
......
...@@ -864,6 +864,14 @@ class TextSymbol(HorizontalSymbol): ...@@ -864,6 +864,14 @@ class TextSymbol(HorizontalSymbol):
self.textbox_alignment = Qt.AlignLeft | Qt.AlignTop self.textbox_alignment = Qt.AlignLeft | Qt.AlignTop
self.parser = ogParser self.parser = ogParser
def check_syntax(self, pr_text):
''' Redefinition of the check syntax function for the text symbol '''
# Standard behaviour except that we permit the last character to be
# a semi-colon, since that is always the case with declarations
# and the text box cannot be followed by a COMMENT symbol
return super().check_syntax(pr_text, check_last_semi=False);
def update_completion_list(self, pr_text): def update_completion_list(self, pr_text):
''' When text was entered, update list of variables/FPAR/Timers ''' ''' When text was entered, update list of variables/FPAR/Timers '''
# note, on standalone systems, if the textbox contains a # note, on standalone systems, if the textbox contains a
......
...@@ -31,26 +31,30 @@ system og; ...@@ -31,26 +31,30 @@ system og;
/* CIF ENDTEXT */ /* CIF ENDTEXT */
/* CIF START (639, 63), (70, 35) */ /* CIF START (639, 63), (70, 35) */
START; START;
/* CIF task (581, 113), (185, 35) */ /* CIF PROCEDURECALL (624, 118), (98, 35) */
call writeln(a(0));
/* CIF task (581, 168), (185, 35) */
task result := result // 'Hello!'; task result := result // 'Hello!';
/* CIF task (626, 163), (96, 35) */ /* CIF task (623, 219), (102, 35) */
task result := ''; task result := '';
/* CIF task (445, 213), (457, 78) */ /* CIF task (459, 269), (429, 78) */
task for each in a(2, length(a) - 1): task for each in a(2, length(a) - 1):
result := result // (if length(result) > 0 then sep else nosep fi) // each result := result // (if length(result) > 0 then sep else nosep fi) // each
endfor; endfor
/* CIF PROCEDURECALL (609, 306), (129, 35) */ /* CIF comment (907, 290), (70, 35) */
comment 'pop ';
/* CIF PROCEDURECALL (609, 362), (129, 35) */
call writeln(result); call writeln(result);
/* CIF task (596, 356), (155, 53) */ /* CIF task (596, 417), (155, 56) */
task for sep2 in a: task for sep2 in a:
call writeln(sep2); call writeln(sep2);
endfor endfor
/* CIF comment (771, 365), (194, 38) */ /* CIF comment (771, 427), (194, 40) */
comment 'Test to check scope comment 'Test to check scope
(sep is also a dcl-variable)'; (sep is also a dcl-variable)';
/* CIF PROCEDURECALL (509, 424), (330, 35) */ /* CIF PROCEDURECALL (509, 491), (330, 35) */
call writeln('String literal (should be "hello"):', strlit); call writeln('String literal (should be "hello"):', strlit);
/* CIF NEXTSTATE (639, 474), (70, 35) */ /* CIF NEXTSTATE (639, 541), (70, 35) */
NEXTSTATE wait; NEXTSTATE wait;
/* CIF state (366, 217), (70, 35) */ /* CIF state (366, 217), (70, 35) */
state wait; state wait;
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment