Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
148 changes: 88 additions & 60 deletions crates/workshop-rs/src/actions/parser.rs

Large diffs are not rendered by default.

36 changes: 21 additions & 15 deletions crates/workshop-rs/src/events/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ impl ParseContext<'_> {
pub(crate) fn event_section(&mut self) -> Result<Event> {
self.expect_keyword("event")?;
self.expect(TokenKind::LBrace, "expected '{' after 'event'")?;
let mut lines: Vec<String> = Vec::new();
let mut lines: Vec<(String, Option<Span>)> = Vec::new();
loop {
match self.peek() {
Some(Token {
Expand All @@ -19,16 +19,16 @@ impl ParseContext<'_> {
..
}) => {
self.pos += 1;
lines.push(String::new());
lines.push((String::new(), None));
}
Some(_) => {
let text = self.line_text()?;
lines.push(text);
let (text, span) = self.line_text()?;
lines.push((text, span));
}
None => return Err(self.malformed("unexpected end of input in event", self.eof())),
}
}
let Some(name_line) = lines.first().cloned() else {
let Some((name_line, _)) = lines.first() else {
return Err(self.malformed("event section is empty", self.previous()));
};
let name_line = name_line.trim();
Expand All @@ -43,13 +43,13 @@ impl ParseContext<'_> {
})?;
match entry.id.as_str() {
"global" => {
if lines[1..].iter().any(|line| !line.trim().is_empty()) {
if lines[1..].iter().any(|(line, _)| !line.trim().is_empty()) {
return Err(self.unsupported_event_parameters("global"));
}
Ok(Event::Global)
}
"eachPlayer" => {
if lines[1..].iter().all(|line| line.trim().is_empty()) {
if lines[1..].iter().all(|(line, _)| line.trim().is_empty()) {
return Ok(Event::EachPlayer);
}
let (team, target) = self.event_filters(&lines, "eachPlayer", true)?;
Expand All @@ -75,18 +75,21 @@ impl ParseContext<'_> {
.get(2..)
.unwrap_or(&[])
.iter()
.any(|line| !line.trim().is_empty())
.any(|(line, _)| !line.trim().is_empty())
{
return Err(self.unsupported_event_parameters("subroutine"));
}
let Some(sub_name) = lines.get(1).map(|s| s.trim()) else {
let Some((sub_name, sub_span)) = lines.get(1) else {
return Err(self.malformed(
"subroutine event requires a subroutine name",
self.previous(),
));
};
let id = self.subroutine_by_name(sub_name)?;
Ok(Event::Subroutine(id))
let id = self.subroutine_by_name(sub_name.trim(), *sub_span)?;
Ok(Event::Subroutine {
subroutine: id,
name_span: *sub_span,
})
}
other => Err(WorkshopError::Unsupported {
message: format!("unsupported event '{other}'"),
Expand All @@ -95,21 +98,24 @@ impl ParseContext<'_> {
}
}

pub(crate) fn player_event(&self, lines: &[String], kind: PlayerEventKind) -> Result<Event> {
pub(crate) fn player_event(
&self,
lines: &[(String, Option<Span>)],
kind: PlayerEventKind,
) -> Result<Event> {
let (team, target) = self.event_filters(lines, kind.catalog_id(), false)?;
Ok(Event::Player { kind, team, target })
}

pub(crate) fn event_filters(
&self,
lines: &[String],
lines: &[(String, Option<Span>)],
event_id: &str,
allow_empty: bool,
) -> Result<(EventTeam, EventTarget)> {
let parameters: Vec<&str> = lines[1..]
.iter()
.map(String::as_str)
.map(str::trim)
.map(|(line, _)| line.trim())
.filter(|line| !line.is_empty())
.collect();
if parameters.is_empty() {
Expand Down
2 changes: 1 addition & 1 deletion crates/workshop-rs/src/events/validate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ pub(crate) fn validate_event(
wir::Event::EachPlayer => ("eachPlayer", None),
wir::Event::EachPlayerWithFilters { team, target } => ("eachPlayer", Some((*team, target))),
wir::Event::Player { kind, team, target } => (kind.catalog_id(), Some((*team, target))),
wir::Event::Subroutine(_) => ("subroutine", None),
wir::Event::Subroutine { .. } => ("subroutine", None),
};
if catalog.entry(Kind::Event, id).is_none() {
return Err(WorkshopError::Unknown {
Expand Down
69 changes: 28 additions & 41 deletions crates/workshop-rs/src/frontend/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -364,9 +364,12 @@ impl<'a> ParseContext<'a> {
}

/// Read a text line (tokens until `;`), joining words and dashes into
/// the literal text, and consume the terminating `;`.
pub(crate) fn line_text(&mut self) -> Result<String> {
/// the literal text, and consume the terminating `;`. Returns the text
/// and the span covering the line's content tokens.
pub(crate) fn line_text(&mut self) -> Result<(String, Option<Span>)> {
let mut parts = Vec::new();
let mut start = None;
let mut end = None;
loop {
match self.peek() {
Some(Token {
Expand All @@ -376,50 +379,34 @@ impl<'a> ParseContext<'a> {
self.pos += 1;
break;
}
Some(Token {
kind: TokenKind::Word(word),
..
}) => {
parts.push(word.clone());
self.pos += 1;
}
Some(Token {
kind: TokenKind::Op(op),
..
}) if op == "-" => {
parts.push("-".to_string());
self.pos += 1;
}
Some(Token {
kind: TokenKind::Number { value, .. },
..
}) => {
parts.push(value.to_string());
self.pos += 1;
}
Some(Token {
kind: TokenKind::Dot,
..
}) => {
parts.push(".".to_string());
Some(token) => {
start.get_or_insert(token.start);
end = Some(token.end);
match &token.kind {
TokenKind::Word(word) => parts.push(word.clone()),
TokenKind::Op(op) if op == "-" => parts.push("-".to_string()),
TokenKind::Number { value, .. } => parts.push(value.to_string()),
TokenKind::Dot => parts.push(".".to_string()),
TokenKind::Colon => parts.push(":".to_string()),
_ => return Err(self.malformed("expected a text line", &token)),
}
self.pos += 1;
}
Some(Token {
kind: TokenKind::Colon,
..
}) => {
parts.push(":".to_string());
self.pos += 1;
None => {
return Err(self.malformed("unexpected end of input in line", self.eof()));
}
Some(token) => return Err(self.malformed("expected a text line", &token)),
None => return Err(self.malformed("unexpected end of input in line", self.eof())),
}
}
Ok(parts
.join(" ")
.replace(" .", ".")
.replace(". ", ".")
.replace(" : ", ":"))
Ok((
parts
.join(" ")
.replace(" .", ".")
.replace(". ", ".")
.replace(" : ", ":"),
start
.zip(end)
.map(|(start, end)| Span::new(self.file(), start, end)),
))
}

/// Consume a known keyword phrase, verifying its spelling.
Expand Down
5 changes: 4 additions & 1 deletion crates/workshop-rs/src/output/roundtrip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,10 @@ fn event_equivalent(
target: target_b,
},
) => kind_a == kind_b && team_a == team_b && target_a == target_b,
(wir::Event::Subroutine(sa), wir::Event::Subroutine(sb)) => {
(
wir::Event::Subroutine { subroutine: sa, .. },
wir::Event::Subroutine { subroutine: sb, .. },
) => {
let name_a = a.subroutines.get(*sa).map(|s| s.name.as_str());
let name_b = b.subroutines.get(*sb).map(|s| s.name.as_str());
name_a == name_b
Expand Down
Loading
Loading