Skip to main content

microcad_lang/
parser.rs

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