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
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)
- 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}
......
......@@ -359,7 +359,7 @@ class EditableText(QGraphicsTextItem):
if self.force_focus:
# 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
# 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
self.setFocus()
self.force_focus = False
......@@ -377,8 +377,8 @@ class EditableText(QGraphicsTextItem):
text_cursor.clearSelection()
self.setTextCursor(text_cursor)
# If something has changed, check syntax and create undo command
if(self.oldSize != self.parent.boundingRect() or
self.oldText != str(self)):
if(self.oldSize != self.parent.boundingRect()
or self.parent.syntax_error or self.oldText != str(self)):
# Call syntax checker from item containing the text (if any)
self.scene().check_syntax(self.parent)
# Update class completion list
......
......@@ -169,6 +169,9 @@ class Symbol(QObject, QGraphicsPathItem):
self.old_rect = self.boundingRect()
# List of visible connection points (that can move)
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):
''' Hook that can be redefined by sub classes to forbid wrong
......@@ -325,8 +328,14 @@ class Symbol(QObject, QGraphicsPathItem):
'''
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) '''
# 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:
_, syntax_errors, _, _, _ = self.parser.parseSingleElement(
self.common_name, text)
......@@ -335,6 +344,10 @@ class Symbol(QObject, QGraphicsPathItem):
LOG.debug(str(traceback.format_exc()))
LOG.error('Checker failed - no parser for this construct?')
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
def paint(self, painter, _, ___):
......
......@@ -140,7 +140,7 @@ except ImportError:
__all__ = ['opengeode', 'SDL_Scene', 'SDL_View', 'parse']
__version__ = '3.1.0'
__version__ = '3.1.1'
if hasattr(sys, 'frozen'):
# Detect if we are running on Windows (py2exe-generated)
......@@ -788,6 +788,7 @@ class SDL_Scene(QGraphicsScene):
errors = self.syntax_errors(symbol)
if not errors:
symbol.syntax_error = False
return
for view in self.views():
......@@ -814,6 +815,9 @@ class SDL_Scene(QGraphicsScene):
msg_box.setStandardButtons(QMessageBox.Discard)
msg_box.setDefaultButton(QMessageBox.Discard)
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()):
......
......@@ -864,6 +864,14 @@ class TextSymbol(HorizontalSymbol):
self.textbox_alignment = Qt.AlignLeft | Qt.AlignTop
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):
''' When text was entered, update list of variables/FPAR/Timers '''
# note, on standalone systems, if the textbox contains a
......
......@@ -31,26 +31,30 @@ system og;
/* CIF ENDTEXT */
/* CIF START (639, 63), (70, 35) */
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!';
/* CIF task (626, 163), (96, 35) */
/* CIF task (623, 219), (102, 35) */
task result := '';
/* CIF task (445, 213), (457, 78) */
/* CIF task (459, 269), (429, 78) */
task for each in a(2, length(a) - 1):
result := result // (if length(result) > 0 then sep else nosep fi) // each
endfor;
/* CIF PROCEDURECALL (609, 306), (129, 35) */
endfor
/* CIF comment (907, 290), (70, 35) */
comment 'pop ';
/* CIF PROCEDURECALL (609, 362), (129, 35) */
call writeln(result);
/* CIF task (596, 356), (155, 53) */
/* CIF task (596, 417), (155, 56) */
task for sep2 in a:
call writeln(sep2);
endfor
/* CIF comment (771, 365), (194, 38) */
/* CIF comment (771, 427), (194, 40) */
comment 'Test to check scope
(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);
/* CIF NEXTSTATE (639, 474), (70, 35) */
/* CIF NEXTSTATE (639, 541), (70, 35) */
NEXTSTATE wait;
/* CIF state (366, 217), (70, 35) */
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