Skip to content

Commit

Permalink
Interpolate #defined values
Browse files Browse the repository at this point in the history
  • Loading branch information
dannymcgee committed May 16, 2024
1 parent 449071e commit 8703bfb
Show file tree
Hide file tree
Showing 9 changed files with 280 additions and 77 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ itertools = "0.12"
lsp-server = "0.7"
lsp-types = "0.95"
naga = { version = "0.19", features = ["wgsl-in"] }
regex = "1.10.4"
ropey = "1.5"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
Expand Down
9 changes: 7 additions & 2 deletions packages/server/src/documents/intake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@ use crate::{

use super::{
DocumentUri, DocumentsMap, ImportedSymbols, InactiveRanges, ModulePathsMap, ParseErrors,
PendingPreprocessing, PendingReads, PruneErrors, WgslAst, WgslComments, WgslPrunedSource,
WgslRope, WgslScopes, WgslSource, WgslSourceMap, WgslTokens,
PendingPreprocessing, PendingReads, PruneErrors, ReplacementErrors, SourceReplacements,
WgslAst, WgslComments, WgslPrunedSource, WgslRope, WgslScopes, WgslSource, WgslSourceMap,
WgslTokens,
};

#[derive(Bundle)]
Expand Down Expand Up @@ -178,6 +179,8 @@ fn spawn_preprocessed(
source_map,
inactive_spans,
prune_errors,
replacements,
replacement_errors,
} = pre::prune(&uri, source, defs);

let mut parser = ParseStream::from(&pruned);
Expand Down Expand Up @@ -208,6 +211,8 @@ fn spawn_preprocessed(
WgslComments(comments),
ParseErrors(parse_errors),
PruneErrors(prune_errors),
SourceReplacements(replacements),
ReplacementErrors(replacement_errors),
WgslAst(tree),
WgslScopes(scopes),
TokenReferences::default(),
Expand Down
12 changes: 11 additions & 1 deletion packages/server/src/documents/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use crate::{
config::Config,
ipc::{notify, Ipc},
lsp_extensions::{UnreadDependency, UnreadDependencyParams},
pre::{self, PruneResult, SourceMap},
pre::{self, PruneResult, SourceMap, SourceReplacement},
utils,
workspace::Workspace,
};
Expand Down Expand Up @@ -104,6 +104,12 @@ pub struct ParseErrors(pub Vec<SpannedError>);
#[derive(Component, Debug, Deref, DerefMut)]
pub struct PruneErrors(pub Vec<SpannedError>);

#[derive(Component, Debug, Deref, DerefMut)]
pub struct SourceReplacements(pub Vec<SourceReplacement>);

#[derive(Component, Debug, Deref, DerefMut)]
pub struct ReplacementErrors(pub Vec<SpannedError>);

#[derive(Component, Clone, Debug, Deref, DerefMut)]
pub struct WgslRope(pub Rope);

Expand Down Expand Up @@ -210,6 +216,8 @@ fn update_documents(
source_map,
inactive_spans,
prune_errors,
replacements,
replacement_errors,
} = pre::prune(uri, source, defs);

let was_pruned = pruned != original;
Expand Down Expand Up @@ -263,6 +271,8 @@ fn update_documents(
WgslSourceMap(source_map),
InactiveRanges(inactive_spans),
PruneErrors(prune_errors),
SourceReplacements(replacements),
ReplacementErrors(replacement_errors),
));
}
}
Expand Down
191 changes: 191 additions & 0 deletions packages/server/src/pre/interpolation.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
use std::{collections::VecDeque, sync::OnceLock};

use bevy_utils::HashMap;
use gramatika::{ArcStr, Position, SpannedError, Substr};
use parser::Span;
use regex::{CaptureLocations, Regex};

static BRACED_PAT: OnceLock<Regex> = OnceLock::new();
static UNBRACED_PAT: OnceLock<Regex> = OnceLock::new();

fn braced_pat() -> &'static Regex {
BRACED_PAT.get_or_init(|| Regex::new(r"#\{([a-zA-Z_][a-zA-Z0-9_]*)\}").unwrap())
}

