Skip to main content

nargo_parser/
shell.rs

1use crate::{ParseState, ParserRegistry};
2use nargo_ir::{CustomBlockIR, IRModule, StyleIR, TemplateIR};
3use nargo_types::{Cursor, Result, Span};
4use std::{collections::HashMap, sync::Arc};
5
6pub struct VocShell<'a> {
7    name: String,
8    state: ParseState<'a>,
9    registry: Arc<ParserRegistry>,
10}
11
12impl<'a> VocShell<'a> {
13    pub fn new(name: String, source: &'a str, registry: Arc<ParserRegistry>) -> Self {
14        Self { name, state: ParseState::new(source), registry }
15    }
16
17    pub fn parse_all(&mut self) -> Result<IRModule> {
18        self.parse_module()
19    }
20
21    pub fn parse_module(&mut self) -> Result<IRModule> {
22        let mut template_nodes = Vec::new();
23        let mut scripts = Vec::new();
24        let mut scripts_server = Vec::new();
25        let mut scripts_client = Vec::new();
26        let mut ir_styles = Vec::new();
27        let mut metadata = HashMap::new();
28        let mut custom_blocks = Vec::new();
29
30        while !self.state.cursor.is_eof() {
31            self.state.cursor.skip_whitespace();
32            if self.state.cursor.is_eof() {
33                break;
34            }
35
36            if self.state.cursor.peek_str("<template") {
37                self.parse_template_block(&mut template_nodes)?;
38            }
39            else if self.state.cursor.peek_str("<script") {
40                self.parse_script_block(&mut scripts, &mut scripts_server, &mut scripts_client)?;
41            }
42            else if self.state.cursor.peek_str("<style") {
43                self.parse_style_block(&mut ir_styles)?;
44            }
45            else if self.state.cursor.peek_str("<metadata") {
46                self.parse_metadata_block(&mut metadata)?;
47            }
48            else if self.state.cursor.peek_str("<") {
49                self.parse_custom_block(&mut custom_blocks)?;
50            }
51            else {
52                self.state.cursor.consume();
53            }
54        }
55
56        let script = self.merge_scripts(scripts);
57        let script_server = self.merge_scripts(scripts_server);
58        let script_client = self.merge_scripts(scripts_client);
59
60        // Analyze scripts to extract metadata (signals, props, emits)
61        let mut script_meta = None;
62        if let Some(s) = &script {
63            let analyzer = nargo_script_analyzer::ScriptAnalyzer::new();
64            if let Ok(meta) = analyzer.analyze(s) {
65                script_meta = Some(meta.to_nargo_value());
66            }
67        }
68
69        Ok(IRModule { name: self.name.clone(), metadata, script, script_server, script_client, script_meta, template: if template_nodes.is_empty() { None } else { Some(TemplateIR { nodes: template_nodes, span: Span::default() }) }, hoisted_nodes: HashMap::new(), styles: ir_styles, i18n: None, wasm: Vec::new(), custom_blocks, tests: Vec::new(), span: Span::default() })
70    }
71
72    fn parse_template_block(&mut self, nodes: &mut Vec<nargo_ir::TemplateNodeIR>) -> Result<()> {
73        let (content, attrs, start_pos) = self.parse_special_tag("template")?;
74        let lang = attrs.get("lang").cloned().unwrap_or_else(|| "nargo".to_string());
75        if let Some(template_parser) = self.registry.get_template_parser(&lang) {
76            let mut sub_state = ParseState::with_cursor(Cursor::with_sliced_source(content, start_pos));
77            nodes.extend(template_parser.parse(&mut sub_state, &lang)?);
78        }
79        Ok(())
80    }
81
82    fn parse_script_block(&mut self, scripts: &mut Vec<nargo_ir::JsProgram>, server: &mut Vec<nargo_ir::JsProgram>, client: &mut Vec<nargo_ir::JsProgram>) -> Result<()> {
83        let (content, attrs, start_pos) = self.parse_special_tag("script")?;
84        let lang = attrs.get("lang").cloned().unwrap_or_else(|| "ts".to_string());
85        let is_server = attrs.contains_key("server");
86        let is_client = attrs.contains_key("client");
87
88        if let Some(script_parser) = self.registry.get_script_parser(&lang) {
89            let mut sub_state = ParseState::with_cursor(Cursor::with_sliced_source(content, start_pos));
90            let program = script_parser.parse(&mut sub_state, &lang)?;
91
92            if is_server {
93                server.push(program);
94            }
95            else if is_client {
96                client.push(program);
97            }
98            else {
99                scripts.push(program);
100            }
101        }
102        Ok(())
103    }
104
105    fn parse_style_block(&mut self, styles: &mut Vec<StyleIR>) -> Result<()> {
106        let (content, attrs, start_pos) = self.parse_special_tag("style")?;
107        let lang = attrs.get("lang").cloned().unwrap_or_else(|| "css".to_string());
108        let scoped = attrs.contains_key("scoped");
109        let span = self.state.cursor.span_from(start_pos);
110
111        if let Some(style_parser) = self.registry.get_style_parser(&lang) {
112            let mut sub_state = ParseState::with_cursor(Cursor::with_sliced_source(content, start_pos));
113            if let Ok((code, trivia)) = style_parser.parse(&mut sub_state, &lang) {
114                styles.push(StyleIR { code, lang, scoped, span, trivia });
115            }
116        }
117        Ok(())
118    }
119
120    fn parse_metadata_block(&mut self, metadata: &mut HashMap<String, nargo_types::NargoValue>) -> Result<()> {
121        let (content, attrs, start_pos) = self.parse_special_tag("metadata")?;
122        let lang = attrs.get("lang").cloned().unwrap_or_else(|| "json".to_string());
123        if let Some(metadata_parser) = self.registry.get_metadata_parser(&lang) {
124            let mut sub_state = ParseState::with_cursor(Cursor::with_sliced_source(content, start_pos));
125            if let Ok((nargo_types::NargoValue::Object(map), _trivia)) = metadata_parser.parse(&mut sub_state, &lang) {
126                metadata.extend(map);
127            }
128        }
129        Ok(())
130    }
131
132    fn parse_custom_block(&mut self, blocks: &mut Vec<CustomBlockIR>) -> Result<()> {
133        self.state.cursor.consume(); // consume '<'
134        let block_name = self.state.cursor.consume_while(|c| c.is_alphanumeric() || c == '-');
135        let attrs = self.state.parse_tag_attributes();
136        self.state.cursor.expect('>')?;
137
138        let start_pos = self.state.cursor.position();
139        let start_offset = self.state.cursor.pos;
140        let end_tag = format!("</{}>", block_name);
141
142        // 优化:使用更高效的字符串查找算法,而不是逐字符消耗
143        let remaining_source = &self.state.cursor.source[start_offset..];
144        if let Some(end_offset) = remaining_source.find(&end_tag) {
145            self.state.cursor.pos = start_offset + end_offset;
146        }
147        else {
148            // 如果没有找到结束标签,继续消耗直到EOF
149            while !self.state.cursor.is_eof() {
150                self.state.cursor.consume();
151            }
152        }
153
154        let content = self.state.cursor.current_str(start_offset).to_string();
155        let span = self.state.cursor.span_from(start_pos);
156        self.state.cursor.consume_str(&end_tag);
157
158        blocks.push(CustomBlockIR { name: block_name, content, attributes: attrs, span, trivia: nargo_ir::Trivia::default() });
159        Ok(())
160    }
161
162    fn parse_special_tag(&mut self, tag: &str) -> Result<(&'a str, HashMap<String, String>, nargo_types::Position)> {
163        let start_tag = format!("<{}", tag);
164        let end_tag = format!("</{}>", tag);
165
166        self.state.cursor.consume_str(&start_tag);
167        let attrs = self.state.parse_tag_attributes();
168        self.state.cursor.expect('>')?;
169
170        let start_pos = self.state.cursor.position();
171        let start_offset = self.state.cursor.pos;
172
173        // 优化:使用更高效的字符串查找算法,而不是逐字符消耗
174        let remaining_source = &self.state.cursor.source[start_offset..];
175        if let Some(end_offset) = remaining_source.find(&end_tag) {
176            self.state.cursor.pos = start_offset + end_offset;
177        }
178        else {
179            // 如果没有找到结束标签,继续消耗直到EOF
180            while !self.state.cursor.is_eof() {
181                self.state.cursor.consume();
182            }
183        }
184
185        let content = &self.state.cursor.source[start_offset..self.state.cursor.pos];
186        self.state.cursor.consume_str(&end_tag);
187
188        Ok((content, attrs, start_pos))
189    }
190
191    fn merge_scripts(&self, mut scripts: Vec<nargo_ir::JsProgram>) -> Option<nargo_ir::JsProgram> {
192        if scripts.is_empty() {
193            return None;
194        }
195        let mut first = scripts.remove(0);
196        for mut script in scripts {
197            first.body.append(&mut script.body);
198        }
199        Some(first)
200    }
201}