Skip to content
Open
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
14 changes: 14 additions & 0 deletions rust/ruby-prism-sys/build/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ fn main() {

let ruby_build_path = prism_lib_path();
let ruby_include_path = prism_include_path();
emit_rerun_hints(&ruby_include_path);

// Tell cargo/rustc that we want to link against `libprism.a`.
println!("cargo:rustc-link-lib=static=prism");
Expand All @@ -23,6 +24,19 @@ fn main() {
write_bindings(&bindings);
}

fn emit_rerun_hints(ruby_include_path: &Path) {
println!("cargo:rerun-if-env-changed=PRISM_INCLUDE_DIR");
println!("cargo:rerun-if-env-changed=PRISM_LIB_DIR");
println!("cargo:rerun-if-changed={}", ruby_include_path.display());

if let Some(project_root) = ruby_include_path.parent() {
let src_path = project_root.join("src");
if src_path.exists() {
println!("cargo:rerun-if-changed={}", src_path.display());
}
}
}

/// Gets the path to project files (`libprism*`) at `[root]/build/`.
///
fn prism_lib_path() -> PathBuf {
Expand Down
44 changes: 43 additions & 1 deletion rust/ruby-prism/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ mod tests {
}

#[test]
fn location_test() {
fn location_slice_test() {
let source = "111 + 222 + 333";
let result = parse(source.as_ref());

Expand Down Expand Up @@ -181,6 +181,48 @@ mod tests {
assert_eq!(slice, "222");
}

#[test]
fn location_line_column_test() {
let source = "first\nsecond\nthird";
let result = parse(source.as_ref());

let node = result.node();
let program = node.as_program_node().unwrap();
let statements = program.statements().body();
let mut iter = statements.iter();

let _first = iter.next().unwrap();
let second = iter.next().unwrap();
let third = iter.next().unwrap();

let second_loc = second.location();
assert_eq!(second_loc.start_line(), 2);
assert_eq!(second_loc.end_line(), 2);
assert_eq!(second_loc.start_column(), 0);
assert_eq!(second_loc.end_column(), 6);

let third_loc = third.location();
assert_eq!(third_loc.start_line(), 3);
assert_eq!(third_loc.end_line(), 3);
assert_eq!(third_loc.start_column(), 0);
assert_eq!(third_loc.end_column(), 5);
}

#[test]
fn location_chop_test() {
let result = parse(b"foo");
let mut location = result.node().as_program_node().unwrap().location();

assert_eq!(location.chop().as_slice(), b"fo");
assert_eq!(location.chop().chop().chop().as_slice(), b"");

// Check that we don't go negative.
for _ in 0..10 {
location = location.chop();
}
assert_eq!(location.as_slice(), b"");
}

#[test]
fn visitor_test() {
use super::{visit_interpolated_regular_expression_node, visit_regular_expression_node, InterpolatedRegularExpressionNode, RegularExpressionNode, Visit};
Expand Down
63 changes: 63 additions & 0 deletions rust/ruby-prism/src/parse_result/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,69 @@ impl<'pr> Location<'pr> {
})
}
}

/// Returns a new location that is the result of chopping off the last byte.
#[must_use]
pub const fn chop(&self) -> Self {
Location {
parser: self.parser,
start: self.start,
length: if self.length == 0 { 0 } else { self.length - 1 },
marker: std::marker::PhantomData,
}
}
}

impl Location<'_> {
/// Returns the line number where this location starts.
#[must_use]
pub fn start_line(&self) -> i32 {
self.line_column(self.start).0
}

/// Returns the column number in bytes where this location starts from the
/// start of the line.
#[must_use]
pub fn start_column(&self) -> u32 {
self.line_column(self.start).1
}

/// Returns the line number where this location ends.
#[must_use]
pub fn end_line(&self) -> i32 {
self.line_column(self.end()).0
}

/// Returns the column number in bytes where this location ends from the
/// start of the line.
#[must_use]
pub fn end_column(&self) -> u32 {
self.line_column(self.end()).1
}

/// Returns the line and column number for the given byte offset, using
/// binary search on the line offsets array.
fn line_column(&self, cursor: u32) -> (i32, u32) {
// SAFETY: We read the line_offsets and start_line from the parser,
// which is valid for the lifetime of this Location.
unsafe {
let parser = self.parser.as_ptr();
let list = &(*parser).line_offsets;
let start_line = (*parser).start_line;
let offsets = std::slice::from_raw_parts(list.offsets, list.size);

let index = offsets.partition_point(|&offset| offset <= cursor);

#[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
if index > 0 && offsets[index - 1] == cursor {
((index - 1) as i32 + start_line, 0)
} else {
let line = (index - 1) as i32 + start_line;
let column = cursor - offsets[index - 1];
(line, column)
}
}
}
}

impl std::fmt::Debug for Location<'_> {
Expand Down