commit 7f93dfb1a042560e57035786c8bab5b5e719bf81
parent 9ec20de861e47bd1b7c0f31c31de5e7c684c6441
Author: Antoine Amarilli <a3nm@a3nm.net>
Date:   Wed, 30 Jan 2013 22:52:01 +0100
Add repeat_ok and incomplete_ok options
Following Robert Rapilly's suggestion
<49CB20FF-7AB0-4A7D-9EE6-A9E1FF2CDC48@orange.fr>.
Diffstat:
8 files changed, 68 insertions(+), 10 deletions(-)
diff --git a/error.py b/error.py
@@ -26,11 +26,13 @@ class Error:
     msg = _("Line is: %s") % (self.line)
     if short:
       if t != []:
-        l.append(msg)
+        if self.line.strip() != "":
+          l.append(msg)
         for x in t:
           l.append(x)
     else:
-      l.append(self.say(msg))
+      if self.line.strip() != "":
+        l.append(self.say(msg))
       for x in t:
         l.append(self.say(x))
     return '\n'.join(l)
@@ -197,3 +199,13 @@ class ErrorMultipleWordOccurrence(Error):
     return Error.report(self, _("Too many occurrences of word %s for rhyme %s")
         % (self.word, self.get_id()), short)
 
+class ErrorIncompleteTemplate(Error):
+  def report(self, short=False):
+    return Error.report(self, _("Poem is not complete"),
+        short)
+
+class ErrorOverflowedTemplate(Error):
+  def report(self, short=False):
+    return Error.report(self, _("Verse is beyond end of poem"),
+        short)
+
diff --git a/plint.py b/plint.py
@@ -9,14 +9,18 @@ def run():
   f2 = None
   if len(sys.argv) == 3:
     f2 = open(sys.argv[2], 'w')
+  should_end = False
   while True:
     line = sys.stdin.readline()
     if not line:
-      break
-    errors = template.check(line, f2)
+      should_end = True
+      line = ""
+    errors = template.check(line, f2, last=should_end)
     for error in errors:
       print(error.report(), file=sys.stderr)
       ok = False
+    if should_end:
+      break
   return ok
 
 if __name__ == '__main__':
diff --git a/static/tpl/french_abab.tpl b/static/tpl/french_abab.tpl
@@ -1,3 +1,4 @@
+! incomplete_ok:no repeat_ok:no
 6/6 A x
 6/6 B X
 6/6 A X
diff --git a/static/tpl/french_abba.tpl b/static/tpl/french_abba.tpl
@@ -1,3 +1,4 @@
+! incomplete_ok:no repeat_ok:no
 6/6 A x
 6/6 B X
 6/6 B X
diff --git a/static/tpl/italian_abab.tpl b/static/tpl/italian_abab.tpl
@@ -1,3 +1,4 @@
+! incomplete_ok:no repeat_ok:no
 6/6 A x
 6/6 B X
 6/6 A X
diff --git a/static/tpl/italian_abba.tpl b/static/tpl/italian_abba.tpl
@@ -1,3 +1,4 @@
+! incomplete_ok:no repeat_ok:no
 6/6 A x
 6/6 B X
 6/6 B X
diff --git a/template.py b/template.py
@@ -48,6 +48,9 @@ class Template:
     self.forbidden_ok = False
     self.hiatus_ok = False
     self.normande_ok = True
+    self.repeat_ok = True
+    self.overflowed = False
+    self.incomplete_ok = True
     self.check_end_hemistiche = True
     self.check_occurrences = True
     self.diaeresis = "classical"
@@ -81,6 +84,10 @@ class Template:
       self.check_end_hemistiche = str2bool(value)
     elif key in ["check_occurrences", "verifie_occurrences"]:
       self.check_occurrences = str2bool(value)
+    elif key in ["repeat_ok"]:
+      self.repeat_ok = str2bool(value)
+    elif key in ["incomplete_ok"]:
+      self.incomplete_ok = str2bool(value)
     else:
       raise ValueError
 
@@ -114,12 +121,23 @@ class Template:
     return ((1+len(hemis.keys()))*abs(pattern.length - c)
         + sum([1 for x in hemis.values() if x != "ok"]))
 
