aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--screenplain/parse.py99
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."""