home · contact · privacy
Calibrate pee need messaging less aggressively.
[plomrogue2] / plomrogue / parser.py
index 5782d695fe1f026d4dc2b00f422cda389fae9988..302733d0322a69c9592276b94239b626debcb4c3 100644 (file)
@@ -1,4 +1,3 @@
-import unittest
 from plomrogue.errors import ArgError
 from plomrogue.mapping import YX
 
 from plomrogue.errors import ArgError
 from plomrogue.mapping import YX
 
@@ -45,16 +44,23 @@ class Parser:
         """Parse yx_string as yx_tuple, return result.
 
         The range_ argument may be 'nonneg' (non-negative, including
         """Parse yx_string as yx_tuple, return result.
 
         The range_ argument may be 'nonneg' (non-negative, including
-        0) or 'pos' (positive, excluding 0).
+        0) or 'pos' (positive, excluding 0) or 'all'.
 
         """
 
         def get_axis_position_from_argument(axis, token):
 
         """
 
         def get_axis_position_from_argument(axis, token):
-            if len(token) < 3 or token[:2] != axis + ':' or \
-                    not (token[2:].isdigit() or token[2] == '-'):
-                raise ArgError('Non-int arg for ' + axis + ' position.')
-            n = int(token[2:])
-            if n < 1 and range_ == 'pos':
+            if token[:2] != axis + ':':
+                raise ArgError('invalid YX tuple formatting')
+            n_string = token[2:]
+            if n_string.strip() != n_string:
+                raise ArgError('invalid YX tuple formatting')
+            try:
+                n = int(n_string)
+            except ValueError:
+                raise ArgError('non-int value for ' + axis + ' position')
+            if range_ == 'all':
+                return n
+            if n < 1 and range == 'pos':
                 raise ArgError('Arg for ' + axis + ' position < 1.')
             elif n < 0 and range_ == 'nonneg':
                 raise ArgError('Arg for ' + axis + ' position < 0.')
                 raise ArgError('Arg for ' + axis + ' position < 1.')
             elif n < 0 and range_ == 'nonneg':
                 raise ArgError('Arg for ' + axis + ' position < 0.')
@@ -71,7 +77,18 @@ class Parser:
         """Parse msg as call to function, return function with args tuple.
 
         Respects function signature defined in function's .argtypes attribute.
         """Parse msg as call to function, return function with args tuple.
 
         Respects function signature defined in function's .argtypes attribute.
+
+        Throws out messages with any but a small list of acceptable characters.
+
         """
         """
+        import string
+        msg = msg.replace('\n', ' ')  # Inserted by some tablet keyboards.
+        legal_chars = string.digits + string.ascii_letters +\
+            string.punctuation + ' ' + 'ÄäÖöÜüߧ' + 'éèáàô' + '–…'
+        for c in msg:
+            if not c in legal_chars:
+                raise ArgError('Command/message contains illegal character(s), '
+                               'may only contain ones of: %s' % legal_chars)
         tokens = self.tokenize(msg)
         if len(tokens) == 0:
             return None, ()
         tokens = self.tokenize(msg)
         if len(tokens) == 0:
             return None, ()
@@ -95,8 +112,8 @@ class Parser:
         tmpl_tokens = signature.split()
         if len(tmpl_tokens) != len(args_tokens):
             raise ArgError('Number of arguments (' + str(len(args_tokens)) +
         tmpl_tokens = signature.split()
         if len(tmpl_tokens) != len(args_tokens):
             raise ArgError('Number of arguments (' + str(len(args_tokens)) +
-                           ') not expected number (' + str(len(tmpl_tokens))
-                           ').')
+                           ') not expected number (' + str(len(tmpl_tokens)) +
+                           ').')
         args = []
         string_string = 'string'
         for i in range(len(tmpl_tokens)):
         args = []
         string_string = 'string'
         for i in range(len(tmpl_tokens)):
@@ -106,10 +123,31 @@ class Parser:
                 if not arg.isdigit():
                     raise ArgError('Argument must be non-negative integer.')
                 args += [int(arg)]
                 if not arg.isdigit():
                     raise ArgError('Argument must be non-negative integer.')
                 args += [int(arg)]
+            elif tmpl == 'int:pos':
+                if not arg.isdigit() or int(arg) < 1:
+                    raise ArgError('Argument must be positive integer.')
+                args += [int(arg)]
+            elif tmpl == 'int':
+                try:
+                    args += [int(arg)]
+                except ValueError:
+                    raise ArgError('Argument must be integer.')
+            elif tmpl == 'bool':
+                if not arg.isdigit() or int(arg) not in (0, 1):
+                    raise ArgError('Argument must be 0 or 1.')
+                args += [bool(int(arg))]
+            elif tmpl == 'char':
+                try:
+                    ord(arg)
+                except TypeError:
+                    raise ArgError('Argument must be single character.')
+                args += [arg]
             elif tmpl == 'yx_tuple:nonneg':
                 args += [self.parse_yx_tuple(arg, 'nonneg')]
             elif tmpl == 'yx_tuple:pos':
                 args += [self.parse_yx_tuple(arg, 'pos')]
             elif tmpl == 'yx_tuple:nonneg':
                 args += [self.parse_yx_tuple(arg, 'nonneg')]
             elif tmpl == 'yx_tuple:pos':
                 args += [self.parse_yx_tuple(arg, 'pos')]
+            elif tmpl == 'yx_tuple':
+                args += [self.parse_yx_tuple(arg, 'all')]
             elif tmpl == string_string:
                 args += [arg]
             elif tmpl[:len(string_string) + 1] == string_string + ':':
             elif tmpl == string_string:
                 args += [arg]
             elif tmpl[:len(string_string) + 1] == string_string + ':':