diff options
-rw-r--r-- | screenplain/parse.py | 99 |
1 files changed, 68 insertions, 31 deletions
diff --git a/screenplain/parse.py b/screenplain/parse.py index a872f7e..3f19662 100644 --- a/screenplain/parse.py +++ b/screenplain/parse.py @@ -6,26 +6,18 @@ import re # According to http://johnaugust.com/2004/how-many-lines-per-page lines_per_page = 56 -slug_re = re.compile('^(EXT\.|INT\.|EXT/INT\.|I/E) ', re.IGNORECASE) - - -class _ParentheticalFlipFlop(object): - def __init__(self): - self.inside = False - - def __call__(self, text): - stripped = text.strip() - starts = stripped.startswith('(') - ends = stripped.endswith(')') - - if self.inside: - if ends: - self.inside = False - return True - else: - if starts and not ends: - self.inside = True - return starts +slug_prefixes = ( + 'INT. ', + 'EXT. ', + 'INT./EXT. ', + 'INT/EXT. ', + 'INT ', + 'EXT ', + 'INT/EXT ', + 'I/E ', +) + +TWOSPACE = ' ' * 2 class Slug(object): indent = '' @@ -50,10 +42,36 @@ class Dialog(object): def __init__(self, lines): self.character = lines[0] - flipflop = _ParentheticalFlipFlop() - self.blocks = [ - (paren, ' '.join(t.strip() for t in lines)) - for paren, lines in itertools.groupby(lines[1:], flipflop)] + self.secondary_character = None + self.dual = False + self.blocks = [] # list of tuples of (is_parenthetical, text) + self.secondary_blocks = [] # for the second speaker if dual + self._parse(lines[1:]) + + def _parse(self, lines): + append = self.blocks.append + inside_parenthesis = False + it = iter(lines) + while True: + try: + line = it.next() + except StopIteration: + return + if line == '||' and not self.dual: + self.dual = True + append = self.secondary_blocks.append + try: + self.secondary_character = it.next() + # what if this doesn't look like a character name? + except StopIteration: + append(line) # keep trailing '||' + return + else: + if line.startswith('('): + inside_parenthesis = True + append((inside_parenthesis, line)) + if line.endswith(')'): + inside_parenthesis = False def format(self): yield self.indent_character + self.character @@ -88,31 +106,50 @@ class Action(object): for line in textwrap.wrap(self.text, width=self.fill): yield self.indent + line +class Transition(object): + indent = '' + fill = 68 + top_margin = 1 + + def __init__(self, lines): + self.text = ' '.join(line.strip() for line in lines) + + def format(self): + for line in textwrap.wrap(self.text, width=self.fill): + yield self.indent + line + def is_blank(string): return string == '' or string.isspace() and string != ' ' def is_slug(blanks_before, string): - return blanks_before >= 2 or slug_re.match(string) + if blanks_before >= 2: + return True + upper = string.upper() + return any(upper.startswith(s) for s in slug_prefixes) def create_paragraph(blanks_before, line_list): if is_slug(blanks_before, line_list[0]): return Slug(line_list) - if len(line_list) > 1 and line_list[0].isupper(): + if ( + len(line_list) > 1 and + line_list[0].isupper() and + not line_list[0].endswith(TWOSPACE)): return Dialog(line_list) + elif len(line_list) == 1 and line_list[0].endswith(':'): + # Not according to spec, see my comment + #http://prolost.com/blog/2011/8/9/screenplay-markdown.html?lastPage=true#comment14865294 + return Transition(line_list) else: return Action(line_list) def parse(source): """Reads raw text input and generates paragraph objects.""" blank_count = 0 - for blank, lines in itertools.groupby( - (line.rstrip() for line in source), is_blank - ): + for blank, lines in itertools.groupby(source, is_blank): if blank: - blank_count += 1 + blank_count = len(list(lines)) else: yield create_paragraph(blank_count, list(lines)) - blank_count = 0 def get_pages(paragraphs): """Generates one list of lines per page.""" |