Skip to main content

microcad_lang/
parser.rs

1// Copyright © 2024-2026 The µcad authors <info@ucad.xyz>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4//! µcad Code Parser
5
6#![allow(missing_docs)]
7
8use microcad_syntax::Span;
9use crate::{parse::*, src_ref::*};
10
11#[derive(Clone)]
12pub struct LineIndex {
13    /// Offset (bytes) the beginning of each line, zero-based
14    line_offsets: Vec<usize>,
15}
16
17impl LineIndex {
18    pub fn new(text: &str) -> LineIndex {
19        let mut line_offsets: Vec<usize> = vec![0];
20
21        let mut offset = 0;
22
23        for c in text.chars() {
24            offset += c.len_utf8();
25            if c == '\n' {
26                line_offsets.push(offset);
27            }
28        }
29
30        LineIndex { line_offsets }
31    }
32
33    /// Returns (line, col) of pos.
34    ///
35    /// The pos is a byte offset, start from 0, e.g. "ab" is 2, "你好" is 6
36    pub fn line_col(&self, input: &str, pos: usize) -> (usize, usize) {
37        let line = self.line_offsets.partition_point(|&it| it <= pos) - 1;
38        let first_offset = self.line_offsets[line];
39
40        // Get line str from original input, then we can get column offset
41        let line_str = &input[first_offset..pos];
42        let col = line_str.chars().count();
43
44        (line + 1, col + 1)
45    }
46}
47
48pub struct ParseContext<'source> {
49    pub source: &'source str,
50    pub source_file_hash: u64,
51    line_index: LineIndex,
52}
53
54impl<'source> ParseContext<'source> {
55    pub fn new(source: &'source str) -> Self {
56        let source_file_hash = {
57            use std::hash::{Hash, Hasher};
58            let mut hasher = rustc_hash::FxHasher::default();
59            source.hash(&mut hasher);
60            hasher.finish()
61        };
62        ParseContext {
63            source,
64            source_file_hash,
65            line_index: LineIndex::new(source),
66        }
67    }
68
69    pub fn src_ref(&self, span: &Span) -> SrcRef {
70        let (line, col) = self.line_index.line_col(self.source, span.start);
71        SrcRef::new(span.clone(), line, col, self.source_file_hash)
72    }
73}
74
75pub trait FromAst: Sized {
76    type AstNode;
77
78    fn from_ast(node: &Self::AstNode, context: &ParseContext) -> Result<Self, ParseError>;
79}