use super::*;
pub(crate) fn parse_global_scene_scope(
pair: Pair<Rule>,
last_name: &mut Option<String>,
filename: &str,
) -> Result<GlobalSceneScope, ParseError> {
let span = Span::from(&pair.as_span());
let mut scene_name = String::new();
let mut is_continuation = false;
let mut attrs = Vec::new();
let mut words = Vec::new();
let mut actors = Vec::new();
let mut code_blocks = Vec::new();
let mut local_scenes = Vec::new();
let mut next_actor_number: u32 = 0;
for inner in pair.into_inner() {
match inner.as_rule() {
Rule::global_scene_start => {
let (name, cont) = parse_global_scene_start(inner, last_name, filename)?;
scene_name = name.clone();
is_continuation = cont;
*last_name = Some(name);
}
Rule::global_scene_attr_line => {
for attr_pair in inner.into_inner() {
if attr_pair.as_rule() == Rule::attr {
attrs.push(parse_attr(attr_pair)?);
}
}
}
Rule::global_scene_word_line => {
for kw_pair in inner.into_inner() {
if kw_pair.as_rule() == Rule::key_words {
words.push(parse_key_words(kw_pair)?);
}
}
}
Rule::scene_actors_line => {
let items = parse_scene_actors_line(inner, &mut next_actor_number)?;
actors.extend(items);
}
Rule::code_block => {
code_blocks.push(parse_code_block(inner)?);
}
Rule::local_start_scene_scope => {
local_scenes.push(parse_local_start_scene_scope(inner)?);
}
Rule::local_scene_scope => {
local_scenes.push(parse_local_scene_scope(inner)?);
}
_ => {}
}
}
Ok(GlobalSceneScope {
name: scene_name,
is_continuation,
attrs,
words,
actors,
code_blocks,
local_scenes,
span,
})
}
pub(crate) fn parse_global_scene_start(
pair: Pair<Rule>,
last_name: &Option<String>,
filename: &str,
) -> Result<(String, bool), ParseError> {
for inner in pair.into_inner() {
match inner.as_rule() {
Rule::global_scene_line => {
for scene_inner in inner.into_inner() {
if scene_inner.as_rule() == Rule::id {
return Ok((scene_inner.as_str().to_string(), false));
}
}
}
Rule::global_scene_continue_line => {
if let Some(name) = last_name {
return Ok((name.clone(), true));
} else {
let span = inner.as_span();
let (line, col) = span.start_pos().line_col();
return Err(ParseError::SyntaxError {
file: filename.to_string(),
line,
column: col,
message: "Unnamed global scene at start of file. A named global scene must appear before any unnamed scenes.".to_string(),
});
}
}
_ => {}
}
}
Ok((String::new(), false))
}
pub(crate) fn parse_scene_actors_line(
pair: Pair<Rule>,
next_number: &mut u32,
) -> Result<Vec<SceneActorItem>, ParseError> {
let mut items = Vec::new();
for inner in pair.into_inner() {
if inner.as_rule() == Rule::actors_item {
let item = parse_actors_item(inner, next_number)?;
items.push(item);
}
}
Ok(items)
}
pub(crate) fn parse_actors_item(
pair: Pair<Rule>,
next_number: &mut u32,
) -> Result<SceneActorItem, ParseError> {
let span = Span::from(&pair.as_span());
let mut name = String::new();
let mut explicit_number: Option<u32> = None;
for inner in pair.into_inner() {
match inner.as_rule() {
Rule::id => {
name = inner.as_str().to_string();
}
Rule::digit_id => {
let normalized = normalize_number_str(inner.as_str());
explicit_number = normalized.parse::<u32>().ok();
}
_ => {}
}
}
let number = explicit_number.unwrap_or(*next_number);
*next_number = number.saturating_add(1);
Ok(SceneActorItem { name, number, span })
}
pub(crate) fn parse_local_start_scene_scope(
pair: Pair<Rule>,
) -> Result<LocalSceneScope, ParseError> {
let span = Span::from(&pair.as_span());
let mut scope = LocalSceneScope::start();
scope.span = span;
for inner in pair.into_inner() {
parse_local_scene_item(inner, &mut scope)?;
}
Ok(scope)
}
pub(crate) fn parse_local_scene_scope(pair: Pair<Rule>) -> Result<LocalSceneScope, ParseError> {
let span = Span::from(&pair.as_span());
let mut scope = LocalSceneScope::start();
scope.span = span;
for inner in pair.into_inner() {
if inner.as_rule() == Rule::local_scene_line {
for scene_inner in inner.into_inner() {
if scene_inner.as_rule() == Rule::id {
scope.name = Some(scene_inner.as_str().to_string());
break;
}
}
} else {
parse_local_scene_item(inner, &mut scope)?;
}
}
Ok(scope)
}
fn parse_local_scene_item(
inner: Pair<Rule>,
scope: &mut LocalSceneScope,
) -> Result<(), ParseError> {
match inner.as_rule() {
Rule::var_set_local
| Rule::var_set_global
| Rule::var_set_none
| Rule::var_set_property => {
scope
.items
.push(LocalSceneItem::VarSet(parse_var_set(inner)?));
}
Rule::call_scene => {
scope
.items
.push(LocalSceneItem::CallScene(parse_call_scene(inner)?));
}
Rule::action_line => {
scope
.items
.push(LocalSceneItem::ActionLine(parse_action_line(inner)?));
}
Rule::continue_action_line => {
scope
.items
.push(LocalSceneItem::ContinueAction(parse_continue_action_line(
inner,
)?));
}
Rule::cue_cmd_line => {
scope
.items
.push(LocalSceneItem::CueCommand(parse_cue_cmd_line(inner)?));
}
Rule::choice_line => {
scope
.items
.push(LocalSceneItem::Choice(parse_choice_line(inner)?));
}
Rule::code_block => {
scope.code_blocks.push(parse_code_block(inner)?);
}
_ => {}
}
Ok(())
}
pub(crate) fn parse_choice_line(pair: Pair<Rule>) -> Result<ChoiceNode, ParseError> {
let span = Span::from(&pair.as_span());
let mut target = String::new();
let mut label = None;
for inner in pair.into_inner() {
match inner.as_rule() {
Rule::id => {
target = inner.as_str().to_string();
}
Rule::choice_label => {
for label_inner in inner.into_inner() {
if label_inner.as_rule() == Rule::string_contents {
label = Some(label_inner.as_str().to_string());
}
}
}
_ => {}
}
}
Ok(ChoiceNode {
target,
label,
span,
})
}
pub(crate) fn parse_cue_cmd_line(pair: Pair<Rule>) -> Result<CueCommandNode, ParseError> {
let span = Span::from(&pair.as_span());
let mut command = String::new();
let mut scope = None;
let mut args = Vec::new();
for inner in pair.into_inner() {
match inner.as_rule() {
Rule::cue_cmd_name => {
command = inner.as_str().to_string();
}
Rule::cue_cmd_scope => {
scope = Some(parse_cue_cmd_scope(inner)?);
}
Rule::cue_cmd_args => {
args = parse_cue_cmd_args(inner)?;
}
_ => {}
}
}
Ok(CueCommandNode {
command,
scope,
args,
span,
})
}
fn parse_cue_cmd_scope(pair: Pair<Rule>) -> Result<ScopedName, ParseError> {
let span = Span::from(&pair.as_span());
for inner in pair.into_inner() {
if inner.as_rule() == Rule::cue_scoped_ident {
let raw = inner.as_str();
if let Some(colon_pos) = raw.find([':', ':']) {
let actor = &raw[..colon_pos];
if let Some(colon_char) = raw[colon_pos..].chars().next() {
let name = &raw[colon_pos + colon_char.len_utf8()..];
return Ok(ScopedName {
actor: Some(actor.to_string()),
name: name.to_string(),
span,
});
}
} else {
return Ok(ScopedName {
actor: None,
name: raw.to_string(),
span,
});
}
}
}
Ok(ScopedName {
actor: None,
name: String::new(),
span,
})
}
fn parse_cue_cmd_args(pair: Pair<Rule>) -> Result<Vec<CueArgToken>, ParseError> {
let mut tokens = Vec::new();
for inner in pair.into_inner() {
match inner.as_rule() {
Rule::cue_arg_at_ref => {
for id_pair in inner.into_inner() {
if id_pair.as_rule() == Rule::id {
tokens.push(CueArgToken::AtRef(id_pair.as_str().to_string()));
}
}
}
Rule::number_literal => {
let normalized = normalize_number_str(inner.as_str());
if normalized.contains('.') {
tokens.push(CueArgToken::Float(normalized.parse().unwrap_or(0.0)));
} else {
tokens.push(CueArgToken::Integer(normalized.parse().unwrap_or(0)));
}
}
Rule::string_contents => {
tokens.push(CueArgToken::StringLiteral(inner.as_str().to_string()));
}
Rule::string_blank => {
tokens.push(CueArgToken::StringLiteral(String::new()));
}
Rule::cue_arg_id => {
tokens.push(CueArgToken::Ident(inner.as_str().to_string()));
}
_ => {}
}
}
Ok(tokens)
}