pd-vm 0.22.5

RustScript bytecode compiler and VM
Documentation
mod cursor;
mod expressions;
mod format;
mod lexer;
mod lint;
mod statements;
mod symbols;

use std::collections::{HashMap, HashSet};

use rt_format::{NoNamedArguments, ParsedFormat};

use crate::ValueType;
use crate::builtins::{
    BuiltinFunction, builtin_namespace_hint, default_host_callable, is_builtin_namespace,
    resolve_builtin_namespace_call,
};
use crate::compiler::source_map::{SourceId, Span};

use self::lexer::{Lexer, ParserFormatArg, Token, TokenKind, is_ident_continue, is_ident_start};
use self::symbols::is_virtual_host_namespace_spec;
use super::{
    ParseError, ReplLocalBinding, STDLIB_PRINT_ARITY, STDLIB_PRINT_NAME,
    ir::{
        AssignmentKind, ClosureExpr, Expr, FunctionDecl, FunctionImpl, FunctionParam, LocalSlot,
        MatchPattern, MatchTypePattern, Stmt, StructDecl, TypeSchema,
    },
};

pub trait ParserDialect {
    fn is_import_keyword(&self, _ident: &str) -> bool {
        false
    }

    fn is_from_keyword(&self, _ident: &str) -> bool {
        false
    }

    fn is_fn_alias_keyword(&self, _ident: &str) -> bool {
        false
    }

    fn is_let_alias_keyword(&self, _ident: &str) -> bool {
        false
    }

    fn allow_import_stmt(&self) -> bool {
        false
    }

    fn allow_return_stmt(&self) -> bool {
        false
    }

    fn allow_require_declaration(&self) -> bool {
        false
    }

    fn allow_typeof_operator(&self) -> bool {
        false
    }

    fn allow_arrow_closure(&self) -> bool {
        false
    }

    fn allow_dotted_call(&self) -> bool {
        false
    }

    fn allow_namespace_path_separator(&self) -> bool {
        true
    }

    fn allow_let_mut_binding(&self) -> bool {
        false
    }

    fn allow_macro_calls(&self) -> bool {
        false
    }

    fn allow_plus_equal_operator(&self) -> bool {
        false
    }

    fn allow_increment_operator(&self) -> bool {
        false
    }

    fn allow_parenthesized_for_loop(&self) -> bool {
        false
    }

    fn allow_for_in_loop(&self) -> bool {
        false
    }
}

pub(super) fn lint_trailing_function_return_semicolons(
    source: &str,
    source_id: SourceId,
    dialect: &'static dyn ParserDialect,
) -> Result<Vec<ParseError>, ParseError> {
    lint::lint_trailing_function_return_semicolons(source, source_id, dialect)
}

pub(super) fn format_source(
    source: &str,
    dialect: &'static dyn ParserDialect,
) -> Result<String, ParseError> {
    format::format_source(source, dialect)
}

pub(super) struct Parser {
    tokens: Vec<Token>,
    pos: usize,
    locals: HashMap<String, LocalSlot>,
    named_local_bindings: Vec<(String, LocalSlot)>,
    next_local: LocalSlot,
    functions: HashMap<String, FunctionDecl>,
    function_list: Vec<FunctionDecl>,
    function_impls: HashMap<u16, FunctionImpl>,
    next_function: u16,
    closure_scopes: Vec<HashMap<String, LocalSlot>>,
    closure_capture_contexts: Vec<ClosureCaptureContext>,
    struct_schemas: HashMap<String, StructDecl>,
    schema_reference_sites: Vec<(String, usize, usize, Span)>,
    active_type_params: Vec<HashSet<String>>,
    unknown_type_spans: Vec<Span>,
    allow_implicit_externs: bool,
    allow_implicit_semicolons: bool,
    enforce_mutable_bindings: bool,
    dialect: &'static dyn ParserDialect,
    loop_depth: usize,
    function_body_depth: usize,
    host_namespace_aliases: HashMap<String, String>,
    direct_host_call_aliases: HashMap<String, String>,
    direct_host_wildcard_imports: HashSet<String>,
    mutable_locals: Vec<bool>,
}

struct ClosureCaptureContext {
    by_name: HashMap<String, LocalSlot>,
    capture_copies: Vec<(LocalSlot, LocalSlot)>,
}

impl Parser {
    pub(super) fn new(
        source: &str,
        source_id: SourceId,
        allow_implicit_externs: bool,
        allow_implicit_semicolons: bool,
        enforce_mutable_bindings: bool,
        dialect: &'static dyn ParserDialect,
    ) -> Result<Self, ParseError> {
        let mut lexer = Lexer::new(source, source_id, dialect);
        let mut tokens = Vec::new();
        loop {
            let token = lexer.next_token()?;
            let is_eof = matches!(token.kind, TokenKind::Eof);
            tokens.push(token);
            if is_eof {
                break;
            }
        }
        Ok(Self {
            tokens,
            pos: 0,
            locals: HashMap::new(),
            named_local_bindings: Vec::new(),
            next_local: 0,
            functions: HashMap::new(),
            function_list: Vec::new(),
            function_impls: HashMap::new(),
            next_function: 0,
            closure_scopes: Vec::new(),
            closure_capture_contexts: Vec::new(),
            struct_schemas: HashMap::new(),
            schema_reference_sites: Vec::new(),
            active_type_params: Vec::new(),
            unknown_type_spans: Vec::new(),
            allow_implicit_externs,
            allow_implicit_semicolons,
            enforce_mutable_bindings,
            dialect,
            loop_depth: 0,
            function_body_depth: 0,
            host_namespace_aliases: HashMap::new(),
            direct_host_call_aliases: HashMap::new(),
            direct_host_wildcard_imports: HashSet::new(),
            mutable_locals: Vec::new(),
        })
    }

