lady_deirdre/lexis/
position.rs

1////////////////////////////////////////////////////////////////////////////////
2// This file is part of "Lady Deirdre", a compiler front-end foundation       //
3// technology.                                                                //
4//                                                                            //
5// This work is proprietary software with source-available code.              //
6//                                                                            //
7// To copy, use, distribute, or contribute to this work, you must agree to    //
8// the terms of the General License Agreement:                                //
9//                                                                            //
10// https://github.com/Eliah-Lakhin/lady-deirdre/blob/master/EULA.md           //
11//                                                                            //
12// The agreement grants a Basic Commercial License, allowing you to use       //
13// this work in non-commercial and limited commercial products with a total   //
14// gross revenue cap. To remove this commercial limit for one of your         //
15// products, you must acquire a Full Commercial License.                      //
16//                                                                            //
17// If you contribute to the source code, documentation, or related materials, //
18// you must grant me an exclusive license to these contributions.             //
19// Contributions are governed by the "Contributions" section of the General   //
20// License Agreement.                                                         //
21//                                                                            //
22// Copying the work in parts is strictly forbidden, except as permitted       //
23// under the General License Agreement.                                       //
24//                                                                            //
25// If you do not or cannot agree to the terms of this Agreement,              //
26// do not use this work.                                                      //
27//                                                                            //
28// This work is provided "as is", without any warranties, express or implied, //
29// except where such disclaimers are legally invalid.                         //
30//                                                                            //
31// Copyright (c) 2024 Ilya Lakhin (Илья Александрович Лахин).                 //
32// All rights reserved.                                                       //
33////////////////////////////////////////////////////////////////////////////////
34
35use std::{
36    cmp::Ordering,
37    fmt::{Display, Formatter},
38    ops::AddAssign,
39};
40
41use crate::lexis::{Site, SourceCode, ToSite};
42
43/// An index of the line in the source code text.
44///
45/// Line numeration starts from 1, such that 1 denotes the first line,
46/// 2 denotes the second line, and so on.
47///
48/// Line 0 also denotes the first line.
49///
50/// If this number exceed the total number of lines, the value interpreted
51/// as the source code text end.
52pub type Line = usize;
53
54/// An index of the character of the line in the source code text.
55///
56/// Column numeration starts from 1, such that 1 denotes the first char,
57/// 2 denotes the second char, and so on.
58///
59/// Column 0 also denotes the first char.
60///
61/// If this number exceed the total number of characters of the line, the value
62/// interpreted as the end of the line.
63///
64/// Note that the line delimiters (`\n` and `\r` chars) are parts of the line
65/// tail.
66pub type Column = usize;
67
68/// A line-column index of the Unicode character within the source code text.
69///
70/// The line and the column indices are 1-based, and the Position object
71/// is always [valid](ToSite::is_valid_site) index for any source code.
72///
73/// For details, see [Line] and [Column] specifications.
74#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
75pub struct Position {
76    /// A line number. This value is 1-based.
77    pub line: Line,
78
79    /// A number of the character in the line. This value is 1-based.
80    pub column: Column,
81}
82
83impl Ord for Position {
84    #[inline]
85    fn cmp(&self, other: &Self) -> Ordering {
86        if self.line < other.line {
87            return Ordering::Less;
88        }
89
90        if self.line > other.line {
91            return Ordering::Greater;
92        }
93
94        self.column.cmp(&other.column)
95    }
96}
97
98impl PartialOrd for Position {
99    #[inline(always)]
100    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
101        Some(self.cmp(other))
102    }
103}
104
105impl Default for Position {
106    #[inline(always)]
107    fn default() -> Self {
108        Self { line: 1, column: 1 }
109    }
110}
111
112impl Display for Position {
113    #[inline(always)]
114    fn fmt(&self, formatter: &mut Formatter) -> std::fmt::Result {
115        formatter.write_fmt(format_args!("{}:{}", self.line, self.column))
116    }
117}
118
119impl<I: Iterator<Item = char>> AddAssign<I> for Position {
120    #[inline]
121    fn add_assign(&mut self, rhs: I) {
122        for ch in rhs {
123            match ch {
124                '\n' => {
125                    self.line += 1;
126                    self.column = 1;
127                }
128
129                _ => {
130                    self.column += 1;
131                }
132            }
133        }
134    }
135}
136
137unsafe impl ToSite for Position {
138    fn to_site(&self, code: &impl SourceCode) -> Option<Site> {
139        let span = code.lines().line_span(self.line);
140
141        Some(
142            self.column
143                .checked_sub(1)
144                .unwrap_or_default()
145                .checked_add(span.start)
146                .unwrap_or(span.end)
147                .min(span.end),
148        )
149    }
150
151    #[inline(always)]
152    fn is_valid_site(&self, _code: &impl SourceCode) -> bool {
153        true
154    }
155}
156
157impl Position {
158    /// A constructor of the Position object.
159    #[inline(always)]
160    pub fn new(line: Line, column: Column) -> Self {
161        Self { line, column }
162    }
163}