fn unbraced_pat() -> &'static Regex {
UNBRACED_PAT.get_or_init(|| Regex::new(r"#([a-zA-Z_][a-zA-Z0-9_]*)").unwrap())
}

#[derive(Clone, Debug)]
pub enum SourceSlice {
Verbatim(Substr, Span),
Replacement(SourceReplacement),
}

#[derive(Clone, Debug)]
pub struct SourceReplacement {
pub original: (Substr, Span),
pub replacement: String,
}

pub(super) struct InterpolationStream<'a> {
full_source: ArcStr,
source: &'a [(Substr, Span)],
defs: &'a HashMap<String, String>,
index: usize,
buffer: VecDeque<SourceSlice>,
braced_locs: CaptureLocations,
unbraced_locs: CaptureLocations,
errors: Vec<SpannedError>,
}

impl<'a> InterpolationStream<'a> {
pub fn new(
full_source: ArcStr,
source: &'a [(Substr, Span)],
defs: &'a HashMap<String, String>,
) -> Self {
Self {
full_source,
source,
defs,
index: 0,
buffer: VecDeque::new(),
braced_locs: braced_pat().capture_locations(),
unbraced_locs: unbraced_pat().capture_locations(),
errors: vec![],
}
}

pub fn into_inner(self) -> Vec<SpannedError> {
self.errors
}
}

impl<'a> Iterator for InterpolationStream<'a> {
type Item = SourceSlice;

fn next(&mut self) -> Option<Self::Item> {
use SourceSlice::*;

if !self.buffer.is_empty() {
return self.buffer.pop_front();
}

if self.index >= self.source.len() {
return None;
}

let (mut substr, mut span) = self.source[self.index].clone();

while let Some(m) = None
.or_else(|| braced_pat().captures_read(&mut self.braced_locs, &substr))
.or_else(|| unbraced_pat().captures_read(&mut self.unbraced_locs, &substr))
{
// Gather the data we need about the match before going any further
// so we don't keep the `substr` borrowed for longer than necessary
let start = m.start();
let (cap_start, cap_end) = self
.braced_locs
.get(1)
.or_else(|| self.unbraced_locs.get(1))
.unwrap();

let symbol = substr.substr(cap_start..cap_end);
let match_len = m.len();

// Slice off the text before the match as verbatim
if let Some((before, before_span)) = split_at(&substr, span, start) {
self.buffer.push_back(Verbatim(before, before_span));

// Buffalo buffalo Buffalo buffalo...
substr = substr.substr(start..);
span.start = before_span.end;
}

// Substr and Span for the match range
let replaced_slice = substr.substr(..match_len);
let replaced_span = Span {
start: span.start,
end: Position {
line: span.start.line,
character: span.start.character + match_len,
},
};

// Ignore other directives that may be part of the pruned text
if symbol == "import" || symbol == "define_import_path" {
self.buffer
.push_back(Verbatim(replaced_slice, replaced_span));
}
// Emit an error if the symbol is undefined and push the slice as verbatim
else if !self.defs.contains_key(symbol.as_str()) {
self.errors.push(SpannedError {
message: format!("`{symbol}` is undefined"),
source: self.full_source.clone(),
span: Some(replaced_span),
});

self.buffer
.push_back(Verbatim(replaced_slice.clone(), replaced_span));
}
// Push the #define replaement
else {
self.buffer.push_back(Replacement(SourceReplacement {
original: (replaced_slice, replaced_span),
replacement: self.defs[symbol.as_str()].clone(),
}));
}

// Advance the "cursor" to the position just past the match
substr = substr.substr(match_len..);
span = Span {
start: replaced_span.end,
end: span.end,
};
}

// We're finished processing interpolation matches in this slice
self.index += 1;

// Handle the remainder of the slice after the last match
if !substr.is_empty() {
if self.buffer.is_empty() {
return Some(Verbatim(substr, span));
} else {
self.buffer.push_back(Verbatim(substr, span));
}
}

self.buffer.pop_front()
}
}

