summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--screenplain/parsers/spmd.py51
-rw-r--r--tests/spmd_test.py58
2 files changed, 104 insertions, 5 deletions
diff --git a/screenplain/parsers/spmd.py b/screenplain/parsers/spmd.py
index 2940a8c..f9aa643 100644
--- a/screenplain/parsers/spmd.py
+++ b/screenplain/parsers/spmd.py
@@ -3,6 +3,7 @@
# http://www.opensource.org/licenses/mit-license.php
import itertools
+from itertools import takewhile
import re
from screenplain.types import (
@@ -18,6 +19,9 @@ slug_regexes = (
TWOSPACE = ' ' * 2
+title_page_key_re = re.compile(r'([^:]+):\s*(.*)')
+title_page_value_re = re.compile(r'(?:\s{3,}|\t)(.+)')
+
centered_re = re.compile(r'\s*>\s*(.*?)\s*<\s*$')
dual_dialog_re = re.compile(r'^(.+?)(\s*\^)$')
slug_re = re.compile(r'(?:(\.)\s*)?(\S.*?)\s*$')
@@ -153,6 +157,24 @@ def _is_blank(line):
def parse(source):
"""Reads raw text input and generates paragraph objects."""
source = (_preprocess_line(line) for line in source)
+
+ title_page_lines = list(takewhile(lambda line: line != '', source))
+ title_page = parse_title_page(title_page_lines)
+
+ if title_page:
+ # The first lines were a title page.
+ # Parse the rest of the source as screenplay body.
+ # TODO: Create a title page from the data in title_page
+ return parse_body(source)
+ else:
+ # The first lines were not a title page.
+ # Parse them as part of the screenplay body.
+ return parse_body(itertools.chain(title_page_lines, [''], source))
+
+
+def parse_body(source):
+ """Reads lines of the main screenplay and generates paragraph objects."""
+
paragraphs = []
for blank, input_lines in itertools.groupby(source, _is_blank):
if not blank:
@@ -160,3 +182,32 @@ def parse(source):
paragraph.update_list(paragraphs)
return paragraphs
+
+def parse_title_page(lines):
+
+ result = {}
+
+ it = iter(lines)
+ try:
+ line = it.next()
+ while True:
+ key_match = title_page_key_re.match(line)
+ if not key_match:
+ return None
+ key, value = key_match.groups()
+ if value:
+ # Single line key/value
+ result.setdefault(key, []).append(value)
+ line = it.next()
+ else:
+ for line in it:
+ value_match = title_page_value_re.match(line)
+ if not value_match:
+ break
+ result.setdefault(key, []).append(value_match.group(1))
+ else:
+ # Last line has been processed
+ break
+ except StopIteration:
+ pass
+ return result
diff --git a/tests/spmd_test.py b/tests/spmd_test.py
index aac8df9..bb5f463 100644
--- a/tests/spmd_test.py
+++ b/tests/spmd_test.py
@@ -4,6 +4,7 @@
import unittest2
from screenplain.parsers.spmd import parse
+from screenplain.parsers import spmd
from screenplain.types import (
Slug, Action, Dialog, DualDialog, Transition, Section
)
@@ -12,11 +13,6 @@ from screenplain.richstring import plain, italic, empty_string
class ParseTests(unittest2.TestCase):
- # A Scene Heading, or "slugline," is any line that has a blank
- # line following it, and either begins with INT or EXT, or has
- # two empty lines preceding it. A Scene Heading always has at
- # least one blank line preceding it.
- # NOTE: Actually the list used in Appendix 1
def test_slug_with_prefix(self):
paras = list(parse([
'INT. SOMEWHERE - DAY',
@@ -337,5 +333,57 @@ class ParseTests(unittest2.TestCase):
self.assertFalse(paras[0].centered)
self.assertEquals([plain(line) for line in lines], paras[0].lines)
+class TitlePageTests(unittest2.TestCase):
+
+ def test_basic_title_page(self):
+ lines = [
+ 'Title:',
+ ' _**BRICK & STEEL**_',
+ ' _**FULL RETIRED**_',
+ 'Author: Stu Maschwitz',
+ ]
+ self.assertDictEqual(
+ {
+ 'Title': ['_**BRICK & STEEL**_', '_**FULL RETIRED**_'],
+ 'Author': ['Stu Maschwitz'],
+ },
+ spmd.parse_title_page(lines)
+ )
+
+ def test_multiple_values(self):
+ lines = [
+ 'Title: Death',
+ 'Title: - a love story',
+ 'Title:',
+ ' (which happens to be true)',
+ ]
+ self.assertDictEqual(
+ {
+ 'Title': [
+ 'Death',
+ '- a love story',
+ '(which happens to be true)'
+ ]
+ },
+ spmd.parse_title_page(lines)
+ )
+
+ def test_empty_value_ignored(self):
+ lines = [
+ 'Title:',
+ 'Author: John August',
+ ]
+ self.assertDictEqual(
+ {'Author': ['John August']},
+ spmd.parse_title_page(lines)
+ )
+
+ def test_unparsable_title_page_returns_none(self):
+ lines = [
+ 'Title: Inception',
+ ' additional line',
+ ]
+ self.assertIsNone(spmd.parse_title_page(lines))
+
if __name__ == '__main__':
unittest2.main()