Skip to main content

rust_store_core/pipeline/
parse.rs

1//! 语法分析:Token 序列 → AST
2
3use std::collections::HashMap;
4
5use super::ast::{Ast, RelAst};
6use super::token::{tokenize, Token};
7
8struct Parser {
9    tokens: Vec<Token>,
10    pos: usize,
11}
12
13/// `parse_body` 产出:`(字段列表, 关系列表)`
14type BodyParts = (Vec<String>, Vec<(String, RelAst)>);
15
16impl Parser {
17    fn peek(&self) -> Option<&Token> {
18        self.tokens.get(self.pos)
19    }
20
21    fn consume(&mut self, kind: &str, value: Option<&str>) -> Result<Token, String> {
22        let tk = match self.peek() {
23            None => {
24                return Err(format!(
25                    "期望 {}({}) 但已到达末尾",
26                    kind,
27                    value.unwrap_or("undefined")
28                ))
29            }
30            Some(t) => t.clone(),
31        };
32        if tk.kind != kind || value.map(|v| tk.value != v).unwrap_or(false) {
33            return Err(format!(
34                "期望 {}({}) 实际 {}({}) 位置 {}",
35                kind,
36                value.unwrap_or("undefined"),
37                tk.kind,
38                tk.value,
39                self.pos
40            ));
41        }
42        self.pos += 1;
43        Ok(tk)
44    }
45
46    fn parse_params(&mut self) -> Result<HashMap<String, String>, String> {
47        let mut p = HashMap::new();
48        let has_paren = matches!(self.peek(), Some(t) if t.kind == "p" && t.value == "(");
49        if !has_paren {
50            return Ok(p);
51        }
52        self.consume("p", Some("("))?;
53        loop {
54            if matches!(self.peek(), Some(t) if t.value == ")") {
55                break;
56            }
57            let key = self
58                .consume("id", None)?
59                .value
60                .trim_start_matches('$')
61                .to_string();
62            // `$pipeline` 直通已移除(D18:不留 Mongo 逃生舱)——显式报错而非静默忽略,
63            // 否则用户自控的 pipeline 参数会被无声丢弃(与「绝不静默」冲突)。
64            if key == "pipeline" {
65                return Err(
66                    "$pipeline 直通已移除:请改用标准 GQL($condition/$sort/$skip/$limit + 关系字段)"
67                        .to_string(),
68                );
69            }
70            self.consume("p", Some(":"))?;
71            let val = self.peek().cloned();
72            if let Some(v) = &val {
73                if v.kind == "ref" || v.kind == "id" {
74                    p.insert(key, v.value.clone());
75                }
76            }
77            self.pos += 1;
78            if matches!(self.peek(), Some(t) if t.value == ",") {
79                self.consume("p", Some(","))?;
80            }
81        }
82        self.consume("p", Some(")"))?;
83        Ok(p)
84    }
85
86    fn parse_body(&mut self) -> Result<BodyParts, String> {
87        let mut fields = Vec::new();
88        let mut relations = Vec::new();
89        let has_brace = matches!(self.peek(), Some(t) if t.kind == "p" && t.value == "{");
90        if !has_brace {
91            return Ok((fields, relations));
92        }
93        self.consume("p", Some("{"))?;
94        loop {
95            match self.peek() {
96                None => break,
97                Some(t) if t.value == "}" => break,
98                Some(t) if t.value == "," => {
99                    self.consume("p", Some(","))?;
100                    continue;
101                }
102                _ => {}
103            }
104            let name = self.consume("id", None)?.value;
105            let has_paren = matches!(self.peek(), Some(t) if t.kind == "p" && t.value == "(");
106            // 关系名后「参数列表 + 选择集」可同时出现(README 记载语法,
107            // 缺陷 D-01:has_brace 必须在消费完参数之后再采样,否则
108            // `Rel($limit:@l){f}` 的 `{` 会被误判为下一个字段导致解析失败)
109            let prm = if has_paren {
110                self.parse_params()?
111            } else {
112                HashMap::new()
113            };
114            let has_brace = matches!(self.peek(), Some(t) if t.kind == "p" && t.value == "{");
115            if has_paren || has_brace {
116                let (f, r) = if has_brace {
117                    self.parse_body()?
118                } else {
119                    (Vec::new(), Vec::new())
120                };
121                relations.push((
122                    name,
123                    RelAst {
124                        fields: f,
125                        relations: r,
126                        params: prm,
127                    },
128                ));
129            } else {
130                fields.push(name);
131            }
132            if matches!(self.peek(), Some(t) if t.value == ",") {
133                self.consume("p", Some(","))?;
134            }
135        }
136        self.consume("p", Some("}"))?;
137        Ok((fields, relations))
138    }
139}
140
141pub fn parse(tokens: &[Token]) -> Result<Ast, String> {
142    let mut p = Parser {
143        tokens: tokens.to_vec(),
144        pos: 0,
145    };
146    let model = p.consume("id", None)?.value;
147    let params = p.parse_params()?;
148    let (fields, relations) = p.parse_body()?;
149    Ok(Ast {
150        model,
151        params,
152        fields,
153        relations,
154    })
155}
156
157/// 解析 GQL 字符串 → AST
158pub fn parse_gql(gql: &str) -> Result<Ast, String> {
159    parse(&tokenize(gql))
160}