Skip to content

Commit e708427

Browse files
Ph0enixKMCopilot
andauthored
Add from between positions method (#78)
* feat: add from between positions * feat: update comment Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * feat: bump version * feat: update description * feat: bump version * feat: update readme comment * feat: update comment Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent 9007f18 commit e708427

8 files changed

Lines changed: 57 additions & 22 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "heraclitus-compiler"
3-
version = "1.8.3"
3+
version = "1.9.0"
44
edition = "2021"
55
description = "Compiler frontend for developing great programming languages"
66
license = "MIT"

README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,12 @@ let tokens = cc.tokenize()?;
4545

4646
# Change log 🚀
4747

48-
## Version 1.8.2
48+
## Version 1.9.0
49+
### Feat:
50+
- Added `PositionInfo::from_between_positions` method for creating a `PositionInfo` from two positions
51+
- Breaking Change: `PositionInfo::new` and `PositionInfo::at_pos` now accept `start` parameter which determines the index of the first character in the source code
52+
53+
## Version 1.8.3
4954
### Feat:
5055
- Introduced `ignore_escaped` property on regions to prevent from escaping region boundaries
5156

src/compiling/failing/logger.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -240,8 +240,8 @@ mod test {
240240
// Uncomment to see the error message
241241
sleep(Duration::from_secs(1));
242242
let trace = [
243-
PositionInfo::at_pos(Some("/path/to/bar".to_string()), (3, 4), 10),
244-
PositionInfo::at_pos(Some("/path/to/foo".to_string()), (2, 9), 24),
243+
PositionInfo::at_pos(Some("/path/to/bar".to_string()), (3, 4), 10, 10),
244+
PositionInfo::at_pos(Some("/path/to/foo".to_string()), (2, 9), 24, 24),
245245
];
246246
super::Logger::new(MessageType::Error, &trace)
247247
.header(MessageType::Error)
@@ -258,7 +258,7 @@ mod test {
258258
// Uncomment to see the error message
259259
sleep(Duration::from_secs(1));
260260
let trace = [
261-
PositionInfo::at_pos(Some("/path/to/foo".to_string()), (2, 6), 1)
261+
PositionInfo::at_pos(Some("/path/to/foo".to_string()), (2, 6), 5, 1)
262262
];
263263
super::Logger::new(MessageType::Error, &trace)
264264
.header(MessageType::Error)

src/compiling/failing/message.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ pub enum MessageType {
3131
/// ```should_panic
3232
/// # use heraclitus_compiler::prelude::*;
3333
/// # let path = Some(format!("path/to/file"));
34-
/// # let position = PositionInfo::at_pos(path.clone(), (0, 0), 0);
34+
/// # let position = PositionInfo::at_pos(path.clone(), (0, 0), 0, 0);
3535
/// # let guess = "type";
3636
/// # let code = Some(format!("code"));
3737
/// # let mut meta = DefaultMetadata::new(vec![], path, code);
@@ -195,7 +195,7 @@ mod test {
195195
"... further code",
196196
].join("\n"));
197197
let path = Some(format!("path/to/file"));
198-
let position = PositionInfo::at_pos(path.clone(), (2, 1), 4);
198+
let position = PositionInfo::at_pos(path.clone(), (2, 1), 14, 4);
199199
let guess = "type";
200200
let mut meta = DefaultMetadata::new(vec![], path, code);
201201
Message::new_err_at_position(&mut meta, position)

src/compiling/failing/position_info.rs

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ pub struct PositionInfo {
2828
pub path: Option<String>,
2929
/// Location of this error
3030
pub position: Position,
31+
/// Character index in the source file where this position starts
32+
pub start: usize,
3133
/// Length of the token
3234
pub len: usize,
3335
/// Additional information
@@ -36,10 +38,11 @@ pub struct PositionInfo {
3638

3739
impl PositionInfo {
3840
/// Create a new error from scratch
39-
pub fn new(meta: &impl Metadata, position: Position, len: usize) -> Self {
41+
pub fn new(meta: &impl Metadata, position: Position, start: usize, len: usize) -> Self {
4042
let info = PositionInfo {
4143
position,
4244
path: meta.get_path(),
45+
start,
4346
len,
4447
data: None
4548
};
@@ -51,17 +54,19 @@ impl PositionInfo {
5154
let info = PositionInfo {
5255
path: meta.get_path(),
5356
position: Position::EOF,
57+
start: 0,
5458
len: 0,
5559
data: None
5660
};
5761
info.updated_pos(meta)
5862
}
5963

6064
/// Create a new error at given position
61-
pub fn at_pos(path: Option<String>, (row, col): (usize, usize), len: usize) -> Self {
65+
pub fn at_pos(path: Option<String>, (row, col): (usize, usize), start: usize, len: usize) -> Self {
6266
PositionInfo {
6367
path,
6468
position: Position::Pos(row, col),
69+
start,
6570
len,
6671
data: None
6772
}
@@ -94,7 +99,7 @@ impl PositionInfo {
9499
/// and error once you finished parsing the entire expression
95100
pub fn from_token(meta: &impl Metadata, token_opt: Option<Token>) -> Self {
96101
match token_opt {
97-
Some(token) => PositionInfo::at_pos(meta.get_path(), token.pos, token.word.chars().count()),
102+
Some(token) => PositionInfo::at_pos(meta.get_path(), token.pos, token.start, token.word.chars().count()),
98103
None => PositionInfo::at_eof(meta)
99104
}
100105
}
@@ -106,15 +111,29 @@ impl PositionInfo {
106111
pub fn from_between_tokens(meta: &impl Metadata, begin: Option<Token>, end: Option<Token>) -> Self {
107112
if let Some(begin) = begin {
108113
let (row, col) = begin.pos;
109-
let end = end.map_or(usize::max_value(), |tok| tok.start);
110-
let len = end - begin.start;
111-
PositionInfo::at_pos(meta.get_path(), (row, col), len)
112-
}
113-
else {
114+
let end_pos = end.map_or(usize::max_value(), |tok| tok.start);
115+
let len = end_pos - begin.start;
116+
PositionInfo::at_pos(meta.get_path(), (row, col), begin.start, len)
117+
} else {
114118
PositionInfo::from_metadata(meta)
115119
}
116120
}
117121

122+
/// Create an error at position between two PositionInfo objects
123+
///
124+
/// This function is used to create an error between two positions
125+
/// which can be used to express an error in a specific range
126+
pub fn from_between_positions(meta: &impl Metadata, begin: PositionInfo, end: PositionInfo) -> Self {
127+
let start_index = begin.start;
128+
let end_index = end.start + end.len;
129+
let len = end_index - start_index;
130+
if let Position::Pos(row, col) = begin.position {
131+
PositionInfo::at_pos(meta.get_path(), (row, col), start_index, len)
132+
} else {
133+
PositionInfo::at_eof(meta)
134+
}
135+
}
136+
118137
/// Attach additional data in form of a string
119138
pub fn data<T: AsRef<str>>(mut self, data: T) -> Self {
120139
self.data = Some(data.as_ref().to_string());
@@ -177,7 +196,7 @@ mod test {
177196

178197
#[test]
179198
fn test_position_info() {
180-
let pos = PositionInfo::at_pos(Some("test".to_string()), (1, 1), 1);
199+
let pos = PositionInfo::at_pos(Some("test".to_string()), (1, 1), 0, 1);
181200
assert_eq!(pos.get_path(), "test");
182201
assert_eq!(pos.get_pos_by_code("test"), (1, 1));
183202
}
@@ -191,4 +210,15 @@ mod test {
191210
let pos = PositionInfo::from_between_tokens(&mut meta, Some(begin.clone()), Some(end.clone()));
192211
assert_eq!(pos.len, end.start - begin.start);
193212
}
213+
214+
#[test]
215+
fn test_position_info_between_positions() {
216+
let begin = PositionInfo::at_pos(Some("test".to_string()), (1, 1), 0, 5);
217+
let end = PositionInfo::at_pos(Some("test".to_string()), (1, 10), 9, 3);
218+
let mut meta = DefaultMetadata::new(vec![], None, Some("begin to end".to_string()));
219+
let pos = PositionInfo::from_between_positions(&mut meta, begin.clone(), end.clone());
220+
assert_eq!(pos.start, 0);
221+
assert_eq!(pos.len, 12);
222+
assert_eq!(pos.get_pos_by_code("begin to end"), (1, 1));
223+
}
194224
}

src/compiling/lexing/lexer.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ impl Lexer {
251251
let pos = lex_state.reader.get_position();
252252
return Err((
253253
LexerErrorType::Singleline,
254-
PositionInfo::at_pos(self.path.clone(), pos, 0)
254+
PositionInfo::at_pos(self.path.clone(), pos, lex_state.reader.get_index(), 0)
255255
.data(region.name.clone()),
256256
));
257257
}
@@ -316,10 +316,10 @@ impl Lexer {
316316
}
317317
self.add_word(&mut lex_state);
318318
// If some region exists that was not closed
319-
if let Err((pos, region)) = lex_state.region_handler.is_region_closed(&lex_state.reader) {
319+
if let Err((pos, start, region)) = lex_state.region_handler.is_region_closed(&lex_state.reader) {
320320
return Err((
321321
LexerErrorType::Unclosed,
322-
PositionInfo::at_pos(self.path.clone(), pos, 0).data(region.name),
322+
PositionInfo::at_pos(self.path.clone(), pos, start, 0).data(region.name),
323323
));
324324
}
325325

src/compiling/lexing/region_handler.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,11 @@ impl RegionHandler {
3535
// Error if after code lexing
3636
// some region was left unclosed
3737
#[inline]
38-
pub fn is_region_closed(&self, reader: &Reader) -> Result<(), ((usize, usize), Region)> {
38+
pub fn is_region_closed(&self, reader: &Reader) -> Result<(), ((usize, usize), usize, Region)> {
3939
if let Some(region) = self.region_stack.last() {
4040
if !region.allow_unclosed_region {
4141
let pos = reader.get_position();
42-
return Err((pos, region.clone()));
42+
return Err((pos, reader.get_index(), region.clone()));
4343
}
4444
}
4545
Ok(())

0 commit comments

Comments
 (0)