Skip to main content

fukurow_sparql/
parser.rs

1//! SPARQL 1.1 構文解析器
2
3use logos::Logos;
4use winnow::{
5    ModalResult, Parser,
6    combinator::{alt, opt, repeat, separated, preceded, terminated, delimited},
7    token::take_while,
8};
9use std::collections::HashMap;
10
11/// SPARQL Parser trait
12#[derive(Debug, Clone, PartialEq, Eq, Hash)]
13pub struct Iri(pub String);
14
15impl std::fmt::Display for Iri {
16    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
17        write!(f, "{}", self.0)
18    }
19}
20
21/// RDF Literal
22#[derive(Debug, Clone, PartialEq, Eq, Hash)]
23pub struct Literal {
24    pub value: String,
25    pub datatype: Option<Iri>,
26    pub language: Option<String>,
27}
28
29/// RDF Variable
30#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
31pub struct Variable(pub String);
32
33/// SPARQL トークン
34#[derive(Logos, Debug, PartialEq)]
35#[logos(skip r"[ \t\n\f]+")] // whitespace
36pub enum Token<'a> {
37    #[token("SELECT")]
38    Select,
39
40    #[token("CONSTRUCT")]
41    Construct,
42
43    #[token("ASK")]
44    Ask,
45
46    #[token("DESCRIBE")]
47    Describe,
48
49    #[token("WHERE")]
50    Where,
51
52    #[token("FILTER")]
53    Filter,
54
55    #[token("OPTIONAL")]
56    Optional,
57
58    #[token("UNION")]
59    Union,
60
61    #[token("GRAPH")]
62    Graph,
63
64    #[token("ORDER")]
65    Order,
66
67    #[token("BY")]
68    By,
69
70    #[token("LIMIT")]
71    Limit,
72
73    #[token("OFFSET")]
74    Offset,
75
76    #[token("DISTINCT")]
77    Distinct,
78
79    #[token("REDUCED")]
80    Reduced,
81
82    #[token("FROM")]
83    From,
84
85    #[token("NAMED")]
86    Named,
87
88    #[regex(r"\?[a-zA-Z_][a-zA-Z0-9_]*")]
89    Variable(&'a str),
90
91    #[regex(r"<[^>]*>")]
92    Iri(&'a str),
93
94    #[regex(r#""([^"\\]|\\.)*""#)]
95    StringLiteral(&'a str),
96
97    #[regex(r"[a-zA-Z_][a-zA-Z0-9_]*:[a-zA-Z_][a-zA-Z0-9_]*")]
98    PrefixedName(&'a str),
99
100    #[regex(r"[0-9]+")]
101    Integer(&'a str),
102
103    #[regex(r"[0-9]*\.[0-9]+")]
104    Decimal(&'a str),
105
106    #[token("{")]
107    LBrace,
108
109    #[token("}")]
110    RBrace,
111
112    #[token("(")]
113    LParen,
114
115    #[token(")")]
116    RParen,
117
118    #[token("[")]
119    LBracket,
120
121    #[token("]")]
122    RBracket,
123
124    #[token(".")]
125    Dot,
126
127    #[token(",")]
128    Comma,
129
130    #[token(";")]
131    Semicolon,
132
133    #[token(":")]
134    Colon,
135
136    #[token("^^")]
137    DoubleCaret,
138
139    #[token("@")]
140    At,
141
142    #[token("a")]
143    A,
144
145    #[token("^")]
146    Caret,
147
148    #[token("/")]
149    Slash,
150
151    #[token("|")]
152    Pipe,
153
154    #[token("*")]
155    Star,
156
157    #[token("+")]
158    Plus,
159
160    #[token("?")]
161    Question,
162
163    #[token("=")]
164    Equals,
165
166    #[token("!=")]
167    NotEquals,
168
169    #[token("<")]
170    LessThan,
171
172    #[token("<=")]
173    LessEqual,
174
175    #[token(">")]
176    GreaterThan,
177
178    #[token(">=")]
179    GreaterEqual,
180
181    #[token("&&")]
182    And,
183
184    #[token("||")]
185    Or,
186
187    #[token("!")]
188    Not,
189
190    #[token("ASC")]
191    Asc,
192
193    #[token("DESC")]
194    Desc,
195
196    #[token("COUNT")]
197    Count,
198
199    #[token("SUM")]
200    Sum,
201
202    #[token("AVG")]
203    Avg,
204
205    #[token("MIN")]
206    Min,
207
208    #[token("MAX")]
209    Max,
210
211    #[token("GROUP")]
212    Group,
213
214    #[token("HAVING")]
215    Having,
216
217    #[token("AS")]
218    As,
219
220    #[token("BASE")]
221    Base,
222
223    #[token("PREFIX")]
224    Prefix,
225}
226
227/// RDF Term
228#[derive(Debug, Clone, PartialEq, Eq, Hash)]
229pub enum Term {
230    Iri(Iri),
231    Literal(Literal),
232    Variable(Variable),
233    BlankNode(String),
234    PrefixedName(String, String), // (prefix, local_name)
235}
236
237/// Triple Pattern
238#[derive(Debug, Clone, PartialEq)]
239pub struct TriplePattern {
240    pub subject: Term,
241    pub predicate: Term,
242    pub object: Term,
243}
244
245/// Property Path
246#[derive(Debug, Clone, PartialEq)]
247pub enum PropertyPath {
248    Predicate(Iri),
249    Inverse(Box<PropertyPath>),
250    Sequence(Vec<PropertyPath>),
251    Alternative(Vec<PropertyPath>),
252    ZeroOrMore(Box<PropertyPath>),
253    OneOrMore(Box<PropertyPath>),
254    ZeroOrOne(Box<PropertyPath>),
255}
256
257/// Graph Pattern
258#[derive(Debug, Clone, PartialEq)]
259pub enum GraphPattern {
260    Bgp(Vec<TriplePattern>),
261    Optional(Box<GraphPattern>),
262    Union(Vec<GraphPattern>),
263    Filter(Expression, Box<GraphPattern>),
264    Graph(VarOrIri, Box<GraphPattern>),
265    Minus(Box<GraphPattern>, Box<GraphPattern>),
266    Service(VarOrIri, Box<GraphPattern>, bool), // silent flag
267}
268
269/// Expression
270#[derive(Debug, Clone, PartialEq)]
271pub enum Expression {
272    Variable(Variable),
273    Iri(Iri),
274    Literal(Literal),
275    // Arithmetic
276    Add(Box<Expression>, Box<Expression>),
277    Subtract(Box<Expression>, Box<Expression>),
278    Multiply(Box<Expression>, Box<Expression>),
279    Divide(Box<Expression>, Box<Expression>),
280    // Comparison
281    Equal(Box<Expression>, Box<Expression>),
282    NotEqual(Box<Expression>, Box<Expression>),
283    LessThan(Box<Expression>, Box<Expression>),
284    LessThanOrEqual(Box<Expression>, Box<Expression>),
285    GreaterThan(Box<Expression>, Box<Expression>),
286    GreaterThanOrEqual(Box<Expression>, Box<Expression>),
287    // Logical
288    And(Box<Expression>, Box<Expression>),
289    Or(Box<Expression>, Box<Expression>),
290    Not(Box<Expression>),
291    // Functions
292    Bound(Variable),
293    IsIri(Box<Expression>),
294    IsLiteral(Box<Expression>),
295    IsBlank(Box<Expression>),
296    Str(Box<Expression>),
297    Lang(Box<Expression>),
298    Datatype(Box<Expression>),
299    IriFunc(Box<Expression>), // Rename to avoid conflict
300    Uri(Box<Expression>),
301    Bnode(Box<Expression>),
302    // Regex
303    Regex(Box<Expression>, Box<Expression>, Option<Box<Expression>>),
304    // Exists
305    Exists(Box<GraphPattern>),
306    NotExists(Box<GraphPattern>),
307}
308
309/// Var or IRI
310#[derive(Debug, Clone, PartialEq)]
311pub enum VarOrIri {
312    Var(Variable),
313    Iri(Iri),
314}
315
316/// Order condition
317#[derive(Debug, Clone, PartialEq)]
318pub enum OrderCondition {
319    Asc(Expression),
320    Desc(Expression),
321}
322
323/// Solution modifier
324#[derive(Debug, Clone, PartialEq)]
325pub struct SolutionModifier {
326    pub group: Option<Vec<Expression>>,
327    pub having: Option<Vec<Expression>>,
328    pub order: Option<Vec<OrderCondition>>,
329    pub limit: Option<u64>,
330    pub offset: Option<u64>,
331    pub distinct: bool,
332    pub reduced: bool,
333}
334
335/// SPARQL Query
336#[derive(Debug, Clone, PartialEq)]
337pub struct SparqlQuery {
338    pub query_type: QueryType,
339    pub variables: Vec<Variable>,
340    pub dataset: Vec<GraphRef>,
341    pub where_clause: GraphPattern,
342    pub solution_modifier: SolutionModifier,
343    pub values: Option<Vec<Bindings>>,
344    pub base_iri: Option<Iri>,
345    pub prefixes: HashMap<String, Iri>,
346}
347
348/// Query Type
349#[derive(Debug, Clone, PartialEq)]
350pub enum QueryType {
351    Select,
352    Construct(Vec<TriplePattern>),
353    Ask,
354    Describe(Vec<VarOrIri>),
355}
356
357/// Bindings (variable -> term mapping)
358pub type Bindings = HashMap<Variable, Term>;
359
360/// Graph reference for FROM/FROM NAMED
361#[derive(Debug, Clone, PartialEq)]
362pub enum GraphRef {
363    Named(Iri),
364    Default(Iri),
365}
366
367use crate::SparqlError;
368
369/// SPARQL Parser trait
370pub trait SparqlParser {
371    fn parse(&self, query: &str) -> Result<SparqlQuery, crate::SparqlError>;
372    fn parse_query(&self, query: &str) -> Result<SparqlQuery, crate::SparqlError>;
373}
374
375/// Default SPARQL Parser with winnow
376pub struct DefaultSparqlParser;
377
378impl DefaultSparqlParser {
379    /// Parse IRI (e.g., <http://example.org>)
380    fn parse_iri(input: &mut &str) -> winnow::ModalResult<Iri> {
381        winnow::combinator::delimited(
382            '<',
383            winnow::token::take_while(1.., |c: char| c != '>'),
384            '>'
385        ).parse_next(input)
386        .map(|s: &str| Iri(s.to_string()))
387    }
388
389    /// Parse variable (e.g., ?x)
390    fn parse_variable(input: &mut &str) -> winnow::ModalResult<Variable> {
391        winnow::combinator::preceded(
392            '?',
393            winnow::token::take_while(1.., |c: char| c.is_alphanumeric() || c == '_')
394        ).parse_next(input)
395        .map(|s: &str| Variable(s.to_string()))
396    }
397
398    /// Parse prefixed name (e.g., rdf:type)
399    fn parse_prefixed_name(input: &mut &str) -> winnow::ModalResult<Term> {
400        let prefix = winnow::token::take_while(1.., |c: char| c.is_alphanumeric() || c == '_');
401        let local = winnow::combinator::preceded(
402            ':',
403            winnow::token::take_while(1.., |c: char| c.is_alphanumeric() || c == '_' || c == '-')
404        );
405
406        (prefix, local).parse_next(input)
407        .map(|(p, l): (&str, &str)| Term::PrefixedName(p.to_string(), l.to_string()))
408    }
409
410    /// Parse term (IRI, variable, literal, prefixed name)
411    fn parse_term(input: &mut &str) -> winnow::ModalResult<Term> {
412        winnow::combinator::alt((
413            Self::parse_variable.map(Term::Variable),
414            Self::parse_iri.map(Term::Iri),
415            Self::parse_prefixed_name,
416        )).parse_next(input)
417    }
418
419    /// Parse triple pattern
420    fn parse_triple_pattern(input: &mut &str) -> winnow::ModalResult<TriplePattern> {
421        let subject = Self::parse_term;
422        let predicate = Self::parse_term;
423        let object = Self::parse_term;
424
425        (subject, predicate, object).parse_next(input)
426        .map(|(s, p, o)| TriplePattern { subject: s, predicate: p, object: o })
427    }
428
429    /// Parse SELECT clause
430    fn parse_select_clause(input: &mut &str) -> winnow::ModalResult<(bool, Vec<Variable>)> {
431        let distinct = winnow::combinator::opt("DISTINCT").map(|d| d.is_some());
432        let star_or_vars = winnow::combinator::alt((
433            "*".map(|_| vec![]), // SELECT * - variables will be determined later
434            winnow::combinator::repeat(1.., Self::parse_variable)
435        ));
436
437        winnow::combinator::preceded("SELECT", (distinct, star_or_vars)).parse_next(input)
438    }
439
440    /// Parse WHERE clause
441    fn parse_where_clause(input: &mut &str) -> winnow::ModalResult<GraphPattern> {
442        winnow::combinator::preceded(
443            "WHERE",
444            winnow::combinator::delimited(
445                '{',
446                Self::parse_group_graph_pattern,
447                '}'
448            )
449        ).parse_next(input)
450    }
451
452    /// Parse group graph pattern (simplified - just BGP for now)
453    fn parse_group_graph_pattern(input: &mut &str) -> winnow::ModalResult<GraphPattern> {
454        let mut triples = winnow::combinator::repeat(
455            0..,
456            winnow::combinator::terminated(
457                Self::parse_triple_pattern,
458                winnow::combinator::opt('.')
459            )
460        );
461
462        triples.parse_next(input).map(|ts| GraphPattern::Bgp(ts))
463    }
464
465    /// Parse PREFIX declaration
466    fn parse_prefix_declaration(input: &mut &str) -> winnow::ModalResult<(String, Iri)> {
467        let prefix = winnow::token::take_while(1.., |c: char| c.is_alphanumeric() || c == '_');
468        let iri = winnow::combinator::delimited(
469            '<',
470            winnow::token::take_while(1.., |c: char| c != '>'),
471            '>'
472        );
473
474        winnow::combinator::preceded(
475            "PREFIX",
476            (prefix, ':', iri)
477        ).parse_next(input)
478        .map(|(p, _, i): (&str, char, &str)| (p.to_string(), Iri(i.to_string())))
479    }
480}
481
482impl SparqlParser for DefaultSparqlParser {
483    fn parse(&self, query: &str) -> Result<SparqlQuery, crate::SparqlError> {
484        // Simple line-based parsing for now
485        let mut prefixes = HashMap::new();
486        let mut variables = Vec::new();
487        let mut query_type = QueryType::Select;
488        let mut in_where = false;
489        let mut in_construct = false;
490        let mut triples = Vec::new();
491        let mut construct_triples = Vec::new();
492
493        for line in query.lines() {
494            let line = line.trim();
495            if line.is_empty() {
496                continue;
497            }
498
499            if line.starts_with("PREFIX") {
500                // Parse PREFIX declaration
501                if let Some(prefix_def) = line.strip_prefix("PREFIX") {
502                    let parts: Vec<&str> = prefix_def.trim().split_whitespace().collect();
503                    if parts.len() >= 2 {
504                        let prefix = parts[0].trim_end_matches(':');
505                        let iri_str = parts[1].trim_matches('<').trim_matches('>');
506                        prefixes.insert(prefix.to_string(), Iri(iri_str.to_string()));
507                    }
508                }
509            } else if line.starts_with("SELECT") {
510                // Parse SELECT variables
511                if let Some(var_part) = line.strip_prefix("SELECT") {
512                    let var_part = var_part.trim();
513                    if var_part.starts_with("DISTINCT") {
514                        // TODO: Handle DISTINCT
515                        continue;
516                    }
517                    if var_part == "*" {
518                        // SELECT * - no specific variables
519                        continue;
520                    }
521                    for part in var_part.split_whitespace() {
522                        if part.starts_with('?') {
523                            variables.push(Variable(part[1..].to_string()));
524                        }
525                    }
526                }
527            } else if line.starts_with("ASK") {
528                // ASK query - no variables needed, just WHERE clause
529                query_type = QueryType::Ask;
530                in_where = true;
531            } else if line.starts_with("CONSTRUCT") {
532                // CONSTRUCT query - parse construct template
533                query_type = QueryType::Construct(vec![]);
534                in_construct = true;
535            } else if line.starts_with("WHERE") {
536                in_where = true;
537                in_construct = false; // Switch from construct to where
538            } else if in_construct && line.trim().ends_with('.') {
539                // Parse construct triple template
540                let line = line.trim();
541                let line = &line[..line.len()-1]; // Remove the trailing dot
542                let parts: Vec<&str> = line.split_whitespace().collect();
543                if parts.len() >= 3 {
544                    let subject = if parts[0].starts_with('?') {
545                        Term::Variable(Variable(parts[0][1..].to_string()))
546                    } else if parts[0].starts_with('<') {
547                        Term::Iri(Iri(parts[0].trim_matches('<').trim_matches('>').to_string()))
548                    } else {
549                        continue; // Skip complex patterns for now
550                    };
551
552                    let predicate = if parts[1] == "a" {
553                        // "a" is shorthand for rdf:type
554                        Term::PrefixedName("rdf".to_string(), "type".to_string())
555                    } else if parts[1].starts_with('<') {
556                        Term::Iri(Iri(parts[1].trim_matches('<').trim_matches('>').to_string()))
557                    } else if parts[1].contains(':') {
558                        let colon_parts: Vec<&str> = parts[1].split(':').collect();
559                        if colon_parts.len() == 2 {
560                            Term::PrefixedName(colon_parts[0].to_string(), colon_parts[1].to_string())
561                        } else {
562                            continue;
563                        }
564                    } else {
565                        continue;
566                    };
567
568                    let object = if parts[2].starts_with('?') {
569                        Term::Variable(Variable(parts[2][1..].to_string()))
570                    } else if parts[2].starts_with('<') {
571                        Term::Iri(Iri(parts[2].trim_matches('<').trim_matches('>').to_string()))
572                    } else if parts[2].starts_with('"') && parts[2].ends_with('"') {
573                        let value = parts[2].trim_matches('"').to_string();
574                        Term::Literal(Literal {
575                            value,
576                            datatype: None,
577                            language: None,
578                        })
579                    } else if parts[2].contains(':') {
580                        let colon_parts: Vec<&str> = parts[2].split(':').collect();
581                        if colon_parts.len() == 2 {
582                            Term::PrefixedName(colon_parts[0].to_string(), colon_parts[1].to_string())
583                        } else {
584                            continue;
585                        }
586                    } else {
587                        continue;
588                    };
589
590                           println!("DEBUG: Adding construct triple: subject={:?}, predicate={:?}, object={:?}", subject, predicate, object);
591                           construct_triples.push(TriplePattern {
592                               subject,
593                               predicate,
594                               object,
595                           });
596                }
597            } else if in_where && line.trim().ends_with('.') {
598                // Parse triple pattern (very simple)
599                let line = line.trim();
600                let line = &line[..line.len()-1]; // Remove the trailing dot
601                let parts: Vec<&str> = line.split_whitespace().collect();
602                if parts.len() >= 3 {
603                    let subject = if parts[0].starts_with('?') {
604                        Term::Variable(Variable(parts[0][1..].to_string()))
605                    } else if parts[0].starts_with('<') {
606                        Term::Iri(Iri(parts[0].trim_matches('<').trim_matches('>').to_string()))
607                    } else {
608                        continue; // Skip complex patterns for now
609                    };
610
611                    let predicate = if parts[1] == "a" {
612                        // "a" is shorthand for rdf:type
613                        Term::PrefixedName("rdf".to_string(), "type".to_string())
614                    } else if parts[1].starts_with('<') {
615                        Term::Iri(Iri(parts[1].trim_matches('<').trim_matches('>').to_string()))
616                    } else if parts[1].contains(':') {
617                        let colon_parts: Vec<&str> = parts[1].split(':').collect();
618                        if colon_parts.len() == 2 {
619                            Term::PrefixedName(colon_parts[0].to_string(), colon_parts[1].to_string())
620                        } else {
621                            continue;
622                        }
623                    } else {
624                        continue;
625                    };
626
627                    let object = if parts[2].starts_with('?') {
628                        Term::Variable(Variable(parts[2][1..].to_string()))
629                    } else if parts[2].starts_with('<') {
630                        Term::Iri(Iri(parts[2].trim_matches('<').trim_matches('>').to_string()))
631                    } else if parts[2].starts_with('"') && parts[2].ends_with('"') {
632                        // Simple string literal
633                        let value = parts[2].trim_matches('"').to_string();
634                        Term::Literal(Literal {
635                            value,
636                            datatype: None,
637                            language: None,
638                        })
639                    } else if parts[2].contains(':') {
640                        let colon_parts: Vec<&str> = parts[2].split(':').collect();
641                        if colon_parts.len() == 2 {
642                            Term::PrefixedName(colon_parts[0].to_string(), colon_parts[1].to_string())
643                        } else {
644                            continue;
645                        }
646                    } else {
647                        continue;
648                    };
649
650                    triples.push(TriplePattern {
651                        subject,
652                        predicate,
653                        object,
654                    });
655                }
656            }
657        }
658
659        println!("DEBUG: Parsed query type: {:?}", query_type);
660        println!("DEBUG: Parsed variables: {:?}", variables);
661        println!("DEBUG: Parsed triples: {:?}", triples);
662        println!("DEBUG: Parsed construct triples: {:?}", construct_triples);
663        println!("DEBUG: Parsed prefixes: {:?}", prefixes);
664
665        // Set construct templates for CONSTRUCT queries
666        let final_query_type = match query_type {
667            QueryType::Construct(_) => QueryType::Construct(construct_triples),
668            _ => query_type,
669        };
670
671        Ok(SparqlQuery {
672            query_type: final_query_type,
673            variables,
674            dataset: vec![],
675            where_clause: GraphPattern::Bgp(triples),
676            solution_modifier: SolutionModifier {
677                group: None,
678                having: None,
679                order: None,
680                limit: None,
681                offset: None,
682                distinct: false,
683                reduced: false,
684            },
685            values: None,
686            base_iri: None,
687            prefixes,
688        })
689    }
690
691
692    fn parse_query(&self, query: &str) -> Result<SparqlQuery, crate::SparqlError> {
693        self.parse(query)
694    }
695}