    pub(super) fn new_with_predeclared_locals(
        source: &str,
        source_id: SourceId,
        allow_implicit_externs: bool,
        allow_implicit_semicolons: bool,
        enforce_mutable_bindings: bool,
        dialect: &'static dyn ParserDialect,
        predeclared_locals: &[ReplLocalBinding],
    ) -> Result<Self, ParseError> {
        let mut parser = Self::new(
            source,
            source_id,
            allow_implicit_externs,
            allow_implicit_semicolons,
            enforce_mutable_bindings,
            dialect,
        )?;
        for binding in predeclared_locals {
            parser.predeclare_local(binding)?;
        }
        Ok(parser)
    }

    pub(super) fn parse_program(&mut self) -> Result<Vec<Stmt>, ParseError> {
        let mut stmts = Vec::new();
        while !self.check(&TokenKind::Eof) {
            stmts.push(self.parse_stmt()?);
        }
        self.validate_schema_reference_sites()?;
        Ok(stmts)
    }

    pub(super) fn local_count(&self) -> usize {
        self.next_local as usize
    }

    pub(super) fn function_decls(&self) -> Vec<FunctionDecl> {
        self.function_list.clone()
    }

    pub(super) fn function_impls(&self) -> HashMap<u16, FunctionImpl> {
        self.function_impls.clone()
    }

    pub(super) fn local_bindings(&self) -> Vec<(String, LocalSlot)> {
        let mut locals = self.named_local_bindings.clone();
        locals.sort_by_key(|(_, index)| *index);
        locals
    }

    pub(super) fn local_bindings_with_mutability(&self) -> Vec<ReplLocalBinding> {
        let mut locals = self
            .locals
            .iter()
            .map(|(name, index)| ReplLocalBinding {
                name: name.clone(),
                mutable: self.is_local_slot_mutable(*index),
                schema: None,
                optional: false,
            })
            .collect::<Vec<_>>();
        locals.sort_by_key(|binding| self.locals.get(&binding.name).copied().unwrap_or(0));
        locals
    }

    pub(super) fn struct_schemas(&self) -> HashMap<String, StructDecl> {
        self.struct_schemas.clone()
    }

    pub(super) fn unknown_type_spans(&self) -> Vec<Span> {
        self.unknown_type_spans.clone()
    }

    fn validate_schema_reference_sites(&self) -> Result<(), ParseError> {
        for (name, arg_count, line, span) in &self.schema_reference_sites {
            let Some(decl) = self.struct_schemas.get(name) else {
                return Err(ParseError {
                    span: Some(*span),
                    code: None,
                    line: *line,
                    message: format!("unknown struct schema '{name}'"),
                });
            };
            if decl.type_params.len() != *arg_count {
                return Err(ParseError {
                    span: Some(*span),
                    code: None,
                    line: *line,
                    message: format!(
                        "struct schema '{name}' expects {} type arguments, got {}",
                        decl.type_params.len(),
                        arg_count
                    ),
                });
            }
            if self.struct_schemas.contains_key(name) {
                continue;
            }
        }
        Ok(())
    }

    fn push_active_type_params(&mut self, params: &[String]) {
        self.active_type_params
            .push(params.iter().cloned().collect::<HashSet<_>>());
    }

    fn pop_active_type_params(&mut self) {
        self.active_type_params.pop();
    }

    fn is_active_type_param(&self, name: &str) -> bool {
        self.active_type_params
            .iter()
            .rev()
            .any(|params| params.contains(name))
    }

    fn parse_type_params(
        &mut self,
        owner: &str,
        owner_name: &str,
    ) -> Result<Vec<String>, ParseError> {
        if !self.check(&TokenKind::Less) {
            return Ok(Vec::new());
        }

        self.expect(&TokenKind::Less, "expected '<' before type parameters")?;
        let mut params = Vec::new();
        let mut seen = HashSet::new();
        loop {
            let param = self.expect_ident("expected type parameter name")?;
            if !seen.insert(param.clone()) {
                return Err(ParseError {
                    span: Some(self.current_span()),
                    code: None,
                    line: self.current_line(),
                    message: format!(
                        "duplicate type parameter '{param}' in {owner} '{owner_name}'"
                    ),
                });
            }
            params.push(param);
            if self.match_kind(&TokenKind::Comma) {
                continue;
            }
            break;
        }
        self.expect(&TokenKind::Greater, "expected '>' after type parameters")?;
        Ok(params)
    }

    fn parse_turbofish_type_args(&mut self) -> Result<Vec<TypeSchema>, ParseError> {
        if !self.check_path_separator() || !self.check_kind_at(self.pos + 2, &TokenKind::Less) {
            return Ok(Vec::new());
        }

        self.match_path_separator();
        self.expect(&TokenKind::Less, "expected '<' after '::' in turbofish")?;
        let mut type_args = Vec::new();
        loop {
            type_args.push(self.parse_declared_type_schema()?);
            if self.match_kind(&TokenKind::Comma) {
                continue;
            }
            break;
        }
        self.expect(&TokenKind::Greater, "expected '>' after type arguments")?;
        Ok(type_args)
    }

    fn function_param_names(params: &[FunctionParam]) -> Vec<String> {
        params.iter().map(|param| param.name.clone()).collect()
    }
}