summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fountain/parse.go100
-rw-r--r--lex/parse.go8
-rw-r--r--lex/type.go5
-rw-r--r--main.go5
-rw-r--r--pdf/create.go8
-rw-r--r--rules/rules.go44
6 files changed, 117 insertions, 53 deletions
diff --git a/fountain/parse.go b/fountain/parse.go
index a3e1747..c0fff65 100644
--- a/fountain/parse.go
+++ b/fountain/parse.go
@@ -1,46 +1,112 @@
+// Fountain is a Markdown-like language for screenplays and the main inspiration for this tool.
+// Read more at fountain.io
package fountain
import (
- "github.com/lapingvino/lexington/lex"
- "strings"
"bufio"
+ "github.com/lapingvino/lexington/lex"
"io"
+ "strings"
)
+var Scene = []string{"INT", "EXT", "EST", "INT./EXT", "INT/EXT", "I/E"}
+
+func CheckScene(row string) (bool, string, string) {
+ var scene bool
+ row = strings.ToUpper(row)
+ for _, prefix := range Scene {
+ if strings.HasPrefix(row, prefix+" ") ||
+ strings.HasPrefix(row, prefix+".") {
+ scene = true
+ }
+ }
+ return scene, "scene", row
+}
+
+func CheckTransition(row string) (bool, string, string) {
+ var trans bool
+ row = strings.ToUpper(row)
+ if strings.HasPrefix(row, ">") || strings.HasSuffix(row, " TO:") {
+ trans = true
+ }
+ return trans, "trans", strings.Trim(row, ">< ")
+}
+
+func CheckSynopse(row string) (bool, string, string) {
+ var synopse bool
+ if strings.HasPrefix(row, "=") {
+ synopse = true
+ }
+ return synopse, "synopse", strings.TrimLeft(row, "= ")
+}
+
+func CheckSection(row string) (bool, string, string) {
+ var section bool
+ if strings.HasPrefix(row, "#") {
+ section = true
+ }
+ return section, "section", row
+}
+
+// This is a Fountain parser, trying to be as close as possible to the description
+// found at https://fountain.io/syntax but it can be incomplete.
+// Over time more and more parts should be configurable here, e.g. INT/EXT translatable to other languages.
func Parse(file io.Reader) (out lex.Screenplay) {
var err error
var s string
- var toParse []string
+ var toParse []string // Fill with two to avoid out of bounds when backtracking
f := bufio.NewReader(file)
for err == nil {
s, err = f.ReadString('\n')
toParse = append(toParse, s)
}
+ toParse = append(toParse, "") // Trigger the backtracking also for the last line
+ out = make(lex.Screenplay, 2, len(toParse))
for i, row := range toParse {
- row = strings.TrimSpace(row)
+ i += 2
+ row = strings.TrimRight(row, "\n\r")
action := "action"
if row == strings.ToUpper(row) {
action = "allcaps"
}
if row == "" {
action = "empty"
- continue
+
+ // Backtracking for elements that need a following empty line
+ checkfuncs := []func(string) (bool, string, string){
+ CheckScene,
+ CheckTransition,
+ CheckSynopse,
+ CheckSection,
+ }
+ for _, checkfunc := range checkfuncs {
+ check, element, contents := checkfunc(out[i-1].Contents)
+ if check && out[i-2].Contents == "" {
+ out[i-1].Type = element
+ out[i-1].Contents = contents
+ break
+ }
+ }
}
- if i <= 0 {
- continue
+ if out[i-1].Type != "action" {
+ out[i-1].Contents = strings.TrimSpace(out[i-1].Contents)
}
- switch out[i-1].Type {
- case "allcaps":
- out[i-1].Type = "speaker"
- if row[0] == '(' && row[len(row)-1] == ')' {
- action = "paren"
- } else {
- action = "dialog"
+
+ // Backtracking to check for dialog sequence
+ if row != "" {
+ switch out[i-1].Type {
+ case "allcaps":
+ out[i-1].Type = "speaker"
+ fallthrough
+ case "paren", "dialog":
+ if row[0] == '(' && row[len(row)-1] == ')' {
+ action = "paren"
+ } else {
+ action = "dialog"
+ }
}
- case "paren", "dialog":
- action = "dialog"
}
out = append(out, lex.Line{action, row})
}
- return out
+ return out[2:] // Remove the safety empty rows
}
diff --git a/lex/parse.go b/lex/parse.go
index e105a22..00a6cbd 100644
--- a/lex/parse.go
+++ b/lex/parse.go
@@ -1,8 +1,8 @@
package lex
import (
- "io"
"bufio"
+ "io"
"strings"
)
@@ -18,9 +18,9 @@ func Parse(file io.Reader) (out Screenplay) {
var line Line
s, err = f.ReadString('\n')
split := strings.SplitN(s, ":", 2)
- switch len(split){
- case 0,1:
- line.Type = strings.Trim(s,": \n\r")
+ switch len(split) {
+ case 0, 1:
+ line.Type = strings.Trim(s, ": \n\r")
case 2:
line.Type = split[0]
line.Contents = strings.TrimSpace(split[1])
diff --git a/lex/type.go b/lex/type.go
index eec2f3e..a906ac2 100644
--- a/lex/type.go
+++ b/lex/type.go
@@ -3,8 +3,7 @@ package lex
type Screenplay []Line
-type Line struct{
- Type string
+type Line struct {
+ Type string
Contents string
}
-
diff --git a/main.go b/main.go
index a6a0ed9..90a56ff 100644
--- a/main.go
+++ b/main.go
@@ -3,15 +3,15 @@
package main
import (
- "github.com/lapingvino/lexington/lex"
"github.com/lapingvino/lexington/fountain"
+ "github.com/lapingvino/lexington/lex"
"github.com/lapingvino/lexington/pdf"
"github.com/lapingvino/lexington/rules"
"flag"
+ "io"
"log"
"os"
- "io"
)
func main() {
@@ -55,7 +55,6 @@ func main() {
}
}
-
log.Println("Input type is ", *from)
switch *from {
case "lex":
diff --git a/pdf/create.go b/pdf/create.go
index 05cc871..52c3122 100644
--- a/pdf/create.go
+++ b/pdf/create.go
@@ -10,9 +10,9 @@ import (
var tr func(string) string
type Tree struct {
- PDF *gofpdf.Fpdf
+ PDF *gofpdf.Fpdf
Rules rules.Set
- F lex.Screenplay
+ F lex.Screenplay
}
func (t Tree) pr(a string, text string) {
@@ -50,9 +50,9 @@ func Create(file string, format rules.Set, contents lex.Screenplay) {
pdf.SetMargins(1, 1, 1)
pdf.SetXY(1, 1)
f := Tree{
- PDF: pdf,
+ PDF: pdf,
Rules: format,
- F: contents,
+ F: contents,
}
f.Render()
err := pdf.OutputFileAndClose(file)
diff --git a/rules/rules.go b/rules/rules.go
index 030fcb0..775142f 100644
--- a/rules/rules.go
+++ b/rules/rules.go
@@ -1,12 +1,12 @@
package rules
-type Format struct{
+type Format struct {
Width float64
- Left float64
- Font string
+ Left float64
+ Font string
Style string
- Size float64
- Hide bool
+ Size float64
+ Hide bool
Align string
}
@@ -31,51 +31,51 @@ func (s Set) Get(action string) (f Format) {
}
var Default = Set{
- "action": {
- Left: 1.5,
+ "action": {
+ Left: 1.5,
Width: 6,
},
"speaker": {
- Left: 4.2,
+ Left: 4.2,
Width: 3.3,
},
- "dialog": {
- Left: 2.9,
+ "dialog": {
+ Left: 2.9,
Width: 3.3,
},
- "scene": {
- Left: 1.5,
+ "scene": {
+ Left: 1.5,
Width: 6,
Style: "b",
},
- "paren": {
- Left: 3.6,
+ "paren": {
+ Left: 3.6,
Width: 4,
},
- "trans": {
- Left: 1.5,
+ "trans": {
+ Left: 1.5,
Width: 6,
Align: "R",
},
- "note": {
- Left: 1.5,
+ "note": {
+ Left: 1.5,
Width: 6,
},
"allcaps": {
- Left: 1.5,
+ Left: 1.5,
Width: 6,
},
"empty": {
- Left: 1.5,
+ Left: 1.5,
Width: 6,
},
"title": {
- Left: 1.5,
+ Left: 1.5,
Width: 6,
Align: "C",
},
"meta": {
- Left: 1.5,
+ Left: 1.5,
Width: 3,
},
}