-  def match(self, line, ofile=None, quiet=False):
+  def match(self, line, ofile=None, quiet=False, last=False):
     """Check a line against current pattern, return errors"""
 
+    was_incomplete = last and not self.beyond
+
     errors = []
     pattern = self.get()
 
+    if last:
+      if was_incomplete:
+        errors.append(error.ErrorIncompleteTemplate())
+      return errors, pattern
+
+    if self.overflowed:
+      errors.append(error.ErrorOverflowedTemplate())
+      return errors, pattern
+
     # check characters
     illegal = set()
     for x in line:
@@ -213,7 +231,7 @@ class Template:
         errors.append(error.ErrorMultipleWordOccurrence(last_word,
           self.occenv[pattern.myid][last_word]))
 
-    # rhyme genres
+        # rhyme genres
     # inequality constraint
     # TODO this is simplistic and order-dependent
     if pattern.femid.swapcase() in self.femenv.keys():
@@ -274,13 +292,19 @@ class Template:
     self.femenv = self.reset_conditional(self.femenv)
     self.occenv = {} # always reset
 
+  @property
+  def beyond(self):
+    return self.position >= len(self.template)
+
   def get(self):
     """Get next state, resetting if needed"""
     self.old_position = self.position
     self.old_env = copy.deepcopy(self.env)
     self.old_femenv = copy.deepcopy(self.femenv)
     self.old_occenv = copy.deepcopy(self.occenv)
-    if self.position >= len(self.template):
+    if self.beyond:
+      if not self.repeat_ok:
+        self.overflowed = True
       self.reset_state()
     result = self.template[self.position]
     self.position += 1
@@ -293,15 +317,15 @@ class Template:
     self.femenv = copy.deepcopy(self.old_femenv)
     self.occenv = copy.deepcopy(self.old_occenv)
 
-  def check(self, line, ofile=None, quiet=False):
+  def check(self, line, ofile=None, quiet=False, last=False):
     """Check line (wrapper)"""
     self.line_no += 1
     line = line.rstrip()
-    if normalize(line) == '':
+    if normalize(line) == '' and not last:
       return []
     #possible = [compute(p) for p in possible]
     #possible = sorted(possible, key=rate)
-    errors, pattern = self.match(line, ofile, quiet=quiet)
+    errors, pattern = self.match(line, ofile, quiet=quiet, last=last)
     for error in errors:
       if error != None:
         # update errors with line position and pattern
diff --git a/views/about.html b/views/about.html
@@ -143,6 +143,13 @@ options suivantes sont reconnues :</p>
   approximations plus permissives qui accepteront à peu près n'importe quelle
   diérèse ou synérèse phonétiquement plausible. La valeur par défaut est
   "classical".</dd>
+  <dt>repeat_ok</dt>
+  <dd>Si cette valeur est false, le modèle ne se répétera pas, un poème plus
+  long que le modèle provoquera une erreur. Par défaut, la valeur est true.</dd>
+  <dt>incomplete_ok</dt>
+  <dd>Si cette valeur est false, le poème devrait avoir exactement la longueur
+  du modèle (ou un multiple, si repeat_ok est true), ne pas aller jusqu'à la fin
+  du modèle provoquera une erreur. Par défaut, la valeur est true.</dd>
 </dl>
 
 <p>Désolé si tout cela semble un peu obscur. Vous pouvez regarder les modèles
@@ -301,6 +308,13 @@ form option:value and separated by spaces, with the following possibilities:</p>
   rules of classical verse or "permissive" to enforce a more permissive
   approximations which will allow mostly all phonetically reasonable diérèses
   and synérèses. Default is "classical".</dd>
+  <dt>repeat_ok</dt>
+  <dd>If the value is false, the template will not repeat, and going beyond its
+  end will be an error. Default is true.</dd>
+  <dt>incomplete_ok</dt>
+  <dd>If the value is false, the poem should match the length of the template
+  (or a multiple thereof is repeat_ok was set), and not completing a template
+  run will be an error. Default is true.</dd>
 </dl>
 
 <p>Sorry if all of this is a bit obscure. You can have a look to the predefined