1use decoded_char::DecodedChar;
2use locspan::{Span, Spanned};
3use rdf_syntax::{BlankIdBuf, IriBuf, LangTagBuf};
4use std::{fmt, iter::Peekable};
5
6#[derive(Debug)]
8pub enum NquadsLexingError<E = std::convert::Infallible> {
9 InvalidLangTag(Span),
10 InvalidCodepoint(u32, Span),
11 InvalidIriRef(String, Span),
12 Unexpected(Option<char>, Span),
13 Stream(E),
14}
15
16impl<E: fmt::Display> fmt::Display for NquadsLexingError<E> {
17 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
18 match self {
19 Self::InvalidLangTag(_) => write!(f, "invalid language tag"),
20 Self::InvalidCodepoint(c, _) => write!(f, "invalid character code point {c:x}"),
21 Self::InvalidIriRef(iri_ref, _) => {
22 write!(f, "invalid IRI reference <{iri_ref}>")
23 }
24 Self::Unexpected(None, _) => write!(f, "unexpected end of file"),
25 Self::Unexpected(Some(c), _) => write!(f, "unexpected character `{c}`"),
26 Self::Stream(e) => e.fmt(f),
27 }
28 }
29}
30
31impl<E: 'static + std::error::Error> std::error::Error for NquadsLexingError<E> {
32 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
33 match self {
34 Self::Stream(e) => Some(e),
35 _ => None,
36 }
37 }
38}
39
40#[derive(Debug)]
42pub enum Token {
43 LangTag(LangTagBuf, Span),
44 Iri(IriBuf, Span),
45 StringLiteral(String, Span),
46 BlankNodeLabel(BlankIdBuf, Span),
47 Dot(Span),
48 Carets(Span),
49}
50
51impl Spanned for Token {
52 fn span(&self) -> Span {
53 match self {
54 Self::LangTag(_, s) => *s,
55 Self::Iri(_, s) => *s,
56 Self::StringLiteral(_, s) => *s,
57 Self::BlankNodeLabel(_, s) => *s,
58 Self::Dot(s) => *s,
59 Self::Carets(s) => *s,
60 }
61 }
62}
63
64impl fmt::Display for Token {
65 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
66 match self {
67 Self::LangTag(tag, _) => write!(f, "language tag `{tag}`"),
68 Self::Iri(iri, _) => write!(f, "IRI <{iri}>"),
69 Self::StringLiteral(string, _) => {
70 write!(f, "string literal \"{}\"", DisplayStringLiteral(string))
71 }
72 Self::BlankNodeLabel(label, _) => write!(f, "blank node label `{label}`"),
73 Self::Dot(_) => write!(f, "dot `.`"),
74 Self::Carets(_) => write!(f, "carets `^^`"),
75 }
76 }
77}
78
79pub struct DisplayStringLiteral<'a>(pub &'a str);
81
82impl fmt::Display for DisplayStringLiteral<'_> {
83 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
84 for c in self.0.chars() {
85 match c {
86 '"' => write!(f, "\\u0022"),
87 '\\' => write!(f, "\\u005c"),
88 '\n' => write!(f, "\\n"),
89 '\r' => write!(f, "\\r"),
90 '\t' => write!(f, "\\t"),
91 '\u{08}' => write!(f, "\\b"),
92 '\u{0c}' => write!(f, "\\f"),
93 c => c.fmt(f),
94 }?
95 }
96
97 Ok(())
98 }
99}
100
101struct Chars<C: Iterator>(Peekable<C>);
103
104impl<E, C: Iterator<Item = Result<DecodedChar, E>>> Chars<C> {
105 fn peek(&mut self) -> Result<Option<DecodedChar>, NquadsLexingError<E>> {
106 match self.0.peek() {
107 None => Ok(None),
108 Some(Ok(c)) => Ok(Some(*c)),
109 Some(Err(_)) => self.next(),
110 }
111 }
112
113 fn next(&mut self) -> Result<Option<DecodedChar>, NquadsLexingError<E>> {
114 self.0.next().transpose().map_err(NquadsLexingError::Stream)
115 }
116}
117
118#[derive(Default)]
120struct Position {
121 span: Span,
122 last_span: Span,
123}
124
125impl Position {
126 fn current(&self) -> Span {
127 self.span
128 }
129
130 fn end(&self) -> Span {
131 self.span.end.into()
132 }
133
134 fn last(&self) -> Span {
135 self.last_span
136 }
137}
138
139pub struct Lexer<C: Iterator<Item = Result<DecodedChar, E>>, E> {
143 chars: Chars<C>,
144 pos: Position,
145 }
147
148impl<C: Iterator<Item = Result<DecodedChar, E>>, E> Lexer<C, E> {
149 pub fn new(chars: C) -> Self {
150 Self {
151 chars: Chars(chars.peekable()),
152 pos: Position::default(),
153 }
154 }
155}
156
157impl<E, C: Iterator<Item = Result<DecodedChar, E>>> Lexer<C, E> {
158 fn peek_decoded_char(&mut self) -> Result<Option<DecodedChar>, NquadsLexingError<E>> {
159 self.chars.peek()
160 }
161
162 fn peek_char(&mut self) -> Result<Option<char>, NquadsLexingError<E>> {
163 self.peek_decoded_char()
164 .map(|c| c.map(DecodedChar::into_char))
165 }
166
167 fn next_char(&mut self) -> Result<Option<char>, NquadsLexingError<E>> {
168 match self.chars.next()? {
169 Some(c) => {
170 self.pos.span.push(c.len());
171 self.pos.last_span.clear();
172 self.pos.last_span.push(c.len());
173 Ok(Some(*c))
174 }
175 None => Ok(None),
176 }
177 }
178
179 fn expect_char(&mut self) -> Result<char, NquadsLexingError<E>> {
180 self.next_char()?
181 .ok_or_else(|| NquadsLexingError::Unexpected(None, self.pos.last()))
182 }
183
184 fn skip_whitespaces(&mut self) -> Result<(), NquadsLexingError<E>> {
185 while let Some(c) = self.peek_char()? {
186 if c.is_whitespace() {
187 self.next_char()?;
188 } else if c == '#' {
189 self.next_comment()?;
190 } else {
191 break;
192 }
193 }
194
195 self.pos.span.clear();
196 Ok(())
197 }
198
199 fn next_comment(&mut self) -> Result<(), NquadsLexingError<E>> {
206 loop {
207 if matches!(self.next_char()?, None | Some('\n')) {
208 break Ok(());
209 }
210 }
211 }
212
213 fn next_langtag(&mut self) -> Result<(LangTagBuf, Span), NquadsLexingError<E>> {
215 let mut tag = String::new();
216
217 loop {
218 match self.peek_char()? {
219 None => {
220 if tag.is_empty() {
221 return Err(NquadsLexingError::InvalidLangTag(self.pos.current()));
222 } else {
223 break;
224 }
225 }
226 Some(c) => {
227 if c.is_ascii_alphabetic() {
228 tag.push(self.expect_char()?);
229 } else if c.is_whitespace() || c == '-' {
230 if tag.is_empty() {
231 return Err(NquadsLexingError::InvalidLangTag(self.pos.current()));
232 } else {
233 break;
234 }
235 } else {
236 self.next_char()?;
237 return Err(NquadsLexingError::Unexpected(Some(c), self.pos.last()));
238 }
239 }
240 }
241 }
242
243 let mut empty_subtag = true;
244 if let Some('-') = self.peek_char()? {
245 tag.push(self.expect_char()?);
246 loop {
247 match self.peek_char()? {
248 Some('-') if !empty_subtag => tag.push(self.expect_char()?),
249 Some(c) if c.is_ascii_alphanumeric() => {
250 empty_subtag = false;
251 tag.push(self.expect_char()?)
252 }
253 Some(c) => {
254 if c.is_whitespace() {
255 if empty_subtag {
256 return Err(NquadsLexingError::InvalidLangTag(self.pos.current()));
257 } else {
258 break;
259 }
260 } else {
261 self.next_char()?;
262 return Err(NquadsLexingError::Unexpected(Some(c), self.pos.last()));
263 }
264 }
265 None => {
266 if empty_subtag {
267 return Err(NquadsLexingError::InvalidLangTag(self.pos.current()));
268 } else {
269 break;
270 }
271 }
272 }
273 }
274 }
275
276 match LangTagBuf::new(tag) {
277 Ok(tag) => Ok((tag, self.pos.current())),
278 Err(_) => Err(NquadsLexingError::InvalidLangTag(self.pos.current())),
279 }
280 }
281
282 fn next_iri(&mut self) -> Result<(IriBuf, Span), NquadsLexingError<E>> {
284 let mut iri = String::new();
285
286 loop {
287 match self.next_char()? {
288 Some('>') => break,
289 Some('\\') => {
290 let span = self.pos.last();
291 let c = match self.next_char()? {
292 Some('u') => self.next_uchar(span, 4)?,
293 Some('U') => self.next_uchar(span, 8)?,
294 unexpected => {
295 return Err(NquadsLexingError::Unexpected(unexpected, self.pos.last()));
296 }
297 };
298
299 iri.push(c)
300 }
301 Some(c) => {
302 if matches!(
303 c,
304 '\u{00}'..='\u{20}' | '<' | '>' | '"' | '{' | '}' | '|' | '^' | '`' | '\\'
305 ) {
306 return Err(NquadsLexingError::Unexpected(Some(c), self.pos.last()));
307 }
308
309 iri.push(c)
310 }
311 None => return Err(NquadsLexingError::Unexpected(None, self.pos.end())),
312 }
313 }
314
315 match IriBuf::new(iri) {
316 Ok(iri) => Ok((iri, self.pos.current())),
317 Err(e) => Err(NquadsLexingError::InvalidIriRef(e.0, self.pos.current())),
318 }
319 }
320
321 fn next_uchar(&mut self, mut span: Span, len: u8) -> Result<char, NquadsLexingError<E>> {
322 let mut codepoint = 0;
323
324 for _ in 0..len {
325 let c = self.expect_char()?;
326 match c.to_digit(16) {
327 Some(d) => codepoint = codepoint << 4 | d,
328 None => return Err(NquadsLexingError::Unexpected(Some(c), self.pos.last())),
329 }
330 }
331
332 span.end = self.pos.current().end;
333
334 match char::try_from(codepoint) {
335 Ok(c) => Ok(c),
336 Err(_) => Err(NquadsLexingError::InvalidCodepoint(codepoint, span)),
337 }
338 }
339
340 fn next_string_literal(&mut self) -> Result<(String, Span), NquadsLexingError<E>> {
342 let mut string = String::new();
343
344 loop {
345 match self.next_char()? {
346 Some('"') => break,
347 Some('\\') => {
348 let span = self.pos.last();
349 let c = match self.next_char()? {
350 Some('u') => self.next_uchar(span, 4)?,
351 Some('U') => self.next_uchar(span, 8)?,
352 Some('t') => '\t',
353 Some('b') => '\u{08}',
354 Some('n') => '\n',
355 Some('r') => '\r',
356 Some('f') => '\u{0c}',
357 Some('\'') => '\'',
358 Some('"') => '"',
359 Some('\\') => '\\',
360 unexpected => {
361 return Err(NquadsLexingError::Unexpected(unexpected, self.pos.last()));
362 }
363 };
364
365 string.push(c)
366 }
367 Some(c) => {
368 if matches!(c, '\n' | '\r') {
369 return Err(NquadsLexingError::Unexpected(Some(c), self.pos.last()));
370 }
371
372 string.push(c)
373 }
374 None => return Err(NquadsLexingError::Unexpected(None, self.pos.end())),
375 }
376 }
377
378 Ok((string, self.pos.current()))
379 }
380
381 fn next_blank_node_label(&mut self) -> Result<(BlankIdBuf, Span), NquadsLexingError<E>> {
383 match self.next_char()? {
384 Some(':') => {
385 let mut label = String::new();
386 label.push('_');
387 label.push(':');
388 match self.next_char()? {
389 Some(c) if c.is_ascii_digit() || is_pn_chars_u(c) => {
390 label.push(c);
391 let mut last_is_pn_chars = true;
392 loop {
393 match self.peek_char()? {
394 Some(c) if is_pn_chars(c) => {
395 label.push(self.expect_char()?);
396 last_is_pn_chars = true
397 }
398 Some('.') => {
399 label.push(self.expect_char()?);
400 last_is_pn_chars = false;
401 }
402 _ if last_is_pn_chars => break,
403 unexpected => {
404 return Err(NquadsLexingError::Unexpected(
405 unexpected,
406 self.pos.last(),
407 ));
408 }
409 }
410 }
411
412 Ok((
413 unsafe { BlankIdBuf::new_unchecked(label) },
414 self.pos.current(),
415 ))
416 }
417 unexpected => Err(NquadsLexingError::Unexpected(unexpected, self.pos.last())),
418 }
419 }
420 unexpected => Err(NquadsLexingError::Unexpected(unexpected, self.pos.last())),
421 }
422 }
423}
424
425impl<E, C: Iterator<Item = Result<DecodedChar, E>>> Iterator for Lexer<C, E> {
426 type Item = Result<Token, NquadsLexingError<E>>;
427
428 fn next(&mut self) -> Option<Self::Item> {
429 if let Err(e) = self.skip_whitespaces() {
430 return Some(Err(e));
431 }
432
433 match self.next_char() {
434 Ok(Some('@')) => match self.next_langtag() {
435 Ok((tag, span)) => Some(Ok(Token::LangTag(tag, span))),
436 Err(e) => Some(Err(e)),
437 },
438 Ok(Some('<')) => match self.next_iri() {
439 Ok((iri, span)) => Some(Ok(Token::Iri(iri, span))),
440 Err(e) => Some(Err(e)),
441 },
442 Ok(Some('"')) => match self.next_string_literal() {
443 Ok((string, span)) => Some(Ok(Token::StringLiteral(string, span))),
444 Err(e) => Some(Err(e)),
445 },
446 Ok(Some('_')) => match self.next_blank_node_label() {
447 Ok((blank_id, span)) => Some(Ok(Token::BlankNodeLabel(blank_id, span))),
448 Err(e) => Some(Err(e)),
449 },
450 Ok(Some('^')) => match self.next_char() {
451 Ok(Some('^')) => Some(Ok(Token::Carets(self.pos.current()))),
452 Ok(unexpected) => Some(Err(NquadsLexingError::Unexpected(
453 unexpected,
454 self.pos.last(),
455 ))),
456 Err(e) => Some(Err(e)),
457 },
458 Ok(Some('.')) => Some(Ok(Token::Dot(self.pos.current()))),
459 Ok(Some(c)) => Some(Err(NquadsLexingError::Unexpected(Some(c), self.pos.last()))),
460 Ok(None) => None,
461 Err(e) => Some(Err(e)),
462 }
463 }
464}
465
466fn is_pn_chars_base(c: char) -> bool {
467 matches!(c, 'A'..='Z' | 'a'..='z' | '\u{00c0}'..='\u{00d6}' | '\u{00d8}'..='\u{00f6}' | '\u{00f8}'..='\u{02ff}' | '\u{0370}'..='\u{037d}' | '\u{037f}'..='\u{1fff}' | '\u{200c}'..='\u{200d}' | '\u{2070}'..='\u{218f}' | '\u{2c00}'..='\u{2fef}' | '\u{3001}'..='\u{d7ff}' | '\u{f900}'..='\u{fdcf}' | '\u{fdf0}'..='\u{fffd}' | '\u{10000}'..='\u{effff}')
468}
469
470fn is_pn_chars_u(c: char) -> bool {
471 is_pn_chars_base(c) || matches!(c, '_' | ':')
472}
473
474fn is_pn_chars(c: char) -> bool {
475 is_pn_chars_u(c)
476 || matches!(c, '-' | '0'..='9' | '\u{00b7}' | '\u{0300}'..='\u{036f}' | '\u{203f}'..='\u{2040}')
477}