This commit is contained in:
Kovid Goyal 2021-03-30 18:22:24 +05:30
commit b9a64085e9
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
3 changed files with 229 additions and 132 deletions

View File

@ -397,9 +397,6 @@ class TemplateDialog(QDialog, Ui_TemplateDialog):
self.buttonBox.button(QDialogButtonBox.StandardButton.Ok).setText(_('&OK')) self.buttonBox.button(QDialogButtonBox.StandardButton.Ok).setText(_('&OK'))
self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setText(_('&Cancel')) self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setText(_('&Cancel'))
self.textbox.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
self.textbox.customContextMenuRequested.connect(self.show_context_menu)
self.color_copy_button.clicked.connect(self.color_to_clipboard) self.color_copy_button.clicked.connect(self.color_to_clipboard)
self.filename_button.clicked.connect(self.filename_button_clicked) self.filename_button.clicked.connect(self.filename_button_clicked)
self.icon_copy_button.clicked.connect(self.icon_to_clipboard) self.icon_copy_button.clicked.connect(self.icon_to_clipboard)
@ -443,6 +440,9 @@ class TemplateDialog(QDialog, Ui_TemplateDialog):
self.set_up_font_boxes() self.set_up_font_boxes()
self.toggle_button.clicked.connect(self.toggle_button_pressed) self.toggle_button.clicked.connect(self.toggle_button_pressed)
self.remove_all_button.clicked.connect(self.remove_all_button_pressed) self.remove_all_button.clicked.connect(self.remove_all_button_pressed)
self.load_button.clicked.connect(self.load_template)
self.save_button.clicked.connect(self.save_template)
# Now geometry # Now geometry
try: try:
geom = gprefs.get('template_editor_dialog_geometry', None) geom = gprefs.get('template_editor_dialog_geometry', None)
@ -451,15 +451,6 @@ class TemplateDialog(QDialog, Ui_TemplateDialog):
except Exception: except Exception:
pass pass
def show_context_menu(self, point):
m = self.textbox.createStandardContextMenu()
m.addSeparator()
ca = m.addAction(_('Load template from file'))
ca.triggered.connect(self.load_template)
ca = m.addAction(_('Save template to file'))
ca.triggered.connect(self.store_template)
m.exec_(self.textbox.mapToGlobal(point))
def load_template(self): def load_template(self):
filename = choose_files(self, 'template_dialog_save_templates', filename = choose_files(self, 'template_dialog_save_templates',
_('Load template from file'), _('Load template from file'),
@ -470,7 +461,7 @@ class TemplateDialog(QDialog, Ui_TemplateDialog):
with open(filename[0], 'r') as f: with open(filename[0], 'r') as f:
self.textbox.setPlainText(f.read()) self.textbox.setPlainText(f.read())
def store_template(self): def save_template(self):
filename = choose_save_file(self, 'template_dialog_save_templates', filename = choose_save_file(self, 'template_dialog_save_templates',
_('Save template to file'), _('Save template to file'),
filters=[ filters=[

View File

@ -330,6 +330,12 @@ you the value as well as all the local variables&lt;/p&gt;</string>
<property name="toolTip"> <property name="toolTip">
<string>The template program text</string> <string>The template program text</string>
</property> </property>
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
</widget> </widget>
</item> </item>
<item row="3" column="0"> <item row="3" column="0">
@ -523,7 +529,7 @@ you the value as well as all the local variables&lt;/p&gt;</string>
<item> <item>
<widget class="QLabel" name="font_name_label"> <widget class="QLabel" name="font_name_label">
<property name="text"> <property name="text">
<string>Current font:</string> <string>Font:</string>
</property> </property>
<property name="buddy"> <property name="buddy">
<cstring>font_box</cstring> <cstring>font_box</cstring>
@ -557,6 +563,41 @@ you the value as well as all the local variables&lt;/p&gt;</string>
</layout> </layout>
</item> </item>
<item row="24" column="3"> <item row="24" column="3">
<layout class="QHBoxLayout">
<item>
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::VLine</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<property name="lineWidth">
<number>3</number>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="load_button">
<property name="text">
<string>Lo&amp;ad</string>
</property>
<property name="toolTip">
<string>Load the template from a file</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="save_button">
<property name="text">
<string>&amp;Save</string>
</property>
<property name="toolTip">
<string>Save the template in a file</string>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox"> <widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation"> <property name="orientation">
<enum>Qt::Horizontal</enum> <enum>Qt::Horizontal</enum>
@ -566,6 +607,8 @@ you the value as well as all the local variables&lt;/p&gt;</string>
</property> </property>
</widget> </widget>
</item> </item>
</layout>
</item>
<item row="25" column="0" colspan="4"> <item row="25" column="0" colspan="4">
<widget class="QFrame"> <widget class="QFrame">
<property name="frameShape"> <property name="frameShape">

View File

@ -41,19 +41,23 @@ class Node(object):
NODE_BINARY_ARITHOP = 19 NODE_BINARY_ARITHOP = 19
NODE_UNARY_ARITHOP = 20 NODE_UNARY_ARITHOP = 20
NODE_PRINT = 21 NODE_PRINT = 21
NODE_LINE_NUMBER = 22
def __init__(self, line_number, name): def __init__(self, line_number, name):
self.line_number = line_number self.my_line_number = line_number
self.my_node_name = name self.my_node_name = name
@property
def node_name(self): def node_name(self):
return self.my_node_name return self.my_node_name
@property
def line_number(self):
return self.my_line_number
class IfNode(Node): class IfNode(Node):
def __init__(self, line_number, condition, then_part, else_part): def __init__(self, line_number, condition, then_part, else_part):
Node.__init__(self, line_number, 'IF') Node.__init__(self, line_number, 'if ...')
self.node_type = self.NODE_IF self.node_type = self.NODE_IF
self.condition = condition self.condition = condition
self.then_part = then_part self.then_part = then_part
@ -62,7 +66,7 @@ class IfNode(Node):
class ForNode(Node): class ForNode(Node):
def __init__(self, line_number, variable, list_field_expr, separator, block): def __init__(self, line_number, variable, list_field_expr, separator, block):
Node.__init__(self, line_number, 'FOR') Node.__init__(self, line_number, 'for ...:')
self.node_type = self.NODE_FOR self.node_type = self.NODE_FOR
self.variable = variable self.variable = variable
self.list_field_expr = list_field_expr self.list_field_expr = list_field_expr
@ -72,7 +76,7 @@ class ForNode(Node):
class AssignNode(Node): class AssignNode(Node):
def __init__(self, line_number, left, right): def __init__(self, line_number, left, right):
Node.__init__(self, line_number, 'ASSIGN') Node.__init__(self, line_number, 'assign to ' + left)
self.node_type = self.NODE_ASSIGN self.node_type = self.NODE_ASSIGN
self.left = left self.left = left
self.right = right self.right = right
@ -80,7 +84,7 @@ class AssignNode(Node):
class FunctionNode(Node): class FunctionNode(Node):
def __init__(self, line_number, function_name, expression_list): def __init__(self, line_number, function_name, expression_list):
Node.__init__(self, line_number, 'FUNCTION CALL') Node.__init__(self, line_number, function_name + '()')
self.node_type = self.NODE_FUNC self.node_type = self.NODE_FUNC
self.name = function_name self.name = function_name
self.expression_list = expression_list self.expression_list = expression_list
@ -88,7 +92,7 @@ class FunctionNode(Node):
class CallNode(Node): class CallNode(Node):
def __init__(self, line_number, function, expression_list): def __init__(self, line_number, function, expression_list):
Node.__init__(self, line_number, 'TEMPLATE CALL') Node.__init__(self, line_number, 'call template: ' + function)
self.node_type = self.NODE_CALL self.node_type = self.NODE_CALL
self.function = function self.function = function
self.expression_list = expression_list self.expression_list = expression_list
@ -96,28 +100,28 @@ class CallNode(Node):
class ArgumentsNode(Node): class ArgumentsNode(Node):
def __init__(self, line_number, expression_list): def __init__(self, line_number, expression_list):
Node.__init__(self, line_number, 'ARGUMENTS') Node.__init__(self, line_number, 'arguments()')
self.node_type = self.NODE_ARGUMENTS self.node_type = self.NODE_ARGUMENTS
self.expression_list = expression_list self.expression_list = expression_list
class GlobalsNode(Node): class GlobalsNode(Node):
def __init__(self, line_number, expression_list): def __init__(self, line_number, expression_list):
Node.__init__(self, line_number, 'GLOBALS') Node.__init__(self, line_number, 'globals()')
self.node_type = self.NODE_GLOBALS self.node_type = self.NODE_GLOBALS
self.expression_list = expression_list self.expression_list = expression_list
class SetGlobalsNode(Node): class SetGlobalsNode(Node):
def __init__(self, line_number, expression_list): def __init__(self, line_number, expression_list):
Node.__init__(self, line_number, 'SET_GLOBALS') Node.__init__(self, line_number, 'set_globals()')
self.node_type = self.NODE_SET_GLOBALS self.node_type = self.NODE_SET_GLOBALS
self.expression_list = expression_list self.expression_list = expression_list
class StringCompareNode(Node): class StringCompareNode(Node):
def __init__(self, line_number, operator, left, right): def __init__(self, line_number, operator, left, right):
Node.__init__(self, line_number, 'COMPARE STRING') Node.__init__(self, line_number, 'comparision: ' + operator)
self.node_type = self.NODE_COMPARE_STRING self.node_type = self.NODE_COMPARE_STRING
self.operator = operator self.operator = operator
self.left = left self.left = left
@ -126,7 +130,7 @@ class StringCompareNode(Node):
class NumericCompareNode(Node): class NumericCompareNode(Node):
def __init__(self, line_number, operator, left, right): def __init__(self, line_number, operator, left, right):
Node.__init__(self, line_number, 'COMPARE NUMBERS') Node.__init__(self, line_number, 'comparison: ' + operator)
self.node_type = self.NODE_COMPARE_NUMERIC self.node_type = self.NODE_COMPARE_NUMERIC
self.operator = operator self.operator = operator
self.left = left self.left = left
@ -135,7 +139,7 @@ class NumericCompareNode(Node):
class LogopBinaryNode(Node): class LogopBinaryNode(Node):
def __init__(self, line_number, operator, left, right): def __init__(self, line_number, operator, left, right):
Node.__init__(self, line_number, 'BINARY LOGICAL OP') Node.__init__(self, line_number, 'binary operator: ' + operator)
self.node_type = self.NODE_BINARY_LOGOP self.node_type = self.NODE_BINARY_LOGOP
self.operator = operator self.operator = operator
self.left = left self.left = left
@ -144,7 +148,7 @@ class LogopBinaryNode(Node):
class LogopUnaryNode(Node): class LogopUnaryNode(Node):
def __init__(self, line_number, operator, expr): def __init__(self, line_number, operator, expr):
Node.__init__(self, line_number, 'UNARY LOGICAL OP') Node.__init__(self, line_number, 'unary operator: ' + operator)
self.node_type = self.NODE_UNARY_LOGOP self.node_type = self.NODE_UNARY_LOGOP
self.operator = operator self.operator = operator
self.expr = expr self.expr = expr
@ -152,7 +156,7 @@ class LogopUnaryNode(Node):
class NumericBinaryNode(Node): class NumericBinaryNode(Node):
def __init__(self, line_number, operator, left, right): def __init__(self, line_number, operator, left, right):
Node.__init__(self, line_number, 'BINARY ARITHMETIC OP') Node.__init__(self, line_number, 'binary operator: ' + operator)
self.node_type = self.NODE_BINARY_ARITHOP self.node_type = self.NODE_BINARY_ARITHOP
self.operator = operator self.operator = operator
self.left = left self.left = left
@ -161,7 +165,7 @@ class NumericBinaryNode(Node):
class NumericUnaryNode(Node): class NumericUnaryNode(Node):
def __init__(self, line_number, operator, expr): def __init__(self, line_number, operator, expr):
Node.__init__(self, line_number, 'UNARY ARITHMETIC OP') Node.__init__(self, line_number, 'unary operator: '+ operator)
self.node_type = self.NODE_UNARY_ARITHOP self.node_type = self.NODE_UNARY_ARITHOP
self.operator = operator self.operator = operator
self.expr = expr self.expr = expr
@ -169,28 +173,28 @@ class NumericUnaryNode(Node):
class ConstantNode(Node): class ConstantNode(Node):
def __init__(self, line_number, value): def __init__(self, line_number, value):
Node.__init__(self, line_number, 'CONSTANT') Node.__init__(self, line_number, 'constant: ' + value)
self.node_type = self.NODE_CONSTANT self.node_type = self.NODE_CONSTANT
self.value = value self.value = value
class VariableNode(Node): class VariableNode(Node):
def __init__(self, line_number, name): def __init__(self, line_number, name):
Node.__init__(self, line_number, 'VARIABLE') Node.__init__(self, line_number, 'variable: ' + name)
self.node_type = self.NODE_RVALUE self.node_type = self.NODE_RVALUE
self.name = name self.name = name
class FieldNode(Node): class FieldNode(Node):
def __init__(self, line_number, expression): def __init__(self, line_number, expression):
Node.__init__(self, line_number, 'FIELD FUNCTION') Node.__init__(self, line_number, 'field()')
self.node_type = self.NODE_FIELD self.node_type = self.NODE_FIELD
self.expression = expression self.expression = expression
class RawFieldNode(Node): class RawFieldNode(Node):
def __init__(self, line_number, expression, default=None): def __init__(self, line_number, expression, default=None):
Node.__init__(self, line_number, 'RAW_FIELD FUNCTION') Node.__init__(self, line_number, 'raw_field()')
self.node_type = self.NODE_RAW_FIELD self.node_type = self.NODE_RAW_FIELD
self.expression = expression self.expression = expression
self.default = default self.default = default
@ -198,14 +202,14 @@ class RawFieldNode(Node):
class FirstNonEmptyNode(Node): class FirstNonEmptyNode(Node):
def __init__(self, line_number, expression_list): def __init__(self, line_number, expression_list):
Node.__init__(self, line_number, 'FIRST_NON_EMPTY FUNCTION') Node.__init__(self, line_number, 'first_non_empty()')
self.node_type = self.NODE_FIRST_NON_EMPTY self.node_type = self.NODE_FIRST_NON_EMPTY
self.expression_list = expression_list self.expression_list = expression_list
class ContainsNode(Node): class ContainsNode(Node):
def __init__(self, line_number, arguments): def __init__(self, line_number, arguments):
Node.__init__(self, line_number, 'CONTAINS FUNCTION') Node.__init__(self, line_number, 'contains()')
self.node_type = self.NODE_CONTAINS self.node_type = self.NODE_CONTAINS
self.value_expression = arguments[0] self.value_expression = arguments[0]
self.test_expression = arguments[1] self.test_expression = arguments[1]
@ -215,17 +219,11 @@ class ContainsNode(Node):
class PrintNode(Node): class PrintNode(Node):
def __init__(self, line_number, arguments): def __init__(self, line_number, arguments):
Node.__init__(self, line_number, 'PRINT') Node.__init__(self, line_number, 'print')
self.node_type = self.NODE_PRINT self.node_type = self.NODE_PRINT
self.arguments = arguments self.arguments = arguments
class LineNumberNode(Node):
def __init__(self, line_number):
Node.__init__(self, line_number, 'LINE NUMBER')
self.node_type = self.NODE_LINE_NUMBER
class _Parser(object): class _Parser(object):
LEX_OP = 1 LEX_OP = 1
LEX_ID = 2 LEX_ID = 2
@ -513,7 +511,6 @@ class _Parser(object):
while True: while True:
while self.token_is_newline(): while self.token_is_newline():
self.line_number += 1 self.line_number += 1
expr_list.append(LineNumberNode(self.line_number))
self.consume() self.consume()
if self.token_is_eof(): if self.token_is_eof():
break break
@ -529,7 +526,7 @@ class _Parser(object):
line_number = self.line_number line_number = self.line_number
condition = self.top_expr() condition = self.top_expr()
if not self.token_is_then(): if not self.token_is_then():
self.error(_("Missing 'then' in if statement")) self.error(_("Expected '%s' in '%s' statement")%('then', 'if'))
self.consume() self.consume()
then_part = self.expression_list() then_part = self.expression_list()
if self.token_is_elif(): if self.token_is_elif():
@ -540,17 +537,18 @@ class _Parser(object):
else: else:
else_part = None else_part = None
if not self.token_is_fi(): if not self.token_is_fi():
self.error(_("Missing 'fi' in if statement")) self.error(_("Expected '%s' in '%s' statement")%('fi', 'if'))
self.consume() self.consume()
return IfNode(line_number, condition, then_part, else_part) return IfNode(line_number, condition, then_part, else_part)
def for_expression(self): def for_expression(self):
line_number = self.line_number
self.consume() self.consume()
if not self.token_is_id(): if not self.token_is_id():
self.error(_("Missing identifier in for statement")) self.error(_("Expected identifier in '%s' statement")%'for')
variable = self.token() variable = self.token()
if not self.token_is_in(): if not self.token_is_in():
self.error(_("Missing 'in' in for statement")) self.error(_("Expected '%s' in '%s' statement")%('in', 'for'))
self.consume() self.consume()
list_expr = self.top_expr() list_expr = self.top_expr()
if self.token_is_separator(): if self.token_is_separator():
@ -559,13 +557,13 @@ class _Parser(object):
else: else:
separator = None separator = None
if not self.token_op_is_colon(): if not self.token_op_is_colon():
self.error(_("Missing colon (':') in for statement")) self.error(_("Expected colon in '%s' statement")%'for')
self.consume() self.consume()
block = self.expression_list() block = self.expression_list()
if not self.token_is_rof(): if not self.token_is_rof():
self.error(_("Missing 'rof' in for statement")) self.error(_("Expected '%s' in '%s' statement")%('rof', 'for'))
self.consume() self.consume()
return ForNode(self.line_number, variable, list_expr, separator, block) return ForNode(line_number, variable, list_expr, separator, block)
def top_expr(self): def top_expr(self):
return self.or_expr() return self.or_expr()
@ -644,7 +642,7 @@ class _Parser(object):
self.consume() self.consume()
rv = self.expression_list() rv = self.expression_list()
if not self.token_op_is_rparen(): if not self.token_op_is_rparen():
self.error(_('Missing )')) self.error(_("Expected a right parenthesis"))
self.consume() self.consume()
return rv return rv
if self.token_is_if(): if self.token_is_if():
@ -652,19 +650,20 @@ class _Parser(object):
if self.token_is_for(): if self.token_is_for():
return self.for_expression() return self.for_expression()
if self.token_is_id(): if self.token_is_id():
line_number = self.line_number
id_ = self.token() id_ = self.token()
# We have an identifier. Check if it is a field reference # We have an identifier. Check if it is a field reference
if len(id_) > 1 and id_[0] == '$': if len(id_) > 1 and id_[0] == '$':
if id_[1] == '$': if id_[1] == '$':
return RawFieldNode(self.line_number, ConstantNode(self.line_number, id_[2:])) return RawFieldNode(line_number, ConstantNode(self.line_number, id_[2:]))
return FieldNode(self.line_number, ConstantNode(self.line_number, id_[1:])) return FieldNode(line_number, ConstantNode(self.line_number, id_[1:]))
# Determine if it is a function # Determine if it is a function
if not self.token_op_is_lparen(): if not self.token_op_is_lparen():
if self.token_op_is_equals(): if self.token_op_is_equals():
# classic assignment statement # classic assignment statement
self.consume() self.consume()
return AssignNode(self.line_number, id_, self.top_expr()) return AssignNode(line_number, id_, self.top_expr())
return VariableNode(self.line_number, id_) return VariableNode(line_number, id_)
# We have a function. # We have a function.
# Check if it is a known one. We do this here so error reporting is # Check if it is a known one. We do this here so error reporting is
@ -682,17 +681,17 @@ class _Parser(object):
break break
self.consume() self.consume()
if self.token() != ')': if self.token() != ')':
self.error(_('Missing closing parenthesis')) self.error(_("Expected a closing right parenthesis for function call"))
if id_ == 'field' and len(arguments) == 1: if id_ == 'field' and len(arguments) == 1:
return FieldNode(self.line_number, arguments[0]) return FieldNode(line_number, arguments[0])
if id_ == 'raw_field' and (len(arguments) in (1, 2)): if id_ == 'raw_field' and (len(arguments) in (1, 2)):
return RawFieldNode(self.line_number, *arguments) return RawFieldNode(line_number, *arguments)
if id_ == 'test' and len(arguments) == 3: if id_ == 'test' and len(arguments) == 3:
return IfNode(self.line_number, arguments[0], (arguments[1],), (arguments[2],)) return IfNode(line_number, arguments[0], (arguments[1],), (arguments[2],))
if id_ == 'first_non_empty' and len(arguments) > 0: if id_ == 'first_non_empty' and len(arguments) > 0:
return FirstNonEmptyNode(self.line_number, arguments) return FirstNonEmptyNode(line_number, arguments)
if (id_ == 'assign' and len(arguments) == 2 and arguments[0].node_type == Node.NODE_RVALUE): if (id_ == 'assign' and len(arguments) == 2 and arguments[0].node_type == Node.NODE_RVALUE):
return AssignNode(self.line_number, arguments[0].name, arguments[1]) return AssignNode(line_number, arguments[0].name, arguments[1])
if id_ == 'arguments' or id_ == 'globals' or id_ == 'set_globals': if id_ == 'arguments' or id_ == 'globals' or id_ == 'set_globals':
new_args = [] new_args = []
for arg_list in arguments: for arg_list in arguments:
@ -701,23 +700,23 @@ class _Parser(object):
self.error(_("Parameters to '{}' must be " self.error(_("Parameters to '{}' must be "
"variables or assignments").format(id_)) "variables or assignments").format(id_))
if arg.node_type == Node.NODE_RVALUE: if arg.node_type == Node.NODE_RVALUE:
arg = AssignNode(self.line_number, arg.name, ConstantNode(self.line_number, '')) arg = AssignNode(line_number, arg.name, ConstantNode(self.line_number, ''))
new_args.append(arg) new_args.append(arg)
if id_ == 'arguments': if id_ == 'arguments':
return ArgumentsNode(self.line_number, new_args) return ArgumentsNode(line_number, new_args)
if id_ == 'set_globals': if id_ == 'set_globals':
return SetGlobalsNode(self.line_number, new_args) return SetGlobalsNode(line_number, new_args)
return GlobalsNode(self.line_number, new_args) return GlobalsNode(line_number, new_args)
if id_ == 'contains' and len(arguments) == 4: if id_ == 'contains' and len(arguments) == 4:
return ContainsNode(self.line_number, arguments) return ContainsNode(line_number, arguments)
if id_ == 'print': if id_ == 'print':
return PrintNode(self.line_number, arguments) return PrintNode(line_number, arguments)
if id_ in self.func_names and not self.funcs[id_].is_python: if id_ in self.func_names and not self.funcs[id_].is_python:
return self.call_expression(id_, arguments) return self.call_expression(id_, arguments)
cls = self.funcs[id_] cls = self.funcs[id_]
if cls.arg_count != -1 and len(arguments) != cls.arg_count: if cls.arg_count != -1 and len(arguments) != cls.arg_count:
self.error(_('Incorrect number of arguments for function {0}').format(id_)) self.error(_('Incorrect number of arguments for function {0}').format(id_))
return FunctionNode(self.line_number, id_, arguments) return FunctionNode(line_number, id_, arguments)
elif self.token_is_constant(): elif self.token_is_constant():
# String or number # String or number
return ConstantNode(self.line_number, self.token()) return ConstantNode(self.line_number, self.token())
@ -726,8 +725,8 @@ class _Parser(object):
class _Interpreter(object): class _Interpreter(object):
def error(self, message): def error(self, message, line_number):
m = _('Interpreter: {0} - line number {1}').format(message, self.line_number) m = _('Interpreter: {0} - line number {1}').format(message, line_number)
raise ValueError(m) raise ValueError(m)
def program(self, funcs, parent, prog, val, is_call=False, args=None, def program(self, funcs, parent, prog, val, is_call=False, args=None,
@ -735,7 +734,6 @@ class _Interpreter(object):
self.parent = parent self.parent = parent
self.parent_kwargs = parent.kwargs self.parent_kwargs = parent.kwargs
self.parent_book = parent.book self.parent_book = parent.book
self.line_number = 1
self.funcs = funcs self.funcs = funcs
self.locals = {'$':val} self.locals = {'$':val}
self.global_vars = global_vars if isinstance(global_vars, dict) else {} self.global_vars = global_vars if isinstance(global_vars, dict) else {}
@ -746,21 +744,16 @@ class _Interpreter(object):
self.break_reporter = None self.break_reporter = None
if is_call: if is_call:
return self.do_node_call(CallNode(self.line_number, prog, None), args=args) return self.do_node_call(CallNode(1, prog, None), args=args)
return self.expression_list(prog) return self.expression_list(prog)
def call_break_reporter(self, txt, val, line_number=None): def call_break_reporter(self, txt, val, line_number):
self.real_break_reporter(txt, val, self.locals, self.real_break_reporter(txt, val, self.locals, line_number)
line_number if line_number else self.line_number)
def expression_list(self, prog): def expression_list(self, prog):
val = '' val = ''
for p in prog: for p in prog:
val = self.expr(p) val = self.expr(p)
if (self.break_reporter and
p.node_type != Node.NODE_LINE_NUMBER and
p.node_type != Node.NODE_IF):
self.break_reporter(p.node_name(), val)
return val return val
INFIX_STRING_COMPARE_OPS = { INFIX_STRING_COMPARE_OPS = {
@ -777,9 +770,14 @@ class _Interpreter(object):
try: try:
left = self.expr(prog.left) left = self.expr(prog.left)
right = self.expr(prog.right) right = self.expr(prog.right)
return ('1' if self.INFIX_STRING_COMPARE_OPS[prog.operator](left, right) else '') res = '1' if self.INFIX_STRING_COMPARE_OPS[prog.operator](left, right) else ''
if (self.break_reporter):
self.break_reporter(prog.node_name, res, prog.line_number)
return res
except: except:
self.error(_('Error during string comparison. Operator {0}').format(prog.operator)) self.error(
_('Error during string comparison. Operator {0}').format(prog.operator),
prog.line_number)
INFIX_NUMERIC_COMPARE_OPS = { INFIX_NUMERIC_COMPARE_OPS = {
"==#": lambda x, y: x == y, "==#": lambda x, y: x == y,
@ -799,32 +797,40 @@ class _Interpreter(object):
try: try:
left = self.float_deal_with_none(self.expr(prog.left)) left = self.float_deal_with_none(self.expr(prog.left))
right = self.float_deal_with_none(self.expr(prog.right)) right = self.float_deal_with_none(self.expr(prog.right))
return '1' if self.INFIX_NUMERIC_COMPARE_OPS[prog.operator](left, right) else '' res = '1' if self.INFIX_NUMERIC_COMPARE_OPS[prog.operator](left, right) else ''
if (self.break_reporter):
self.break_reporter(prog.node_name, res, prog.line_number)
return res
except: except:
self.error(_('Value used in comparison is not a number. Operator {0}').format(prog.operator)) self.error(
_('Value used in comparison is not a number. Operator {0}').format(prog.operator),
prog.line_number)
def do_node_if(self, prog): def do_node_if(self, prog):
line_number = prog.line_number line_number = prog.line_number
test_part = self.expr(prog.condition) test_part = self.expr(prog.condition)
if self.break_reporter: if self.break_reporter:
self.break_reporter('if: condition', test_part, line_number=line_number) self.break_reporter("'if': condition value", test_part, line_number)
if test_part: if test_part:
v = self.expression_list(prog.then_part) v = self.expression_list(prog.then_part)
if self.break_reporter: if self.break_reporter:
self.break_reporter('if: then part', v, line_number=line_number) self.break_reporter("'if': then-block value", v, line_number)
return v return v
elif prog.else_part: elif prog.else_part:
v = self.expression_list(prog.else_part) v = self.expression_list(prog.else_part)
if self.break_reporter: if self.break_reporter:
self.break_reporter('if: else part', v, line_number=line_number) self.break_reporter("'if': else-block value", v, line_number)
return v return v
return '' return ''
def do_node_rvalue(self, prog): def do_node_rvalue(self, prog):
try: try:
if (self.break_reporter):
self.break_reporter(prog.node_name, self.locals[prog.name], prog.line_number)
return self.locals[prog.name] return self.locals[prog.name]
except: except:
self.error(_('Unknown identifier {0}').format(prog.name)) self.error(_('Unknown identifier {0}').format(prog.name),
prog.line_number)
def do_node_func(self, prog): def do_node_func(self, prog):
args = list() args = list()
@ -834,8 +840,11 @@ class _Interpreter(object):
# Evaluate the function. # Evaluate the function.
id_ = prog.name.strip() id_ = prog.name.strip()
cls = self.funcs[id_] cls = self.funcs[id_]
return cls.eval_(self.parent, self.parent_kwargs, res = cls.eval_(self.parent, self.parent_kwargs,
self.parent_book, self.locals, *args) self.parent_book, self.locals, *args)
if (self.break_reporter):
self.break_reporter(prog.node_name, res, prog.line_number)
return res
def do_node_call(self, prog, args=None): def do_node_call(self, prog, args=None):
if args is None: if args is None:
@ -849,53 +858,78 @@ class _Interpreter(object):
self.locals['*arg_'+ str(dex)] = v self.locals['*arg_'+ str(dex)] = v
val = self.expression_list(prog.function) val = self.expression_list(prog.function)
self.locals = saved_locals self.locals = saved_locals
if (self.break_reporter):
self.break_reporter(prog.node_name, val, prog.line_number)
return val return val
def do_node_arguments(self, prog): def do_node_arguments(self, prog):
for dex, arg in enumerate(prog.expression_list): for dex, arg in enumerate(prog.expression_list):
self.locals[arg.left] = self.locals.get('*arg_'+ str(dex), self.expr(arg.right)) self.locals[arg.left] = self.locals.get('*arg_'+ str(dex), self.expr(arg.right))
if (self.break_reporter):
self.break_reporter(prog.node_name, '', prog.line_number)
return '' return ''
def do_node_globals(self, prog): def do_node_globals(self, prog):
res = '' res = ''
for arg in prog.expression_list: for arg in prog.expression_list:
res = self.locals[arg.left] = self.global_vars.get(arg.left, self.expr(arg.right)) res = self.locals[arg.left] = self.global_vars.get(arg.left, self.expr(arg.right))
if (self.break_reporter):
self.break_reporter(prog.node_name, res, prog.line_number)
return res return res
def do_node_set_globals(self, prog): def do_node_set_globals(self, prog):
res = '' res = ''
for arg in prog.expression_list: for arg in prog.expression_list:
res = self.global_vars[arg.left] = self.locals.get(arg.left, self.expr(arg.right)) res = self.global_vars[arg.left] = self.locals.get(arg.left, self.expr(arg.right))
if (self.break_reporter):
self.break_reporter(prog.node_name, res, prog.line_number)
return res return res
def do_node_constant(self, prog): def do_node_constant(self, prog):
if (self.break_reporter):
self.break_reporter(prog.node_name, prog.value, prog.line_number)
return prog.value return prog.value
def do_node_field(self, prog): def do_node_field(self, prog):
try: try:
name = self.expr(prog.expression) name = self.expr(prog.expression)
try: try:
return self.parent.get_value(name, [], self.parent_kwargs) res = self.parent.get_value(name, [], self.parent_kwargs)
if (self.break_reporter):
self.break_reporter(prog.node_name, res, prog.line_number)
return res
except: except:
self.error(_('Unknown field {0}').format(name)) self.error(_('Unknown field {0}').format(name),
prog.line_number)
except ValueError as e: except ValueError as e:
raise e raise e
except: except:
self.error(_('Unknown field {0}').format('internal parse error')) self.error(_('Unknown field {0}').format('internal parse error'),
prog.line_number)
def do_node_raw_field(self, prog): def do_node_raw_field(self, prog):
try: try:
name = self.expr(prog.expression) name = self.expr(prog.expression)
res = getattr(self.parent_book, name, None) res = getattr(self.parent_book, name, None)
if res is None and prog.default is not None: if res is None and prog.default is not None:
return self.expr(prog.default) res = self.expr(prog.default)
if (self.break_reporter):
self.break_reporter(prog.node_name, res, prog.line_number)
return res
if res is not None: if res is not None:
if isinstance(res, list): if isinstance(res, list):
fm = self.parent_book.metadata_for_field(name) fm = self.parent_book.metadata_for_field(name)
if fm is None: if fm is None:
return ', '.join(res) res = ', '.join(res)
return fm['is_multiple']['list_to_ui'].join(res) else:
return unicode_type(res) res = fm['is_multiple']['list_to_ui'].join(res)
else:
res = unicode_type(res)
else:
res = unicode_type(res) # Should be the string "None"
if (self.break_reporter):
self.break_reporter(prog.node_name, res, prog.line_number)
return res
except ValueError as e: except ValueError as e:
raise e raise e
except: except:
@ -904,15 +938,22 @@ class _Interpreter(object):
def do_node_assign(self, prog): def do_node_assign(self, prog):
t = self.expr(prog.right) t = self.expr(prog.right)
self.locals[prog.left] = t self.locals[prog.left] = t
if (self.break_reporter):
self.break_reporter(prog.node_name, t, prog.line_number)
return t return t
def do_node_first_non_empty(self, prog): def do_node_first_non_empty(self, prog):
for expr in prog.expression_list: for expr in prog.expression_list:
if v := self.expr(expr): if v := self.expr(expr):
if (self.break_reporter):
self.break_reporter(prog.node_name, v, prog.line_number)
return v return v
if (self.break_reporter):
self.break_reporter(prog.node_name, '', prog.line_number)
return '' return ''
def do_node_for(self, prog): def do_node_for(self, prog):
line_number = prog.line_number
try: try:
separator = ',' if prog.separator is None else self.expr(prog.separator) separator = ',' if prog.separator is None else self.expr(prog.separator)
v = prog.variable v = prog.variable
@ -923,26 +964,33 @@ class _Interpreter(object):
res = [r.strip() for r in res.split(separator) if r.strip()] res = [r.strip() for r in res.split(separator) if r.strip()]
ret = '' ret = ''
if self.break_reporter: if self.break_reporter:
self.break_reporter(_("'for' value list"), separator.join(res)) self.break_reporter("'for' list value", separator.join(res), line_number)
for x in res: for x in res:
self.locals[v] = x self.locals[v] = x
ret = self.expression_list(prog.block) ret = self.expression_list(prog.block)
return ret if (self.break_reporter):
self.break_reporter("'for' block value", ret, line_number)
elif self.break_reporter: elif self.break_reporter:
self.break_reporter(_("'for' value list"), '') # Shouldn't get here
self.break_reporter("'for' list value", '', line_number)
self.error(_('The field {0} is not a list').format(f)) ret = ''
return ret
except ValueError as e: except ValueError as e:
raise e raise e
except Exception as e: except Exception as e:
self.error(_('Unhandled exception {0}').format(e)) self.error(_('Unhandled exception {0}').format(e), line_number)
def do_node_contains(self, prog): def do_node_contains(self, prog):
v = self.expr(prog.value_expression) v = self.expr(prog.value_expression)
t = self.expr(prog.test_expression) t = self.expr(prog.test_expression)
if re.search(t, v, flags=re.I): if re.search(t, v, flags=re.I):
return self.expr(prog.match_expression) res = self.expr(prog.match_expression)
return self.expr(prog.not_match_expression) else:
res = self.expr(prog.not_match_expression)
if (self.break_reporter):
self.break_reporter(prog.node_name, res, prog.line_number)
return res
LOGICAL_BINARY_OPS = { LOGICAL_BINARY_OPS = {
'and': lambda self, x, y: self.expr(x) and self.expr(y), 'and': lambda self, x, y: self.expr(x) and self.expr(y),
@ -951,9 +999,14 @@ class _Interpreter(object):
def do_node_logop(self, prog): def do_node_logop(self, prog):
try: try:
return ('1' if self.LOGICAL_BINARY_OPS[prog.operator](self, prog.left, prog.right) else '') res = ('1' if self.LOGICAL_BINARY_OPS[prog.operator](self, prog.left, prog.right) else '')
if (self.break_reporter):
self.break_reporter(prog.node_name, res, prog.line_number)
return res
except: except:
self.error(_('Error during operator evaluation. Operator {0}').format(prog.operator)) self.error(
_('Error during operator evaluation. Operator {0}').format(prog.operator),
prog.line_number)
LOGICAL_UNARY_OPS = { LOGICAL_UNARY_OPS = {
'not': lambda x: not x, 'not': lambda x: not x,
@ -962,9 +1015,14 @@ class _Interpreter(object):
def do_node_logop_unary(self, prog): def do_node_logop_unary(self, prog):
try: try:
expr = self.expr(prog.expr) expr = self.expr(prog.expr)
return ('1' if self.LOGICAL_UNARY_OPS[prog.operator](expr) else '') res = ('1' if self.LOGICAL_UNARY_OPS[prog.operator](expr) else '')
if (self.break_reporter):
self.break_reporter(prog.node_name, res, prog.line_number)
return res
except: except:
self.error(_('Error during operator evaluation. Operator {0}').format(prog.operator)) self.error(
_('Error during operator evaluation. Operator {0}').format(prog.operator),
prog.line_number)
ARITHMETIC_BINARY_OPS = { ARITHMETIC_BINARY_OPS = {
'+': lambda x, y: x + y, '+': lambda x, y: x + y,
@ -977,9 +1035,14 @@ class _Interpreter(object):
try: try:
answer = self.ARITHMETIC_BINARY_OPS[prog.operator](float(self.expr(prog.left)), answer = self.ARITHMETIC_BINARY_OPS[prog.operator](float(self.expr(prog.left)),
float(self.expr(prog.right))) float(self.expr(prog.right)))
return unicode_type(answer if modf(answer)[0] != 0 else int(answer)) res = unicode_type(answer if modf(answer)[0] != 0 else int(answer))
if (self.break_reporter):
self.break_reporter(prog.node_name, res, prog.line_number)
return res
except: except:
self.error(_('Error during arithmetic operator evaluation. Operator {0}').format(prog.operator)) self.error(
_('Error during operator evaluation. Operator {0}').format(prog.operator),
prog.line_number)
ARITHMETIC_UNARY_OPS = { ARITHMETIC_UNARY_OPS = {
'+': lambda x: x, '+': lambda x: x,
@ -989,9 +1052,14 @@ class _Interpreter(object):
def do_node_unary_arithop(self, prog): def do_node_unary_arithop(self, prog):
try: try:
expr = self.ARITHMETIC_UNARY_OPS[prog.operator](float(self.expr(prog.expr))) expr = self.ARITHMETIC_UNARY_OPS[prog.operator](float(self.expr(prog.expr)))
return unicode_type(expr if modf(expr)[0] != 0 else int(expr)) res = unicode_type(expr if modf(expr)[0] != 0 else int(expr))
if (self.break_reporter):
self.break_reporter(prog.node_name, res, prog.line_number)
return res
except: except:
self.error(_('Error during arithmetic operator evaluation. Operator {0}').format(prog.operator)) self.error(
_('Error during operator evaluation. Operator {0}').format(prog.operator),
prog.line_number)
def do_node_print(self, prog): def do_node_print(self, prog):
res = [] res = []
@ -1000,10 +1068,6 @@ class _Interpreter(object):
print(res) print(res)
return res[0] if res else '' return res[0] if res else ''
def do_node_line_number(self, prog):
self.line_number = prog.line_number
return ''
NODE_OPS = { NODE_OPS = {
Node.NODE_IF: do_node_if, Node.NODE_IF: do_node_if,
Node.NODE_ASSIGN: do_node_assign, Node.NODE_ASSIGN: do_node_assign,
@ -1026,7 +1090,6 @@ class _Interpreter(object):
Node.NODE_BINARY_ARITHOP: do_node_binary_arithop, Node.NODE_BINARY_ARITHOP: do_node_binary_arithop,
Node.NODE_UNARY_ARITHOP: do_node_unary_arithop, Node.NODE_UNARY_ARITHOP: do_node_unary_arithop,
Node.NODE_PRINT: do_node_print, Node.NODE_PRINT: do_node_print,
Node.NODE_LINE_NUMBER: do_node_line_number,
} }
def expr(self, prog): def expr(self, prog):
@ -1039,7 +1102,7 @@ class _Interpreter(object):
except: except:
if (DEBUG): if (DEBUG):
traceback.print_exc() traceback.print_exc()
self.error(_('Internal error evaluating an expression')) self.error(_('Internal error evaluating an expression'), prog.line_number)
class TemplateFormatter(string.Formatter): class TemplateFormatter(string.Formatter):