Skip to main content

vm/compiler/parser/
mod.rs

1mod cursor;
2mod expressions;
3mod format;
4mod lexer;
5mod lint;
6mod statements;
7mod symbols;
8
9use std::collections::{HashMap, HashSet};
10
11use rt_format::{NoNamedArguments, ParsedFormat};
12
13use crate::ValueType;
14use crate::builtins::{
15    BuiltinFunction, builtin_namespace_hint, default_host_callable, is_builtin_namespace,
16    resolve_builtin_namespace_call,
17};
18use crate::compiler::source_map::{SourceId, Span};
19
20use self::lexer::{Lexer, ParserFormatArg, Token, TokenKind, is_ident_continue, is_ident_start};
21use self::symbols::is_virtual_host_namespace_spec;
22use super::{
23    ParseError, ReplLocalBinding, STDLIB_PRINT_ARITY, STDLIB_PRINT_NAME,
24    ir::{
25        AssignmentKind, ClosureExpr, Expr, FunctionDecl, FunctionImpl, FunctionParam, LocalSlot,
26        MatchPattern, MatchTypePattern, Stmt, StructDecl, TypeSchema,
27    },
28};
29
30pub trait ParserDialect {
31    fn is_import_keyword(&self, _ident: &str) -> bool {
32        false
33    }
34
35    fn is_from_keyword(&self, _ident: &str) -> bool {
36        false
37    }
38
39    fn is_fn_alias_keyword(&self, _ident: &str) -> bool {
40        false
41    }
42
43    fn is_let_alias_keyword(&self, _ident: &str) -> bool {
44        false
45    }
46
47    fn allow_import_stmt(&self) -> bool {
48        false
49    }
50
51    fn allow_return_stmt(&self) -> bool {
52        false
53    }
54
55    fn allow_require_declaration(&self) -> bool {
56        false
57    }
58
59    fn allow_typeof_operator(&self) -> bool {
60        false
61    }
62
63    fn allow_arrow_closure(&self) -> bool {
64        false
65    }
66
67    fn allow_dotted_call(&self) -> bool {
68        false
69    }
70
71    fn allow_namespace_path_separator(&self) -> bool {
72        true
73    }
74
75    fn allow_let_mut_binding(&self) -> bool {
76        false
77    }
78
79    fn allow_macro_calls(&self) -> bool {
80        false
81    }
82
83    fn allow_plus_equal_operator(&self) -> bool {
84        false
85    }
86
87    fn allow_increment_operator(&self) -> bool {
88        false
89    }
90
91    fn allow_parenthesized_for_loop(&self) -> bool {
92        false
93    }
94
95    fn allow_for_in_loop(&self) -> bool {
96        false
97    }
98}
99
100pub(super) fn lint_trailing_function_return_semicolons(
101    source: &str,
102    source_id: SourceId,
103    dialect: &'static dyn ParserDialect,
104) -> Result<Vec<ParseError>, ParseError> {
105    lint::lint_trailing_function_return_semicolons(source, source_id, dialect)
106}
107
108pub(super) fn format_source(
109    source: &str,
110    dialect: &'static dyn ParserDialect,
111) -> Result<String, ParseError> {
112    format::format_source(source, dialect)
113}
114
115pub(super) struct Parser {
116    tokens: Vec<Token>,
117    pos: usize,
118    locals: HashMap<String, LocalSlot>,
119    named_local_bindings: Vec<(String, LocalSlot)>,
120    next_local: LocalSlot,
121    functions: HashMap<String, FunctionDecl>,
122    function_list: Vec<FunctionDecl>,
123    function_impls: HashMap<u16, FunctionImpl>,
124    parsed_function_decls: HashSet<u16>,
125    next_function: u16,
126    closure_scopes: Vec<HashMap<String, LocalSlot>>,
127    closure_capture_contexts: Vec<ClosureCaptureContext>,
128    struct_schemas: HashMap<String, StructDecl>,
129    schema_reference_sites: Vec<(String, usize, usize, Span)>,
130    active_type_params: Vec<HashSet<String>>,
131    unknown_type_spans: Vec<Span>,
132    allow_implicit_externs: bool,
133    allow_implicit_semicolons: bool,
134    enforce_mutable_bindings: bool,
135    dialect: &'static dyn ParserDialect,
136    loop_depth: usize,
137    function_body_depth: usize,
138    host_namespace_aliases: HashMap<String, String>,
139    direct_host_call_aliases: HashMap<String, String>,
140    direct_host_wildcard_imports: HashSet<String>,
141    mutable_locals: Vec<bool>,
142    borrowed_map_iter_locals: Vec<LocalSlot>,
143    local_schemas: HashMap<LocalSlot, TypeSchema>,
144}
145
146struct ClosureCaptureContext {
147    by_name: HashMap<String, LocalSlot>,
148    capture_copies: Vec<(LocalSlot, LocalSlot)>,
149}
150
151impl Parser {
152    pub(super) fn new(
153        source: &str,
154        source_id: SourceId,
155        allow_implicit_externs: bool,
156        allow_implicit_semicolons: bool,
157        enforce_mutable_bindings: bool,
158        dialect: &'static dyn ParserDialect,
159    ) -> Result<Self, ParseError> {
160        let mut lexer = Lexer::new(source, source_id, dialect);
161        let mut tokens = Vec::new();
162        loop {
163            let token = lexer.next_token()?;
164            let is_eof = matches!(token.kind, TokenKind::Eof);
165            tokens.push(token);
166            if is_eof {
167                break;
168            }
169        }
170        Ok(Self {
171            tokens,
172            pos: 0,
173            locals: HashMap::new(),
174            named_local_bindings: Vec::new(),
175            next_local: 0,
176            functions: HashMap::new(),
177            function_list: Vec::new(),
178            function_impls: HashMap::new(),
179            parsed_function_decls: HashSet::new(),
180            next_function: 0,
181            closure_scopes: Vec::new(),
182            closure_capture_contexts: Vec::new(),
183            struct_schemas: HashMap::new(),
184            schema_reference_sites: Vec::new(),
185            active_type_params: Vec::new(),
186            unknown_type_spans: Vec::new(),
187            allow_implicit_externs,
188            allow_implicit_semicolons,
189            enforce_mutable_bindings,
190            dialect,
191            loop_depth: 0,
192            function_body_depth: 0,
193            host_namespace_aliases: HashMap::new(),
194            direct_host_call_aliases: HashMap::new(),
195            direct_host_wildcard_imports: HashSet::new(),
196            mutable_locals: Vec::new(),
197            borrowed_map_iter_locals: Vec::new(),
198            local_schemas: HashMap::new(),
199        })
200    }
201
202    pub(super) fn new_with_predeclared_locals(
203        source: &str,
204        source_id: SourceId,
205        allow_implicit_externs: bool,
206        allow_implicit_semicolons: bool,
207        enforce_mutable_bindings: bool,
208        dialect: &'static dyn ParserDialect,
209        predeclared_locals: &[ReplLocalBinding],
210    ) -> Result<Self, ParseError> {
211        let mut parser = Self::new(
212            source,
213            source_id,
214            allow_implicit_externs,
215            allow_implicit_semicolons,
216            enforce_mutable_bindings,
217            dialect,
218        )?;
219        for binding in predeclared_locals {
220            parser.predeclare_local(binding)?;
221        }
222        Ok(parser)
223    }
224
225    pub(super) fn parse_program(&mut self) -> Result<Vec<Stmt>, ParseError> {
226        self.predeclare_functions()?;
227        let mut stmts = Vec::new();
228        while !self.check(&TokenKind::Eof) {
229            stmts.push(self.parse_stmt()?);
230        }
231        self.validate_schema_reference_sites()?;
232        Ok(stmts)
233    }
234
235    fn predeclare_functions(&mut self) -> Result<(), ParseError> {
236        let mut index = 0usize;
237        while index < self.tokens.len() {
238            match &self.tokens[index].kind {
239                TokenKind::Fn => {
240                    let line = self.tokens[index].line;
241                    let Some(Token {
242                        kind: TokenKind::Ident(name),
243                        ..
244                    }) = self.tokens.get(index + 1)
245                    else {
246                        index += 1;
247                        continue;
248                    };
249                    let name = name.clone();
250                    let exported =
251                        index > 0 && matches!(self.tokens[index - 1].kind, TokenKind::Pub);
252                    let mut cursor = index + 2;
253                    let mut type_params = Vec::new();
254                    if self
255                        .tokens
256                        .get(cursor)
257                        .is_some_and(|token| matches!(token.kind, TokenKind::Less))
258                    {
259                        cursor += 1;
260                        while let Some(token) = self.tokens.get(cursor) {
261                            match &token.kind {
262                                TokenKind::Ident(param) => type_params.push(param.clone()),
263                                TokenKind::Greater => {
264                                    cursor += 1;
265                                    break;
266                                }
267                                _ => {}
268                            }
269                            cursor += 1;
270                        }
271                    }
272                    if !self
273                        .tokens
274                        .get(cursor)
275                        .is_some_and(|token| matches!(token.kind, TokenKind::LParen))
276                    {
277                        index += 1;
278                        continue;
279                    }
280                    cursor += 1;
281                    let mut arity = 0usize;
282                    let mut angle_depth = 0usize;
283                    let mut paren_depth = 1usize;
284                    let mut has_param = false;
285                    while let Some(token) = self.tokens.get(cursor) {
286                        match token.kind {
287                            TokenKind::LParen => paren_depth += 1,
288                            TokenKind::RParen if paren_depth == 1 && angle_depth == 0 => {
289                                if has_param {
290                                    arity += 1;
291                                }
292                                break;
293                            }
294                            TokenKind::RParen => paren_depth = paren_depth.saturating_sub(1),
295                            TokenKind::Less => angle_depth += 1,
296                            TokenKind::Greater => angle_depth = angle_depth.saturating_sub(1),
297                            TokenKind::Comma if paren_depth == 1 && angle_depth == 0 => {
298                                if has_param {
299                                    arity += 1;
300                                    has_param = false;
301                                }
302                            }
303                            _ if paren_depth == 1 && angle_depth == 0 => has_param = true,
304                            _ => {}
305                        }
306                        cursor += 1;
307                    }
308                    if self.functions.contains_key(&name) {
309                        return Err(ParseError {
310                            span: None,
311                            code: None,
312                            line,
313                            message: format!("duplicate function '{name}'"),
314                        });
315                    }
316                    let arity = u8::try_from(arity).map_err(|_| ParseError {
317                        span: None,
318                        code: None,
319                        line,
320                        message: "function arity too large".to_string(),
321                    })?;
322                    let function_index = self.next_function;
323                    self.next_function = self.next_function.checked_add(1).ok_or(ParseError {
324                        span: None,
325                        code: None,
326                        line,
327                        message: "function index overflow".to_string(),
328                    })?;
329                    let decl = FunctionDecl {
330                        name: name.clone(),
331                        arity,
332                        index: function_index,
333                        args: vec![String::new(); usize::from(arity)],
334                        arg_schemas: vec![None; usize::from(arity)],
335                        return_schema: None,
336                        type_params,
337                        exported,
338                        return_type: ValueType::Unknown,
339                    };
340                    self.functions.insert(name, decl.clone());
341                    self.function_list.push(decl);
342                    index = cursor;
343                }
344                _ => index += 1,
345            }
346        }
347        Ok(())
348    }
349
350    pub(super) fn local_count(&self) -> usize {
351        self.next_local as usize
352    }
353
354    pub(super) fn function_decls(&self) -> Vec<FunctionDecl> {
355        self.function_list.clone()
356    }
357
358    pub(super) fn function_impls(&self) -> HashMap<u16, FunctionImpl> {
359        self.function_impls.clone()
360    }
361
362    pub(super) fn local_bindings(&self) -> Vec<(String, LocalSlot)> {
363        let mut locals = self.named_local_bindings.clone();
364        locals.sort_by_key(|(_, index)| *index);
365        locals
366    }
367
368    pub(super) fn local_bindings_with_mutability(&self) -> Vec<ReplLocalBinding> {
369        let mut locals = self
370            .locals
371            .iter()
372            .map(|(name, index)| ReplLocalBinding {
373                name: name.clone(),
374                mutable: self.is_local_slot_mutable(*index),
375                schema: None,
376                optional: false,
377            })
378            .collect::<Vec<_>>();
379        locals.sort_by_key(|binding| self.locals.get(&binding.name).copied().unwrap_or(0));
380        locals
381    }
382
383    pub(super) fn struct_schemas(&self) -> HashMap<String, StructDecl> {
384        self.struct_schemas.clone()
385    }
386
387    pub(super) fn unknown_type_spans(&self) -> Vec<Span> {
388        self.unknown_type_spans.clone()
389    }
390
391    fn validate_schema_reference_sites(&self) -> Result<(), ParseError> {
392        for (name, arg_count, line, span) in &self.schema_reference_sites {
393            let Some(decl) = self.struct_schemas.get(name) else {
394                return Err(ParseError {
395                    span: Some(*span),
396                    code: None,
397                    line: *line,
398                    message: format!("unknown struct schema '{name}'"),
399                });
400            };
401            if decl.type_params.len() != *arg_count {
402                return Err(ParseError {
403                    span: Some(*span),
404                    code: None,
405                    line: *line,
406                    message: format!(
407                        "struct schema '{name}' expects {} type arguments, got {}",
408                        decl.type_params.len(),
409                        arg_count
410                    ),
411                });
412            }
413            if self.struct_schemas.contains_key(name) {
414                continue;
415            }
416        }
417        Ok(())
418    }
419
420    fn push_active_type_params(&mut self, params: &[String]) {
421        self.active_type_params
422            .push(params.iter().cloned().collect::<HashSet<_>>());
423    }
424
425    fn pop_active_type_params(&mut self) {
426        self.active_type_params.pop();
427    }
428
429    fn is_active_type_param(&self, name: &str) -> bool {
430        self.active_type_params
431            .iter()
432            .rev()
433            .any(|params| params.contains(name))
434    }
435
436    fn parse_type_params(
437        &mut self,
438        owner: &str,
439        owner_name: &str,
440    ) -> Result<Vec<String>, ParseError> {
441        if !self.check(&TokenKind::Less) {
442            return Ok(Vec::new());
443        }
444
445        self.expect(&TokenKind::Less, "expected '<' before type parameters")?;
446        let mut params = Vec::new();
447        let mut seen = HashSet::new();
448        loop {
449            let param = self.expect_ident("expected type parameter name")?;
450            if !seen.insert(param.clone()) {
451                return Err(ParseError {
452                    span: Some(self.current_span()),
453                    code: None,
454                    line: self.current_line(),
455                    message: format!(
456                        "duplicate type parameter '{param}' in {owner} '{owner_name}'"
457                    ),
458                });
459            }
460            params.push(param);
461            if self.match_kind(&TokenKind::Comma) {
462                continue;
463            }
464            break;
465        }
466        self.expect(&TokenKind::Greater, "expected '>' after type parameters")?;
467        Ok(params)
468    }
469
470    fn parse_turbofish_type_args(&mut self) -> Result<Vec<TypeSchema>, ParseError> {
471        if !self.check_path_separator() || !self.check_kind_at(self.pos + 2, &TokenKind::Less) {
472            return Ok(Vec::new());
473        }
474
475        self.match_path_separator();
476        self.expect(&TokenKind::Less, "expected '<' after '::' in turbofish")?;
477        let mut type_args = Vec::new();
478        loop {
479            type_args.push(self.parse_declared_type_schema()?);
480            if self.match_kind(&TokenKind::Comma) {
481                continue;
482            }
483            break;
484        }
485        self.expect(&TokenKind::Greater, "expected '>' after type arguments")?;
486        Ok(type_args)
487    }
488
489    fn function_param_names(params: &[FunctionParam]) -> Vec<String> {
490        params.iter().map(|param| param.name.clone()).collect()
491    }
492}