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            // 语法层只做「按名取值」:`$pipeline` 的档位判决不在解析层
58            // (判决唯一在消费点 `pipeline/build.rs::build_pipeline`,那里有 §4.5 注释块)。
59            let key = self
60                .consume("id", None)?
61                .value
62                .trim_start_matches('$')
63                .to_string();
64            self.consume("p", Some(":"))?;
65            let val = self.peek().cloned();
66            if let Some(v) = &val {
67                if v.kind == "ref" || v.kind == "id" {
68                    p.insert(key, v.value.clone());
69                }
70            }
71            self.pos += 1;
72            if matches!(self.peek(), Some(t) if t.value == ",") {
73                self.consume("p", Some(","))?;
74            }
75        }
76        self.consume("p", Some(")"))?;
77        Ok(p)
78    }
79
80    fn parse_body(&mut self) -> Result<BodyParts, String> {
81        let mut fields = Vec::new();
82        let mut relations = Vec::new();
83        let has_brace = matches!(self.peek(), Some(t) if t.kind == "p" && t.value == "{");
84        if !has_brace {
85            return Ok((fields, relations));
86        }
87        self.consume("p", Some("{"))?;
88        loop {
89            match self.peek() {
90                None => break,
91                Some(t) if t.value == "}" => break,
92                Some(t) if t.value == "," => {
93                    self.consume("p", Some(","))?;
94                    continue;
95                }
96                _ => {}
97            }
98            let name = self.consume("id", None)?.value;
99            let has_paren = matches!(self.peek(), Some(t) if t.kind == "p" && t.value == "(");
100            // 关系名后「参数列表 + 选择集」可同时出现(README 记载语法,
101            // 缺陷 D-01:has_brace 必须在消费完参数之后再采样,否则
102            // `Rel($limit:@l){f}` 的 `{` 会被误判为下一个字段导致解析失败)
103            let prm = if has_paren {
104                self.parse_params()?
105            } else {
106                HashMap::new()
107            };
108            // 关系级 `$pipeline` 从不被消费(`$pipeline` 直通仅限根级 GQL)→ 两档一律显式报错,
109            // 否则该参数会被静默丢弃(与「绝不静默」冲突)。
110            if prm.contains_key("pipeline") {
111                return Err(format!(
112                    "关系 \"{name}\" 的参数不支持 $pipeline($pipeline 直通仅限根级 GQL)"
113                ));
114            }
115            let has_brace = matches!(self.peek(), Some(t) if t.kind == "p" && t.value == "{");
116            if has_paren || has_brace {
117                let (f, r) = if has_brace {
118                    self.parse_body()?
119                } else {
120                    (Vec::new(), Vec::new())
121                };
122                relations.push((
123                    name,
124                    RelAst {
125                        fields: f,
126                        relations: r,
127                        params: prm,
128                    },
129                ));
130            } else {
131                fields.push(name);
132            }
133            if matches!(self.peek(), Some(t) if t.value == ",") {
134                self.consume("p", Some(","))?;
135            }
136        }
137        self.consume("p", Some("}"))?;
138        Ok((fields, relations))
139    }
140}
141
142pub fn parse(tokens: &[Token]) -> Result<Ast, String> {
143    let mut p = Parser {
144        tokens: tokens.to_vec(),
145        pos: 0,
146    };
147    let model = p.consume("id", None)?.value;
148    let params = p.parse_params()?;
149    let (fields, relations) = p.parse_body()?;
150    Ok(Ast {
151        model,
152        params,
153        fields,
154        relations,
155    })
156}
157
158/// 解析 GQL 字符串 → AST
159pub fn parse_gql(gql: &str) -> Result<Ast, String> {
160    parse(&tokenize(gql))
161}