snale wants to see
This commit is contained in:
parent
ac15bde327
commit
1405bc0495
135
Syntax.fs
135
Syntax.fs
@ -1,46 +1,145 @@
|
|||||||
|
/// AST definition for VijiN declarative markup.
|
||||||
|
///
|
||||||
|
/// VijiN scene files are inspired by stage play manuscripts.
|
||||||
|
/// Most of a scene is "spoken" text, written as a speaker name followed by a colon.
|
||||||
|
/// Directives in square brackets indicate how the content should be presented,
|
||||||
|
/// rather than defining the content itself.
|
||||||
|
///
|
||||||
|
/// The syntax includes some formatting indicators.
|
||||||
|
/// Text between forward slashes is emphasized, with the expectation that it
|
||||||
|
/// will be rendered in italics.
|
||||||
|
/// Double asterisks indicate yelled text, to be rendered in bold or large font.
|
||||||
|
/// Underscores mark whispered text.
|
||||||
|
///
|
||||||
|
/// Almost all syntactic constraints can be escaped by quoting text in backticks.
|
||||||
|
/// The exception is that backtick-quoted text cannot contain backticks or newlines.
|
||||||
module VijiN.Syntax
|
module VijiN.Syntax
|
||||||
|
|
||||||
|
/// Name of a variable to expand.
|
||||||
|
///
|
||||||
|
/// A variable name may contain any text that doesn't have other syntactic meaning,
|
||||||
|
/// including whitespace. It may be quoted in backticks.
|
||||||
type Variable = Variable of string
|
type Variable = Variable of string
|
||||||
|
|
||||||
|
/// A single term of text, as an element of a spoken line, directive argument,
|
||||||
|
/// character name, &c.
|
||||||
type Term = TextTerm of string | VarTerm of Variable
|
type Term = TextTerm of string | VarTerm of Variable
|
||||||
|
|
||||||
type Character = Character of Term
|
|
||||||
// TODO: Character should probably have more, like selecting portrait outfit/style
|
|
||||||
|
|
||||||
type DirectivePrefix = string
|
type DirectivePrefix = string
|
||||||
type DirectiveInfix = string
|
type DirectiveInfix = string
|
||||||
type DirectiveOperator = DirectivePrefix * DirectiveInfix
|
type DirectiveSuffix = string
|
||||||
|
/// Fixed, syntactic components of a directive. May be interspersed with arguments.
|
||||||
|
/// Directives in a directive line may be separated with either dots or commas;
|
||||||
|
/// in the former case, the variant is DirectiveOperator, and in the latter
|
||||||
|
/// case, the variant is ContinueDirective with the implication that the prefix
|
||||||
|
/// is the same as that of the previous operator in the block.
|
||||||
|
type DirectiveOperator =
|
||||||
|
| DirectiveOperator of DirectivePrefix * DirectiveInfix option * DirectiveSuffix option
|
||||||
|
| ContinueDirective of DirectiveInfix option * DirectiveSuffix option
|
||||||
|
|
||||||
|
/// A single argument to a directive.
|
||||||
type DirectiveArg = Arg of Term list
|
type DirectiveArg = Arg of Term list
|
||||||
type Directive = DirectiveOperator * DirectiveArg list list // TODO: non-empty list
|
type DirectivePrefixArg = DirectiveArg
|
||||||
type Directives = Directive list // TODO: non-empty list
|
type DirectiveInfixArg = DirectiveArg
|
||||||
|
type DirectiveSuffixArg = DirectiveArg
|
||||||
type ChoiceText = ChoiceText of Term list
|
type DirectiveListArg = DirectiveArg
|
||||||
type ChoiceValue = ChoiceValue of Term list
|
/// A directive instance, the composition of an operator with any arguments.
|
||||||
type ChoiceOption = ChoiceText * ChoiceValue option
|
/// An argument may be absent even if a later argument is provided.
|
||||||
type Choice = Variable * ChoiceOption list // TODO: non-empty list
|
type Directive = DirectiveOperator * DirectivePrefixArg * DirectiveInfixArg * DirectiveSuffixArg
|
||||||
|
/// A directive block. Either a single directive with a possibly empty list of
|
||||||
|
/// arguments, or a list of directives
|
||||||
|
type DirectiveBlock =
|
||||||
|
| SingleDirective of Directive * DirectiveListArg list
|
||||||
|
| DirectiveList of Directive list
|
||||||
|
|
||||||
|
/// The speaker of a spoken line.
|
||||||
|
/// The narrator is syntactically indicated by an empty name, i.e. a line
|
||||||
|
/// beginning with a colon. Narrated lines may also be given as lines spoken
|
||||||
|
/// by "Narrator" when there is no defined character named the same, but that
|
||||||
|
/// is resolved semantically rather than syntactically.
|
||||||
|
type Speaker =
|
||||||
|
| Narrator
|
||||||
|
| Speaker of Term list
|
||||||
|
/// An inline directive appearing between a speaker name and their line.
|
||||||
type InlineDirective = Directive
|
type InlineDirective = Directive
|
||||||
|
/// The text of a spoken line.
|
||||||
type SpokenText =
|
type SpokenText =
|
||||||
|
/// Plain spoken or narrated text.
|
||||||
| Plain of Term list
|
| Plain of Term list
|
||||||
|
/// Emphasized text written between slashes. Intended to be rendered in italics.
|
||||||
| Emphasis of Term list
|
| Emphasis of Term list
|
||||||
|
/// Yelled text written between double asterisks. Intended to be rendered
|
||||||
|
/// in bold or large text.
|
||||||
| Yell of Term list
|
| Yell of Term list
|
||||||
|
/// Whispered text written between underscores. Intended to be rendered
|
||||||
|
/// in small text.
|
||||||
| Whisper of Term list
|
| Whisper of Term list
|
||||||
type Spoken = Character option * InlineDirective list * SpokenText list // TODO: non-empty list
|
/// A spoken line. The character is absent for narrated lines.
|
||||||
|
type Spoken = Speaker * InlineDirective list * SpokenText list // TODO: non-empty list
|
||||||
|
|
||||||
|
/// A comment. Text between both square and round brackets, without nesting.
|
||||||
type Comment = Comment of string
|
type Comment = Comment of string
|
||||||
|
|
||||||
|
/// A line of script.
|
||||||
type Line =
|
type Line =
|
||||||
| SpokenLine of Spoken
|
| SpokenLine of Spoken
|
||||||
| DirectiveLine of Directives
|
| DirectiveLine of DirectiveBlock
|
||||||
| CommentLine of Comment
|
| CommentLine of Comment
|
||||||
| ChoiceLine of Choice
|
|
||||||
|
|
||||||
type LineNumber = LineNo of int // TODO: >= 1
|
type LineNumber = LineNo of int
|
||||||
type PageNumber = PageNo of int // TODO: >= 1
|
type PageNumber = PageNo of int
|
||||||
|
/// A complete line of script along with metadata.
|
||||||
type ScriptLine = LineNumber * PageNumber * Line
|
type ScriptLine = LineNumber * PageNumber * Line
|
||||||
// TODO: other diffing metadata
|
// TODO: other diffing metadata
|
||||||
|
|
||||||
type SceneName = SceneName of string
|
/// An entire parsed scene.
|
||||||
|
type Scene = ScriptLine list
|
||||||
|
|
||||||
type Scene = SceneName * Character list * ScriptLine list
|
|
||||||
|
module private Parse =
|
||||||
|
open FParsec
|
||||||
|
|
||||||
|
type UserState = {
|
||||||
|
/// The list of all available parsers.
|
||||||
|
/// Never changes within a parsing unit.
|
||||||
|
directiveParsers: seq<Parser<DirectivePrefix, UserState> * Parser<DirectiveInfix option, UserState> * Parser<DirectiveSuffix option, UserState>>
|
||||||
|
}
|
||||||
|
type Parser<'t> = Parser<'t, UserState>
|
||||||
|
|
||||||
|
let ws = unicodeSpaces
|
||||||
|
|
||||||
|
/// Unquoted terms may contain most characters, including whitespace, but
|
||||||
|
/// not those characters used elsewhere in the syntax: square brackets,
|
||||||
|
/// curly brackets, stops, commas, or colons.
|
||||||
|
let unquotedTerm: Parser<_> = noneOf "[]{}.,:`" |> manyChars
|
||||||
|
|
||||||
|
/// Quoted terms can be used to escape from syntactic requirements almost
|
||||||
|
/// anywhere, including inside variable names, character names, directive
|
||||||
|
/// arguments, &c.
|
||||||
|
///
|
||||||
|
/// A quoted term is expressed as arbitrary text between backticks (`).
|
||||||
|
/// They cannot themselves contain backticks or newlines.
|
||||||
|
let quotedTerm: Parser<_> = noneOf "`\n" |> manyChars |> between (pchar '`') (pchar '`')
|
||||||
|
|
||||||
|
/// Text is the concatenation of quoted and unquoted terms.
|
||||||
|
let text: Parser<_> = quotedTerm <|> unquotedTerm |> manyStrings
|
||||||
|
|
||||||
|
/// A variable use is text between curly brackets.
|
||||||
|
let variable: Parser<_> = text |> between (pchar '{') (pchar '}') |>> Variable
|
||||||
|
|
||||||
|
/// A term is either a variable or text, possibly quoted.
|
||||||
|
let term: Parser<_> = (variable |>> VarTerm) <|> (text |>> TextTerm)
|
||||||
|
|
||||||
|
/// A dot-ended directive contains a fixed-prefix directive instance and
|
||||||
|
/// zero or more continued directives separated by commas.
|
||||||
|
let dotDirective prefix infix suffix: Parser<Directive> =
|
||||||
|
// TODO: continued directives
|
||||||
|
pipe4
|
||||||
|
(prefix .>> ws)
|
||||||
|
(((preturn [] .>>. infix) <|> (many term .>>. infix)) .>> ws)
|
||||||
|
(((preturn [] .>>. suffix) <|> (many term .>>. suffix)) .>> ws)
|
||||||
|
(many term .>> ws .>> skipChar '.' .>> ws)
|
||||||
|
(fun pfx (parg, ifx) (iarg, sfx) sarg -> (DirectiveOperator (pfx, ifx, sfx), Arg parg, Arg iarg, Arg sarg))
|
||||||
|
|
||||||
|
/// Parse the speaker of a spoken line, through the colon.
|
||||||
|
let speaker = ws >>. ((skipChar ':' >>% Narrator) <|> (many1 term |>> Speaker .>> ws .>> skipChar ':')) <?> "speaker"
|
||||||
|
@ -7,4 +7,7 @@
|
|||||||
<Compile Include="Syntax.fs" />
|
<Compile Include="Syntax.fs" />
|
||||||
<Compile Include="Program.fs" />
|
<Compile Include="Program.fs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="FParsec" Version="2.0.0-beta2" />
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
Loading…
Reference in New Issue
Block a user