fn split_at(substr: &Substr, span: Span, idx: usize) -> Option<(Substr, Span)> {
if idx == 0 {
return None;
}

let before = substr.substr(..idx);

let mut line_inc = 0_usize;
let mut remaining = before.as_str();
while let Some(i) = remaining.find('\n') {
line_inc += 1;
remaining = &remaining[i + 1..];
}
let char_inc = remaining.len();

let start = span.start;
let mut end = start;

end.line += line_inc;

if line_inc > 0 {
end.character = char_inc;
} else {
end.character += char_inc;
}

Some((before, Span { start, end }))
}
21 changes: 11 additions & 10 deletions packages/server/src/pre/mapping.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,17 @@ impl SourceMap {
return Some(needle);
}

let offset = compute_offset(dest.start, src.start);
let offset = compute_offset(needle, dest.start, src.start);
Some(apply_offset(needle, offset))
}
None => {
let start_idx = self.find_index_by_dest_pos(needle.start)?;
let (dest_start, src_start) = self.0[start_idx];
let start_offset = compute_offset(dest_start.start, src_start.start);
let start_offset = compute_offset(needle, dest_start.start, src_start.start);

let end_idx = self.find_index_by_dest_pos(needle.end)?;
let (dest_end, src_end) = self.0[end_idx];
let end_offset = compute_offset(dest_end.end, src_end.end);
let end_offset = compute_offset(needle, dest_end.end, src_end.end);

Some(apply_offsets(needle, start_offset, end_offset))
}
Expand All @@ -41,17 +41,17 @@ impl SourceMap {
return Some(needle);
}

let offset = compute_offset(src.start, dest.start);
let offset = compute_offset(needle, src.start, dest.start);
Some(apply_offset(needle, offset))
}
None => {
let start_idx = self.find_index_by_src_pos(needle.start)?;
let (dest_start, src_start) = self.0[start_idx];
let start_offset = compute_offset(src_start.start, dest_start.start);
let start_offset = compute_offset(needle, src_start.start, dest_start.start);

let end_idx = self.find_index_by_src_pos(needle.end)?;
let (dest_end, src_end) = self.0[end_idx];
let end_offset = compute_offset(src_end.end, dest_end.end);
let end_offset = compute_offset(needle, src_end.end, dest_end.end);

Some(apply_offsets(needle, start_offset, end_offset))
}
Expand Down Expand Up @@ -103,11 +103,12 @@ impl SourceMap {
}
}

fn compute_offset(from: Position, to: Position) -> (isize, isize) {
fn compute_offset(needle: Span, from: Position, to: Position) -> (isize, isize) {
let line = (to.line as isize) - (from.line as isize);
let character = match line {
0 => (to.character as isize) - (from.character as isize),
_ => 0,
let character = if line == 0 && needle.start.line == from.line {
(to.character as isize) - (from.character as isize)
} else {
0
};

(line, character)
Expand Down
9 changes: 7 additions & 2 deletions packages/server/src/pre/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ use parser::{

use crate::pre::pruner::Pruner;

pub use self::mapping::SourceMap;
pub use self::{interpolation::SourceReplacement, mapping::SourceMap};

mod interpolation;
mod interpreter;
mod mapping;
mod pruner;
Expand All @@ -20,6 +21,8 @@ pub struct PruneResult {
pub source_map: SourceMap,
pub inactive_spans: Vec<Span>,
pub prune_errors: Vec<SpannedError>,
pub replacements: Vec<SourceReplacement>,
pub replacement_errors: Vec<SpannedError>,
}

pub fn prune(uri: &Url, source: ArcStr, defs: &HashMap<String, String>) -> PruneResult {
Expand All @@ -30,7 +33,7 @@ pub fn prune(uri: &Url, source: ArcStr, defs: &HashMap<String, String>) -> Prune
let mut pruner = Pruner::new(source, defs.clone());
parsed.walk(&mut pruner);

let (pruned, source_map) = pruner.write_output_mapped();
let (pruned, source_map, replacements, replacement_errors) = pruner.write_output();

eprintln!("Pruned source for {uri}:");
for (idx, line) in pruned.lines().enumerate() {
Expand All @@ -46,5 +49,7 @@ pub fn prune(uri: &Url, source: ArcStr, defs: &HashMap<String, String>) -> Prune
source_map,
inactive_spans: pruner.inactive_spans,
prune_errors: pruner.errors,
replacements,
replacement_errors,
}
}
Loading

0 comments on commit 8703bfb

Please sign in to comment.