diff options
author | Reinier Heeres <reinier@heeres.eu> | 2007-11-13 10:13:57 (GMT) |
---|---|---|
committer | Reinier Heeres <reinier@heeres.eu> | 2007-11-13 10:13:57 (GMT) |
commit | 3b0b9526b75d167981e4c2aeaa8b298de18c9583 (patch) | |
tree | 7fe78eaba745b0c6eb368b688c6d994fdcd1b656 | |
parent | 297422fff90867e20be3cc7d1165c3e4e77ff584 (diff) |
Unicode parsing + bugfixes
-rw-r--r-- | MANIFEST | 2 | ||||
-rw-r--r-- | NEWS | 5 | ||||
-rw-r--r-- | calculate.py | 60 | ||||
-rw-r--r-- | eqnparser.py | 76 | ||||
-rw-r--r-- | eqnparserhelp.py | 39 | ||||
-rw-r--r-- | layout.py | 7 | ||||
-rw-r--r-- | mathlib.py | 29 |
7 files changed, 136 insertions, 82 deletions
@@ -6,7 +6,7 @@ mathlib.py plotlib.py svgimage.py toolbars.py -po/calculate.pot +po/Calculate.pot po/en.po po/es.po po/nl.po @@ -1,3 +1,8 @@ +* Parser converted to unicode +* Updates to improve translation #4527 +* Mul/Div symbol i18n #4573 +* Mul/Div button fixed #3526 + 12 11 diff --git a/calculate.py b/calculate.py index 7a83d4e..ad326c7 100644 --- a/calculate.py +++ b/calculate.py @@ -1,3 +1,4 @@ +# -*- coding: UTF-8 -*- # calculate.py, sugar calculator, by: # Reinier Heeres <reinier@heeres.eu> # Miguel Alvarez <miguel@laptop.org> @@ -114,6 +115,8 @@ class Calculate(activity.Activity): 'plus': '+', 'minus': '-', 'asterisk': '*', + 'multiply': u'⨯', + 'divide': u'÷', 'slash': '/', 'BackSpace': lambda o: o.remove_character(-1), 'Delete': lambda o: o.remove_character(1), @@ -162,7 +165,7 @@ class Calculate(activity.Activity): self.clipboard = gtk.Clipboard() self.select_reason = self.SELECT_SELECT - self.buffer = "" + self.buffer = u"" self.showing_version = 0 self.showing_error = False self.show_vars = False @@ -184,21 +187,21 @@ class Calculate(activity.Activity): self.presence = presenceservice.get_instance() self.owner = self.presence.get_owner() self.owner_id = str(self.owner._properties["nick"]) - _logger.debug(_('Owner_id: %s'), self.owner_id) + _logger.debug('Owner_id: %s', self.owner_id) options = { 'receive_message': self.receive_message, 'on_connect': lambda: self.helper.send_message("req_sync", "") } +# self.helper.create_shared_object('old_eqs', +# {'changed': lambda x: self.buddy_old_eqs_cb(), +# 'type': 'python'}, +# iv = []) +# self.helper.create_shared_object('vars', +# {'changed': lambda x: self.buddy_vars_cb(), +# 'type': 'python'}, +# iv = []) self.helper = SharingHelper(self, opt=options) - self.helper.create_shared_object('old_eqs', - {'changed': lambda x: self.buddy_old_eqs_cb(), - 'type': 'python'}, - iv = []) - self.helper.create_shared_object('vars', - {'changed': lambda x: self.buddy_vars_cb(), - 'type': 'python'}, - iv = []) _logger.info(_('Available functions:')) for f in self.parser.get_function_names(): @@ -211,7 +214,7 @@ class Calculate(activity.Activity): return True def cleanup_cb(self, arg): - _logger.debug(_('Cleaning up...')) + _logger.debug('Cleaning up...') def equation_pressed_cb(self, eqn): """Callback for when an equation box is clicked""" @@ -317,9 +320,9 @@ class Calculate(activity.Activity): def process(self): """Parse the equation entered and show the result""" - s = self.text_entry.get_text() - label = self.label_entry.get_text() - _logger.debug(_('process(): parsing \'%s\', label: \'%s\''), s, label) + s = unicode(self.text_entry.get_text()) + label = unicode(self.label_entry.get_text()) + _logger.debug('process(): parsing \'%r\', label: \'%r\'', s, label) res = self.parser.parse(s) if type(res) == types.StringType and res.find('</svg>') > -1: res = SVGImage(data=res) @@ -331,8 +334,8 @@ class Calculate(activity.Activity): self.parser.set_var('Ans', self.ml.format_number(eqn.result)) self.helper.send_message("add_eq", str(eqn)) self.showing_error = False - self.text_entry.set_text('') - self.label_entry.set_text('') + self.text_entry.set_text(u'') + self.label_entry.set_text(u'') # Show error else: @@ -344,7 +347,7 @@ class Calculate(activity.Activity): return res is not None def refresh_bar(self): - _logger.debug(_('Refreshing right bar...')) + _logger.debug('Refreshing right bar...') self.refresh_last_eq() if self.layout.varbut.selected == 0: self.refresh_history() @@ -463,7 +466,7 @@ class Calculate(activity.Activity): self.layout.show_history(list) def clear(self): - self.text_entry.set_text('') + self.text_entry.set_text(u'') self.text_entry.grab_focus() return True @@ -476,6 +479,8 @@ class Calculate(activity.Activity): ########################################## def write_file(self, file_path): + raise ValueError + """Write journal entries, Calculate Journal Version (cjv) 1.0""" _logger.info(_('Writing to journal (%s)'), file_path) @@ -584,7 +589,7 @@ class Calculate(activity.Activity): if end_ofs - start_ofs <= 0: return False partial_name = str[start_ofs:end_ofs] - _logger.debug(_('tab-completing %s...'), partial_name) + _logger.debug('tab-completing %s...', partial_name) # Lookup matching variables vars = self.parser.get_var_names(start=partial_name) @@ -657,15 +662,12 @@ class Calculate(activity.Activity): return key = gtk.gdk.keyval_name(event.keyval) - if key is None: - if event.hardware_keycode == 219: - if (event.state & gtk.gdk.SHIFT_MASK): - key = 'slash' - else: - key = 'asterisk' + if event.hardware_keycode == 219: + if (event.state & gtk.gdk.SHIFT_MASK): + key = 'divide' else: - key = 'None' - _logger.debug(_('Key: %s (%r, %r)'), key, event.keyval, event.hardware_keycode) + key = 'multiply' + _logger.debug('Key: %s (%r, %r)', key, event.keyval, event.hardware_keycode) if (event.state & gtk.gdk.CONTROL_MASK) and self.CTRL_KEYMAP.has_key(key): f = self.CTRL_KEYMAP[key] @@ -716,7 +718,7 @@ class Calculate(activity.Activity): (start, end) = sel text = self.text_entry.get_text() elif len(sel) != 0: - _logger.error(_('button_pressed(): len(sel) != 0 or 2')) + _logger.error('button_pressed(): len(sel) != 0 or 2') return False if type == self.TYPE_FUNCTION: @@ -778,7 +780,7 @@ class Calculate(activity.Activity): tmp = [] self.clear_equations() for eq_str in val: - _logger.info(_('receive_message: %s'), str(eq_str)) + _logger.debug('receive_message: %s', str(eq_str)) self.add_equation(Equation(str=str(eq_str))) self.refresh_bar() diff --git a/eqnparser.py b/eqnparser.py index 3d2b6d9..c4ccca3 100644 --- a/eqnparser.py +++ b/eqnparser.py @@ -1,3 +1,4 @@ +# -*- coding: UTF-8 -*- # eqnparser.py, generic equation parser by Reinier Heeres <reinier@heeres.eu> # # This program is free software; you can redistribute it and/or modify @@ -86,7 +87,7 @@ class ParserState: self.result_type = t return True elif self.result_type != t: - _logger.debug(_('Type error')) + _logger.debug('Type error') return False else: return True @@ -96,10 +97,10 @@ class ParserState: if msg is not None: self.error_msg = msg if range is not None: - _logger.debug(_('Setting range: %r'), range) + _logger.debug('Setting range: %r', range) self.error_range = range else: - _logger.debug(_('Setting offset: %d'), self.ofs) + _logger.debug('Setting offset: %d', self.ofs) self.error_range = (self.ofs, self.ofs + 1) def set_error_range(self, r): @@ -126,13 +127,13 @@ class EqnParser: INVALID_OP = ('err', OP_INVALID, 0, lambda x: False) - NAME_CHARS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789 ' - DIGITS = '0123456789' - SPACE_CHARS = '\t \r\n' + NAME_CHARS = u'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789 ' + DIGITS = u'0123456789' + SPACE_CHARS = u'\t \r\n' # These will be filled from register_operator - OP_START_CHARS = "" - OP_CHARS = "" + OP_START_CHARS = u"" + OP_CHARS = u"" TYPE_UNKNOWN = 1 TYPE_INT = 2 @@ -207,7 +208,9 @@ class EqnParser: self.register_operator('-', self.OP_DIADIC, 0, lambda x: self.ml.sub(x[0], x[1])) self.register_operator('-', self.OP_PRE, 1, lambda x: self.ml.negate(x[0])) self.register_operator('*', self.OP_DIADIC, 1, lambda x: self.ml.mul(x[0], x[1])) + self.register_operator(u'⨯', self.OP_DIADIC, 1, lambda x: self.ml.mul(x[0], x[1])) self.register_operator('/', self.OP_DIADIC, 1, lambda x: self.ml.div(x[0], x[1])) + self.register_operator(u'÷', self.OP_DIADIC, 1, lambda x: self.ml.div(x[0], x[1])) self.register_operator('^', self.OP_DIADIC, 2, lambda x: self.ml.pow(x[0], x[1])) self.register_operator('**', self.OP_DIADIC, 2, lambda x: self.ml.pow(x[0], x[1])) @@ -294,12 +297,12 @@ class EqnParser: # _logger.error('EqnParser.lookup_var(): recursion detected') # return None # self.variables[name].highest_level = level - if type(self.variables[name]) is types.StringType and self.parse_var[name]: + if type(self.variables[name]) is types.UnicodeType and self.parse_var[name]: return self.parse(self.variables[name], reset=False) else: return self.variables[name] else: - _logger.debug(_('variable %s not defined'), name) + _logger.debug('variable %s not defined', name) ps.set_type(self.TYPE_SYMBOLIC) return None @@ -350,15 +353,16 @@ class EqnParser: self.ps.set_error(ParserState.PARSE_ERROR, msg=_('Unable to parse argument %d: \'%s\'') % (i, args[i])) return None + res = f(pargs) try: - res = f(pargs) + pass # Maybe we should map exceptions to more obvious error messages except Exception, inst: res = None self.ps.set_error(ParserState.PARSE_ERROR, msg=_("Function error: %s") % (str(inst))) - _logger.debug(_('Function \'%s\' returned %s'), func, self.ml.format_number(res)) + _logger.debug('Function \'%s\' returned %s', func, self.ml.format_number(res)) return res def parse_number(self, ps): @@ -375,14 +379,14 @@ class EqnParser: ps.next() # exponent - if ps.char is not None and ps.char in 'eE': + if ps.char is not None and ps.char in u'eE': ps.next() - if ps.char is not None and ps.char in '+-': + if ps.char is not None and ps.char in u'+-': ps.next() while ps.more() and ps.char in self.DIGITS: ps.next() - _logger.debug(_('parse_number(): %d - %d: %s'), startofs, ps.ofs, ps.str[startofs:ps.ofs]) + _logger.debug('parse_number(): %d - %d: %s', startofs, ps.ofs, ps.str[startofs:ps.ofs]) n = self.ml.parse_number(ps.str[startofs:ps.ofs]) return n @@ -411,7 +415,7 @@ class EqnParser: break if op is not None: - _logger.debug(_('parse_operator(): %d - %d: %s'), startofs, ps.ofs, ps.str[startofs:ps.ofs]) + _logger.debug('parse_operator(): %d - %d: %s', startofs, ps.ofs, ps.str[startofs:ps.ofs]) return op else: return self.INVALID_OP @@ -431,7 +435,7 @@ class EqnParser: if pcount == 0 and (ps.ofs - startofs) > 0: args.append(ps.str[startofs:ps.ofs]) ps.next() - _logger.debug(_('parse_func_args(): %d - %d: %r'), startofs, ps.ofs, args) + _logger.debug('parse_func_args(): %d - %d: %r', startofs, ps.ofs, args) return args def parse_var_func(self, ps): @@ -445,7 +449,7 @@ class EqnParser: # handle function if ps.char == '(': ps.next() - _logger.debug(_('parse_var_func(): function %d - %d: %s'), startofs, ps.ofs, name) + _logger.debug('parse_var_func(): function %d - %d: %s', startofs, ps.ofs, name) args = self.parse_func_args(ps) ret = self.eval_func(name, args, ps.level) if ret is None: @@ -454,7 +458,7 @@ class EqnParser: # handle var else: - _logger.debug(_('parse_var_func(): variable %d - %d: %s'), startofs, ps.ofs, name) + _logger.debug('parse_var_func(): variable %d - %d: %s', startofs, ps.ofs, name) res = self.lookup_var(name, ps) if res is None: ps.set_error(ParserState.PARSE_ERROR, msg=_("Variable '%s' not defined") % (name), range=(startofs, ps.ofs)) @@ -463,14 +467,14 @@ class EqnParser: def _parse(self, ps, presedence=None): if presedence is None: ps.inc_level() - _logger.debug(_('_parse(): %s, presedence: %r'), ps.state_string(), presedence) + _logger.debug('_parse(): %s, presedence: %r', ps.state_string(), presedence) op = None left_val = None right_val = None while ps.more(): -# _logger.debug(_('Looking at \'%c\', ofs %d in \'%s\''), ps.char, ps.ofs, ps.str) +# _logger.debug('Looking at %r, ofs %d in %r', ps.char, ps.ofs, ps.str) # Skip spaces if ps.char in self.SPACE_CHARS: @@ -491,7 +495,7 @@ class EqnParser: ps.set_error(ParserState.PARSE_ERROR, msg=_("Right parenthesis unexpected")) return None else: - _logger.debug(_('returning %s'), self.ml.format_number(left_val)) + _logger.debug('returning %s', self.ml.format_number(left_val)) return left_val if ps.level > 0: @@ -502,7 +506,7 @@ class EqnParser: ps.set_error(ParserState.PARSE_ERROR, msg=_("Right parenthesis unexpected")) return None else: - _logger.debug(_('returning %s'), self.ml.format_number(left_val)) + _logger.debug('returning %s', self.ml.format_number(left_val)) return left_val else: _logger.error_(('Parse error (right parenthesis, no level to close)')) @@ -510,7 +514,7 @@ class EqnParser: return None # Parse number - elif ps.char in '0123456789.': + elif ps.char == '.' or ps.char in self.DIGITS: if right_val is not None or left_val is not None: _logger.error(_('Number not expected')) ps.set_error(ParserState.PARSE_ERROR, msg=_("Number not expected")) @@ -532,15 +536,16 @@ class EqnParser: if otype == self.OP_DIADIC: if presedence is not None and opres <= presedence: ps.set_ofs(startofs) - _logger.debug(_('returning %s (by presedence, %d)'), self.ml.format_number(left_val), ps.ofs) + _logger.debug('returning %s (by presedence, %d)', self.ml.format_number(left_val), ps.ofs) return left_val else: right_val = self._parse(ps, presedence=opres) if right_val == None: return None + print('left: %r, right: %r') % (left_val, right_val) res = of([left_val, right_val]) - _logger.debug(_('OP: %s, %s ==> %s'), self.ml.format_number(left_val), self.ml.format_number(right_val), self.ml.format_number(res)) + _logger.debug('OP: %s, %s ==> %s', self.ml.format_number(left_val), self.ml.format_number(right_val), self.ml.format_number(res)) left_val = res right_val = None op = None @@ -548,7 +553,7 @@ class EqnParser: # Operator that goes after value elif otype == self.OP_POST: res = of([left_val]) - _logger.debug(_('OP POST: %s ==> %s'), self.ml.format_number(left_val), self.ml.format_number(res)) + _logger.debug('OP POST: %s ==> %s', self.ml.format_number(left_val), self.ml.format_number(res)) left_val = res op = None @@ -558,29 +563,29 @@ class EqnParser: if right_val is None: return None left_val = of([right_val]) - _logger.debug(_('OP PRE: %s ==> %s'), self.ml.format_number(right_val), self.ml.format_number(left_val)) + _logger.debug('OP PRE: %s ==> %s', self.ml.format_number(right_val), self.ml.format_number(left_val)) op = None elif otype == self.OP_INVALID: - _logger.debug(_('Invalid operator')) + _logger.debug('Invalid operator') ps.set_error(ParserState.PARSE_ERROR, msg=_("Invalid operator"), range=(startofs, ps.ofs)) return None # Parse variable or function else: if left_val is not None: - _logger.debug(_('Operator expected')) + _logger.debug('Operator expected: %r', self.OP_START_CHARS) ps.set_error(ParserState.PARSE_ERROR, msg=_("Operator expected")) return None left_val = self.parse_var_func(ps) if not ps.more() and ps.level > 0: - _logger.debug(_('Parse error: \')\' expected')) + _logger.debug('Parse error: \')\' expected') ps.set_error(ParserState.PARSE_ERROR, msg=_("Right parenthesis unexpected")) return None elif op is None and left_val is not None: - _logger.debug(_('returning %s'), self.ml.format_number(left_val)) + _logger.debug('returning %s', self.ml.format_number(left_val)) return left_val else: _logger.error(_('_parse(): returning None')) @@ -596,7 +601,12 @@ class EqnParser: def parse(self, eqn, reset=True): """Construct ParserState object and call _parse""" - _logger.debug(_('parse(): %s'), eqn) + + # Parse everything in unicode + if type(eqn) is types.StringType: + eqn = unicode(eqn) + + _logger.debug('parse(): %r', eqn) self.reset_variable_level(0) if reset: diff --git a/eqnparserhelp.py b/eqnparserhelp.py index 34da3ae..2eb8d7c 100644 --- a/eqnparserhelp.py +++ b/eqnparserhelp.py @@ -19,33 +19,42 @@ # 2007-09-16: rwh, first version import types - from gettext import gettext as _ +import logging +_logger = logging.getLogger('EqnParser') + class Callable: def __init__(self, f): self.__call__ = f -# Just an example implementation; should be fixed for internationalization class EqnParserHelp(): + # Unfortunately gettext is not yet initialized at the time _() is called here. + # Still do it like this to make sure these strings show up in the POT-file DICT = { - "acos": "help_acos", - "asin": "help_asin", - "exp": "help_exp", - "functions": "help_functions", - "operators": "help_operators", - "plot": "help_plot", - "sqrt": "help_sqrt", - "test": "help_test", - "variables": "help_variables", + # These are the help topics and should explain how things work + "acos": _("help_acos"), + "asin": _("help_asin"), + "cos": _("help_cos"), + "exp": _("help_exp"), + "functions": _("help_functions"), + "operators": _("help_operators"), + "plot": _("help_plot"), + "sin": _("help_sin"), + "sqrt": _("help_sqrt"), + "test": _("help_test"), + "variables": _("help_variables"), } def __init__(self): - return + pass def help(about): - if type(about) != types.StringType or len(about) == 0: + _logger.debug('help about %r', about) + + t = type(about) + if (t != types.StringType and t != types.UnicodeType) or len(about) == 0: return _("help_usage") if about == "index": @@ -56,8 +65,8 @@ class EqnParserHelp(): ret = "" for (key, val) in EqnParserHelp.DICT.iteritems(): - if about.find(key) != -1: - ret += _(val) + if about == key: + ret += val if ret == "": ret += _("No help about '%s' available, use help(index) for the index") % (about) @@ -18,6 +18,9 @@ class CalcLayout: return gtk.gdk.Color(int(rf*0xFFFF), int(gf*0xFFFF), int(bf*0xFFFF)) def create_button_data(self): + mul_sym = self._parent.ml.mul_sym + div_sym = self._parent.ml.div_sym + self.button_data = [ # [x, y, width, label, bgcol, cb] [0, 0, 1, '7', self.col_gray2, lambda w: self._parent.add_text('7')], @@ -43,8 +46,8 @@ class CalcLayout: [3, 1, 1, '+', self.col_gray3, lambda w: self._parent.add_text('+')], [4, 1, 1, '-', self.col_gray3, lambda w: self._parent.add_text('-')], [5, 1, 1, '(', self.col_gray3, lambda w: self._parent.add_text('(')], - [3, 2, 1, 'x', self.col_gray3, lambda w: self._parent.add_text('*')], - [4, 2, 1, '/', self.col_gray3, lambda w: self._parent.add_text('/')], + [3, 2, 1, mul_sym, self.col_gray3, lambda w: self._parent.add_text(mul_sym)], + [4, 2, 1, div_sym, self.col_gray3, lambda w: self._parent.add_text(div_sym)], [5, 2, 1, ')', self.col_gray3, lambda w: self._parent.add_text(')')], [3, 3, 3, 'enter', self.col_gray1, lambda w: self._parent.process()], @@ -25,6 +25,8 @@ import random import logging _logger = logging.getLogger('MathLib') +from gettext import gettext as _ + class MathLib: ANGLE_DEG = math.pi/180 ANGLE_RAD = 1 @@ -49,6 +51,29 @@ class MathLib: self.set_constant('c_n', self.parse_number('0')) #neutron properties self.set_constant('m_n', self.parse_number('1.6749272928e-27')) + self.setup_i18n() + + def setup_i18n(self): + # The separator to mark thousands (default: ',') + self.thousand_sep = _('thousand_sep') + if self.thousand_sep == 'thousand_sep': + self.thousand_sep = ',' + + # The separator to mark fractions (default: '.') + self.fraction_sep = _('fraction_sep') + if self.fraction_sep == 'fraction_sep': + self.fraction_sep = '.' + + # The multiplication symbol (default: '*') + self.mul_sym = _('mul_sym') + if self.mul_sym == 'mul_sym': + self.mul_sym = '*' + + # The division symbol (default: '/') + self.div_sym = _('div_sym') + if self.div_sym == 'div_sym': + self.div_sym = '/' + def set_angle_type(self, type): self.angle_scaling = self.d(type) _logger.debug('Angle type set to:%s', self.angle_scaling) @@ -115,9 +140,9 @@ class MathLib: for i in xrange(len(digits)): if i == dot_pos: if i == 0: - res += '0.' + res += '0' + self.fraction_sep else: - res += '.' + res += self.fraction_sep res += str(digits[i]) if len(digits) < dot_pos: |