rl_lang/parser/statements/import_statement.rs
1//! Import statement parser (`get`).
2//!
3//! Handles all four import forms in rl-lang:
4//!
5//! ```text
6//! // 1. single file module
7//! get mymodule
8//!
9//! // 2. file module with path
10//! get mymodule::utils
11//!
12//! // 3. stdlib function
13//! get std::math::sin
14//!
15//! // 4. named imports from a module or stdlib
16//! get sin, cos from std::math
17//! get add, sub from mymodule::utils
18//! ```
19//!
20//! The first token after `get` and whether `::` or `from` follows determines
21//! which [`StatementKind`] variant is produced:
22//!
23//! | syntax | kind |
24//! |---|---|
25//! | `get mod` | [`StatementKind::ImportFile`] |
26//! | `get mod::sub` | [`StatementKind::ImportFile`] |
27//! | `get std::ns::fn` | [`StatementKind::Import`] |
28//! | `get fn, fn from std::ns` | [`StatementKind::Import`] |
29//! | `get fn, fn from mod::sub` | [`StatementKind::ImportFileNamed`] |
30
31use crate::{
32 ast::statements::{Statement, StatementKind},
33 lexer::tokentypes::TokenType,
34 parser::parser_logic::Parser,
35 utils::errors::Error,
36};
37
38impl Parser {
39 /// Parses a `get` import statement.
40 ///
41 /// Called after `get` has been consumed. Dispatches on the tokens that
42 /// follow the first identifier:
43 ///
44 /// - **`get mod`** (no `::`, no `from`) - single-segment file import.
45 /// Produces [`StatementKind::ImportFile`]`{ path: [mod] }`.
46 ///
47 /// - **`get mod::sub::…`** - multi-segment path. If the first segment is
48 /// `std`, the last segment is treated as the function name and the rest
49 /// as the namespace path -> [`StatementKind::Import`]. Otherwise the whole
50 /// path is a file module -> [`StatementKind::ImportFile`].
51 ///
52 /// - **`get name, name from path`** - named imports. If `path` starts with
53 /// `std` -> [`StatementKind::Import`]`{ names, path }`. Otherwise ->
54 /// [`StatementKind::ImportFileNamed`]`{ path, names }`.
55 ///
56 /// # Errors
57 /// Returns an error if an identifier is missing after `get`, `::`, `,`, or
58 /// `from`, or if `from` itself is absent in the named-import form.
59 pub fn parse_import(&mut self, start: crate::utils::span::Span) -> Result<Statement, Error> {
60 let first = match self.peek() {
61 TokenType::Identifier(name) => name,
62 _ => return Err(self.err("expected identifier after 'get'", self.peek_span())),
63 };
64 self.advance();
65
66 // multi-segment path: get mod::sub OR get std::math::sin
67 if self.match_type(&[TokenType::ColonColon]) {
68 let mut segments = vec![first];
69 loop {
70 match self.peek() {
71 TokenType::Identifier(seg) => {
72 self.advance();
73 segments.push(seg);
74 }
75 _ => return Err(self.err("expected identifier after '::'", self.peek_span())),
76 }
77 if !self.match_type(&[TokenType::ColonColon]) {
78 break;
79 }
80 }
81 let span = start.join(self.previous_span());
82 let is_std = segments[0] == "std";
83 return if is_std {
84 // last segment is the function name; everything before it is the path
85 let name = segments
86 .pop()
87 .ok_or_else(|| self.err("expected function name after '::'", start))?;
88 Ok(Statement::new(
89 StatementKind::Import {
90 names: vec![name],
91 path: segments,
92 },
93 span,
94 ))
95 } else {
96 Ok(Statement::new(
97 StatementKind::ImportFile { path: segments },
98 span,
99 ))
100 };
101 }
102
103 // single-segment file import: get mymodule
104 if !matches!(self.peek(), TokenType::Comma | TokenType::From) {
105 let span = start.join(self.previous_span());
106 return Ok(Statement::new(
107 StatementKind::ImportFile { path: vec![first] },
108 span,
109 ));
110 }
111
112 // named imports: get add, sub from …
113 let mut names = vec![first];
114 if self.match_type(&[TokenType::Comma]) {
115 loop {
116 match self.peek() {
117 TokenType::Identifier(name) => {
118 self.advance();
119 names.push(name);
120 }
121 _ => return Err(self.err("expected identifier after ','", self.peek_span())),
122 }
123 if !self.match_type(&[TokenType::Comma]) {
124 break;
125 }
126 }
127 }
128
129 if !self.match_type(&[TokenType::From]) {
130 return Err(self.err("expected 'from' after names", self.peek_span()));
131 }
132
133 let mut path = Vec::new();
134 loop {
135 match self.peek() {
136 TokenType::Identifier(segment) => {
137 self.advance();
138 path.push(segment);
139 }
140 _ => return Err(self.err("expected path after 'from'", self.peek_span())),
141 }
142 if !self.match_type(&[TokenType::ColonColon]) {
143 break;
144 }
145 }
146
147 let span = start.join(self.previous_span());
148 let is_std = path.first().map(|s| s == "std").unwrap_or(false);
149
150 if is_std {
151 Ok(Statement::new(StatementKind::Import { names, path }, span))
152 } else {
153 Ok(Statement::new(
154 StatementKind::ImportFileNamed { path, names },
155 span,
156 ))
157 }
158 }
159}