wgsl_parser/decl/
import.rs1use std::sync::Arc;
6
7use gramatika::{Parse, ParseStreamer, Span, Spanned, SpannedError, Token as _};
8
9use crate::{
10 parser::ErrorRecoveringParseStream,
11 token::{brace, directive, ident, punct, Token, TokenKind},
12 ParseStream,
13};
14
15#[derive(Clone, DebugLisp)]
22pub struct ImportPathDecl {
23 pub keyword: Token,
24 pub path: ImportPath,
25}
26
27#[derive(Clone, DebugLisp)]
39pub struct ImportDecl {
40 pub keyword: Token,
41 pub path: ImportPath,
42}
43
44#[derive(Clone, DebugLisp)]
45pub enum ImportPath {
46 Namespaced(NamespacedImportPath),
47 Block(ImportPathBlock),
48 Leaf(ImportPathLeaf),
49}
50
51#[derive(Clone, DebugLisp)]
52pub struct NamespacedImportPath {
53 pub namespace: Token,
54 pub path: Arc<ImportPath>,
55}
56
57#[derive(Clone, DebugLisp)]
58pub struct ImportPathBlock {
59 pub brace_open: Token,
60 pub paths: Arc<[ImportPath]>,
61 pub brace_close: Token,
62}
63
64#[derive(Clone, DebugLisp)]
65pub struct ImportPathLeaf {
66 pub name: Token,
67 pub as_binding: Option<Token>,
68}
69
70impl ImportPathDecl {
71 pub fn name(&self) -> &Token {
79 let mut import_path = &self.path;
80 loop {
81 match import_path {
82 ImportPath::Namespaced(NamespacedImportPath { path, .. }) => {
83 import_path = path.as_ref();
84 }
85 ImportPath::Leaf(ImportPathLeaf { name, .. }) => {
86 break name;
87 }
88 ImportPath::Block(_) => {
89 unreachable!();
90 }
91 }
92 }
93 }
94}
95
96impl Parse for ImportPathDecl {
97 type Stream = ParseStream;
98
99 fn parse(input: &mut Self::Stream) -> gramatika::Result<Self> {
100 let keyword = input.consume(directive!["#define_import_path"])?;
101 let path = input.parse()?;
102
103 let mut parsed_path = &path;
105 loop {
106 match parsed_path {
107 ImportPath::Namespaced(NamespacedImportPath { path, .. }) => {
108 parsed_path = path.as_ref();
109 }
110 ImportPath::Block(block) => {
111 return Err(SpannedError {
112 message: "Path blocks are not valid in `#define_import_path`".into(),
113 source: input.source(),
114 span: Some(block.span()),
115 });
116 }
117 ImportPath::Leaf(leaf) => {
118 if let Some(as_binding) = leaf.as_binding.as_ref() {
119 return Err(SpannedError {
120 message: "`as` bindings are not valid in `#define_import_path`".into(),
121 source: input.source(),
122 span: Some(as_binding.span()),
123 });
124 } else {
125 break;
126 }
127 }
128 }
129 }
130
131 Ok(Self { keyword, path })
132 }
133}
134
135impl Spanned for ImportPathDecl {
136 fn span(&self) -> Span {
137 self.keyword.span().through(self.path.span())
138 }
139}
140
141impl Parse for ImportDecl {
142 type Stream = ParseStream;
143
144 fn parse(input: &mut Self::Stream) -> gramatika::Result<Self> {
145 let keyword = input.consume(directive!["#import"])?;
146 let path = input.parse()?;
147
148 Ok(Self { keyword, path })
149 }
150}
151
152impl Spanned for ImportDecl {
153 fn span(&self) -> Span {
154 self.keyword.span().through(self.path.span())
155 }
156}
157
158impl Parse for ImportPath {
159 type Stream = ParseStream;
160
161 fn parse(input: &mut Self::Stream) -> gramatika::Result<Self> {
162 use TokenKind::*;
163
164 match input.next() {
165 Some(mut next) => match next.as_matchable() {
166 (Ident | Path, _, _) => {
167 next = match next.kind() {
168 TokenKind::Ident => input.upgrade_last(TokenKind::Ident, Token::module)?,
169 TokenKind::Path => next,
170 _ => unreachable!(),
171 };
172
173 if input.check(punct![::]) {
174 input.discard();
175
176 Ok(ImportPath::Namespaced(NamespacedImportPath {
177 namespace: next,
178 path: Arc::new(input.parse()?),
179 }))
180 } else {
181 let name = next;
182 let as_binding = if input.check(ident![as]) {
183 let _ = input.consume_as(TokenKind::Ident, Token::keyword)?;
184 Some(input.consume_as(TokenKind::Ident, Token::module)?)
185 } else {
186 None
187 };
188
189 Ok(ImportPath::Leaf(ImportPathLeaf { name, as_binding }))
190 }
191 }
192 (Brace, "{", _) => {
193 let brace_open = next;
194
195 let paths =
196 input.parse_seq_separated(punct![,], |input| !input.check(brace!("}")))?;
197
198 let brace_close = input.consume(brace!("}"))?;
199
200 Ok(ImportPath::Block(ImportPathBlock {
201 brace_open,
202 paths: paths.into(),
203 brace_close,
204 }))
205 }
206 (_, _, span) => Err(SpannedError {
207 message: "Expected identifier or `{`".into(),
208 source: input.source(),
209 span: Some(span),
210 }),
211 },
212 None => Err(SpannedError {
213 message: "Unexpected end of input".into(),
214 source: input.source(),
215 span: input.prev().map(|token| token.span()),
216 }),
217 }
218 }
219}
220
221impl Spanned for ImportPath {
222 fn span(&self) -> Span {
223 match self {
224 ImportPath::Namespaced(NamespacedImportPath { namespace, path }) => {
225 namespace.span().through(path.span())
226 }
227 ImportPath::Block(inner) => inner.span(),
228 ImportPath::Leaf(inner) => inner.span(),
229 }
230 }
231}
232
233impl Spanned for ImportPathBlock {
234 fn span(&self) -> Span {
235 self.brace_open.span().through(self.brace_close.span())
236 }
237}
238
239impl Spanned for ImportPathLeaf {
240 fn span(&self) -> Span {
241 if let Some(binding) = self.as_binding.as_ref() {
242 self.name.span().through(binding.span())
243 } else {
244 self.name.span()
245 }
246 }
247}