plint

French poetry validator
git clone https://a3nm.net/git/plint/
Log | Files | Refs | README

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:
error.py | 16++++++++++++++--
plint.py | 8++++++--
static/tpl/french_abab.tpl | 1+
static/tpl/french_abba.tpl | 1+
static/tpl/italian_abab.tpl | 1+
static/tpl/italian_abba.tpl | 1+
template.py | 36++++++++++++++++++++++++++++++------
views/about.html | 14++++++++++++++
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&nbsp;:</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