mimispec 0.2.0

A high-density intent description language for human-AI collaboration
Documentation
use crate::ast::*;
use crate::error::ParseError;
use crate::lexer::TokenKind;
use crate::parser::Parser;

impl Parser {
    pub(super) fn parse_ui(&mut self) -> Result<UiDef, ParseError> {
        let keyword_commitment = self.expect_kw(TokenKind::Ui, "`ui`")?;
        let name = self.fuzzy_ident()?;
        let mut binds = None;
        if self.check(&TokenKind::Binds) {
            self.advance();
            binds = Some(self.fuzzy_ident()?);
        }
        self.expect(TokenKind::Colon, "`:`")?;
        if self.check(&TokenKind::Ellipsis) {
            self.advance();
            return Ok(UiDef {
                name,
                binds,
                rules: vec![],
                root: UiNode::Stack {
                    stack: StackNode {
                        description: None,
                        children: vec![],
                        keyword_commitment: Commitment::None,
                    },
                },
                keyword_commitment,
            });
        }
        let root = self.parse_ui_root()?;
        Ok(UiDef {
            name,
            binds,
            rules: vec![],
            root,
            keyword_commitment,
        })
    }

    fn parse_ui_root(&mut self) -> Result<UiNode, ParseError> {
        self.skip_newlines();
        self.expect(TokenKind::Indent, "indented block")?;
        self.enter_block()?;
        self.skip_newlines();
        let node = match self.peek_kind() {
            Some(TokenKind::Stack) => self.parse_stack_node(),
            Some(TokenKind::Parallel) => self.parse_parallel_node(),
            _ => {
                let (found, line, col) = match self.peek() {
                    Some(t) => (t.kind.to_string(), t.line, t.col),
                    None => ("EOF".into(), 0, 0),
                };
                Err(ParseError::unexpected_token(
                    found,
                    "`stack` or `parallel`".into(),
                    line,
                    col,
                ))
            }
        }?;
        if self.check(&TokenKind::Dedent) {
            self.advance();
        }
        self.leave_block();
        Ok(node)
    }

    pub(super) fn parse_ui_node(&mut self) -> Result<UiNode, ParseError> {
        self.skip_newlines();
        match self.peek_kind() {
            Some(TokenKind::Stack) => self.parse_stack_node(),
            Some(TokenKind::Parallel) => self.parse_parallel_node(),
            Some(TokenKind::String(_)) => self.parse_ui_leaf(),
            Some(TokenKind::Error) => self.parse_error_node(),
            _ => {
                let (found, line, col) = match self.peek() {
                    Some(t) => (t.kind.to_string(), t.line, t.col),
                    None => ("EOF".into(), 0, 0),
                };
                Err(ParseError::unexpected_token(
                    found,
                    "`stack`, `parallel`, `error` or string literal".into(),
                    line,
                    col,
                ))
            }
        }
    }

    fn parse_stack_node(&mut self) -> Result<UiNode, ParseError> {
        let keyword_commitment = self.expect_kw(TokenKind::Stack, "`stack`")?;
        let description = if matches!(self.peek_kind(), Some(TokenKind::String(_))) {
            Some(self.fuzzy_string()?)
        } else {
            None
        };
        self.expect(TokenKind::Colon, "`:`")?;
        let children = self.parse_block(|p| p.parse_ui_node());
        Ok(UiNode::Stack {
            stack: StackNode {
                description,
                children,
                keyword_commitment,
            },
        })
    }

    fn parse_parallel_node(&mut self) -> Result<UiNode, ParseError> {
        let keyword_commitment = self.expect_kw(TokenKind::Parallel, "`parallel`")?;
        let description = if matches!(self.peek_kind(), Some(TokenKind::String(_))) {
            Some(self.fuzzy_string()?)
        } else {
            None
        };
        self.expect(TokenKind::Colon, "`:`")?;
        let children = self.parse_block(|p| p.parse_ui_node());
        Ok(UiNode::Parallel {
            parallel: StackNode {
                description,
                children,
                keyword_commitment,
            },
        })
    }

    fn parse_error_node(&mut self) -> Result<UiNode, ParseError> {
        let keyword_commitment = self.expect_kw(TokenKind::Error, "`error`")?;
        let message = self.fuzzy_string()?;
        let desc = if self.matches(&TokenKind::Desc) {
            Some(self.parse_desc_after_keyword()?)
        } else {
            None
        };
        Ok(UiNode::Error {
            error: UiErrorNode {
                message,
                desc,
                keyword_commitment,
            },
        })
    }

    fn parse_ui_leaf(&mut self) -> Result<UiNode, ParseError> {
        let content = self.fuzzy_string()?;
        let mut desc = None;
        let mut requires = None;
        let mut requires_kw_commitment = Commitment::None;
        let mut with = Vec::new();
        let mut with_kw_commitment = Commitment::None;
        let mut on_binding = None;

        loop {
            if self.line_will_end() {
                break;
            }
            if self.matches(&TokenKind::Desc) {
                desc = Some(self.parse_desc_after_keyword()?);
            } else if self.check(&TokenKind::Requires) {
                requires_kw_commitment = self.expect_kw(TokenKind::Requires, "`requires`")?;
                requires = Some(self.parse_condition()?);
            } else if self.check(&TokenKind::With) {
                with_kw_commitment = self.expect_kw(TokenKind::With, "`with`")?;
                with = self.parse_capabilities()?;
            } else if self.check(&TokenKind::On) {
                on_binding = Some(self.parse_on_binding()?);
            } else {
                break;
            }
        }

        Ok(UiNode::Leaf {
            leaf: UiLeaf {
                content,
                desc,
                requires,
                requires_keyword_commitment: requires_kw_commitment,
                with,
                with_keyword_commitment: with_kw_commitment,
                on: on_binding,
            },
        })
    }

    fn parse_on_binding(&mut self) -> Result<OnBinding, ParseError> {
        self.expect_kw(TokenKind::On, "`on`")?;
        let event_name = if matches!(self.peek_kind(), Some(TokenKind::String(_))) {
            EventName::Natural {
                text: self.fuzzy_string()?,
            }
        } else {
            EventName::Ident {
                value: self.fuzzy_ident()?,
            }
        };
        self.expect(TokenKind::Colon, "`:`")?;
        let action = self.parse_action_expr()?;
        Ok(OnBinding { event_name, action })
    }

    fn parse_action_expr(&mut self) -> Result<ActionExpr, ParseError> {
        let mut actions = Vec::new();
        actions.push(self.parse_action()?);
        while self.matches(&TokenKind::Comma) {
            actions.push(self.parse_action()?);
        }
        Ok(ActionExpr { actions })
    }

    fn parse_action(&mut self) -> Result<Action, ParseError> {
        if self.check(&TokenKind::Arrow) {
            self.advance();
            let target = self.fuzzy_ident()?;
            return Ok(Action::Navigate { target });
        }

        if matches!(self.peek_kind(), Some(TokenKind::String(_))) {
            let text = self.fuzzy_string()?;
            return Ok(Action::Natural { text });
        }

        let expr = self.parse_expr(0)?;

        if self.matches(&TokenKind::Assign) {
            let value = self.parse_expr(0)?;
            return Ok(Action::Assign {
                target: expr,
                value,
            });
        }

        Ok(Action::Call { expr })
    }
}