Factor away the Parser struct (#4520)
Signed-off-by: Nick Cameron <nrc@ncameron.org>
This commit is contained in:
		@ -185,7 +185,7 @@ pub async fn modify_ast_for_sketch(
 | 
			
		||||
    let recasted = program.ast.recast(&FormatOptions::default(), 0);
 | 
			
		||||
 | 
			
		||||
    // Re-parse the ast so we get the correct source ranges.
 | 
			
		||||
    *program = crate::parser::parse(&recasted, module_id)?.into();
 | 
			
		||||
    *program = crate::parser::parse_str(&recasted, module_id)?.into();
 | 
			
		||||
 | 
			
		||||
    Ok(recasted)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -3194,7 +3194,7 @@ const cylinder = startSketchOn('-XZ')
 | 
			
		||||
    return arg0
 | 
			
		||||
}"#;
 | 
			
		||||
        let module_id = ModuleId::default();
 | 
			
		||||
        let program = crate::parser::parse(some_program_string, module_id).unwrap();
 | 
			
		||||
        let program = crate::parser::parse_str(some_program_string, module_id).unwrap();
 | 
			
		||||
 | 
			
		||||
        // Check the program output for the types of the parameters.
 | 
			
		||||
        let function = program.body.first().unwrap();
 | 
			
		||||
@ -3265,7 +3265,7 @@ const cylinder = startSketchOn('-XZ')
 | 
			
		||||
    return 1
 | 
			
		||||
}"#;
 | 
			
		||||
        let module_id = ModuleId::default();
 | 
			
		||||
        let program = crate::parser::parse(some_program_string, module_id).unwrap();
 | 
			
		||||
        let program = crate::parser::parse_str(some_program_string, module_id).unwrap();
 | 
			
		||||
 | 
			
		||||
        // Check the program output for the types of the parameters.
 | 
			
		||||
        let function = program.body.first().unwrap();
 | 
			
		||||
 | 
			
		||||
@ -2413,7 +2413,7 @@ impl ExecutorContext {
 | 
			
		||||
                    }
 | 
			
		||||
                    let module_id = exec_state.add_module(resolved_path.clone());
 | 
			
		||||
                    let source = self.fs.read_to_string(&resolved_path, source_range).await?;
 | 
			
		||||
                    let program = crate::parser::parse(&source, module_id)?;
 | 
			
		||||
                    let program = crate::parser::parse_str(&source, module_id)?;
 | 
			
		||||
                    let (module_memory, module_exports) = {
 | 
			
		||||
                        exec_state.import_stack.push(resolved_path.clone());
 | 
			
		||||
                        let original_execution = self.engine.replace_execution_kind(ExecutionKind::Isolated);
 | 
			
		||||
 | 
			
		||||
@ -88,13 +88,11 @@ impl Program {
 | 
			
		||||
    pub fn parse(input: &str) -> Result<Program, KclError> {
 | 
			
		||||
        let module_id = ModuleId::default();
 | 
			
		||||
        let tokens = token::lexer(input, module_id)?;
 | 
			
		||||
        let parser = parser::Parser::new(tokens);
 | 
			
		||||
        let ast = parser.ast()?;
 | 
			
		||||
        let ast = parser::parse_tokens(tokens)?;
 | 
			
		||||
 | 
			
		||||
        Ok(Program { ast })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Deserialize the ast from a stringified json
 | 
			
		||||
    pub fn compute_digest(&mut self) -> ast::types::digest::Digest {
 | 
			
		||||
        self.ast.compute_digest()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -298,8 +298,7 @@ impl crate::lsp::backend::Backend for Backend {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Lets update the ast.
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens.clone());
 | 
			
		||||
        let result = parser.ast();
 | 
			
		||||
        let result = crate::parser::parse_tokens(tokens.clone());
 | 
			
		||||
        let mut ast = match result {
 | 
			
		||||
            Ok(ast) => ast,
 | 
			
		||||
            Err(err) => {
 | 
			
		||||
@ -1302,11 +1301,7 @@ impl LanguageServer for Backend {
 | 
			
		||||
        // I don't know if we need to do this again since it should be updated in the context.
 | 
			
		||||
        // But I figure better safe than sorry since this will write back out to the file.
 | 
			
		||||
        let module_id = ModuleId::default();
 | 
			
		||||
        let Ok(tokens) = crate::token::lexer(current_code, module_id) else {
 | 
			
		||||
            return Ok(None);
 | 
			
		||||
        };
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let Ok(ast) = parser.ast() else {
 | 
			
		||||
        let Ok(ast) = crate::parser::parse_str(current_code, module_id) else {
 | 
			
		||||
            return Ok(None);
 | 
			
		||||
        };
 | 
			
		||||
        // Now recast it.
 | 
			
		||||
@ -1340,11 +1335,7 @@ impl LanguageServer for Backend {
 | 
			
		||||
        // I don't know if we need to do this again since it should be updated in the context.
 | 
			
		||||
        // But I figure better safe than sorry since this will write back out to the file.
 | 
			
		||||
        let module_id = ModuleId::default();
 | 
			
		||||
        let Ok(tokens) = crate::token::lexer(current_code, module_id) else {
 | 
			
		||||
            return Ok(None);
 | 
			
		||||
        };
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let Ok(mut ast) = parser.ast() else {
 | 
			
		||||
        let Ok(mut ast) = crate::parser::parse_str(current_code, module_id) else {
 | 
			
		||||
            return Ok(None);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -16,57 +16,44 @@ pub const PIPE_OPERATOR: &str = "|>";
 | 
			
		||||
/// Parse the given KCL code into an AST.  This is the top-level.
 | 
			
		||||
pub fn top_level_parse(code: &str) -> Result<Node<Program>, KclError> {
 | 
			
		||||
    let module_id = ModuleId::default();
 | 
			
		||||
    parse(code, module_id)
 | 
			
		||||
    parse_str(code, module_id)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Parse the given KCL code into an AST.
 | 
			
		||||
pub fn parse(code: &str, module_id: ModuleId) -> Result<Node<Program>, KclError> {
 | 
			
		||||
pub fn parse_str(code: &str, module_id: ModuleId) -> Result<Node<Program>, KclError> {
 | 
			
		||||
    let tokens = crate::token::lexer(code, module_id)?;
 | 
			
		||||
    let parser = Parser::new(tokens);
 | 
			
		||||
    parser.ast()
 | 
			
		||||
    parse_tokens(tokens)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct Parser {
 | 
			
		||||
    pub tokens: Vec<Token>,
 | 
			
		||||
    pub unknown_tokens: Vec<Token>,
 | 
			
		||||
}
 | 
			
		||||
pub fn parse_tokens(tokens: Vec<Token>) -> Result<Node<Program>, KclError> {
 | 
			
		||||
    let (tokens, unknown_tokens): (Vec<Token>, Vec<Token>) = tokens
 | 
			
		||||
        .into_iter()
 | 
			
		||||
        .partition(|token| token.token_type != TokenType::Unknown);
 | 
			
		||||
 | 
			
		||||
impl Parser {
 | 
			
		||||
    pub fn new(tokens: Vec<Token>) -> Self {
 | 
			
		||||
        let (tokens, unknown_tokens): (Vec<Token>, Vec<Token>) = tokens
 | 
			
		||||
            .into_iter()
 | 
			
		||||
            .partition(|token| token.token_type != TokenType::Unknown);
 | 
			
		||||
        Self { tokens, unknown_tokens }
 | 
			
		||||
    if !unknown_tokens.is_empty() {
 | 
			
		||||
        let source_ranges = unknown_tokens.iter().map(SourceRange::from).collect();
 | 
			
		||||
        let token_list = unknown_tokens.iter().map(|t| t.value.as_str()).collect::<Vec<_>>();
 | 
			
		||||
        let message = if token_list.len() == 1 {
 | 
			
		||||
            format!("found unknown token '{}'", token_list[0])
 | 
			
		||||
        } else {
 | 
			
		||||
            format!("found unknown tokens [{}]", token_list.join(", "))
 | 
			
		||||
        };
 | 
			
		||||
        return Err(KclError::Lexical(KclErrorDetails { source_ranges, message }));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Run the parser
 | 
			
		||||
    pub fn ast(&self) -> Result<Node<Program>, KclError> {
 | 
			
		||||
        if !self.unknown_tokens.is_empty() {
 | 
			
		||||
            let source_ranges = self.unknown_tokens.iter().map(SourceRange::from).collect();
 | 
			
		||||
            let token_list = self.unknown_tokens.iter().map(|t| t.value.as_str()).collect::<Vec<_>>();
 | 
			
		||||
            let message = if token_list.len() == 1 {
 | 
			
		||||
                format!("found unknown token '{}'", token_list[0])
 | 
			
		||||
            } else {
 | 
			
		||||
                format!("found unknown tokens [{}]", token_list.join(", "))
 | 
			
		||||
            };
 | 
			
		||||
            return Err(KclError::Lexical(KclErrorDetails { source_ranges, message }));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Important, to not call this before the unknown tokens check.
 | 
			
		||||
        if self.tokens.is_empty() {
 | 
			
		||||
            // Empty file should just do nothing.
 | 
			
		||||
            return Ok(Node::<Program>::default());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Check all the tokens are whitespace or comments.
 | 
			
		||||
        if self
 | 
			
		||||
            .tokens
 | 
			
		||||
            .iter()
 | 
			
		||||
            .all(|t| t.token_type.is_whitespace() || t.token_type.is_comment())
 | 
			
		||||
        {
 | 
			
		||||
            return Ok(Node::<Program>::default());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        parser_impl::run_parser(&mut self.tokens.as_slice())
 | 
			
		||||
    // Important, to not call this before the unknown tokens check.
 | 
			
		||||
    if tokens.is_empty() {
 | 
			
		||||
        // Empty file should just do nothing.
 | 
			
		||||
        return Ok(Node::<Program>::default());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Check all the tokens are whitespace or comments.
 | 
			
		||||
    if tokens
 | 
			
		||||
        .iter()
 | 
			
		||||
        .all(|t| t.token_type.is_whitespace() || t.token_type.is_comment())
 | 
			
		||||
    {
 | 
			
		||||
        return Ok(Node::<Program>::default());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    parser_impl::run_parser(&mut tokens.as_slice())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,14 +1,10 @@
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
 | 
			
		||||
    macro_rules! parse_and_lex {
 | 
			
		||||
        ($func_name:ident, $test_kcl_program:expr) => {
 | 
			
		||||
            #[test]
 | 
			
		||||
            fn $func_name() {
 | 
			
		||||
                let module_id = $crate::parser::ModuleId::default();
 | 
			
		||||
                if let Ok(v) = $crate::token::lexer($test_kcl_program, module_id) {
 | 
			
		||||
                    let _ = $crate::parser::Parser::new(v).ast();
 | 
			
		||||
                }
 | 
			
		||||
                let _ = crate::parser::top_level_parse($test_kcl_program);
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -2827,7 +2827,7 @@ const mySk1 = startSketchAt([0, 0])"#;
 | 
			
		||||
        for test in tests {
 | 
			
		||||
            // Run the original parser
 | 
			
		||||
            let tokens = crate::token::lexer(test, ModuleId::default()).unwrap();
 | 
			
		||||
            let mut expected_body = crate::parser::Parser::new(tokens.clone()).ast().unwrap().inner.body;
 | 
			
		||||
            let mut expected_body = crate::parser::parse_tokens(tokens.clone()).unwrap().inner.body;
 | 
			
		||||
            assert_eq!(expected_body.len(), 1);
 | 
			
		||||
            let BodyItem::VariableDeclaration(expected) = expected_body.pop().unwrap() else {
 | 
			
		||||
                panic!("Expected variable declaration");
 | 
			
		||||
@ -2854,8 +2854,7 @@ const mySk1 = startSketchAt([0, 0])"#;
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_math_parse() {
 | 
			
		||||
        let module_id = ModuleId::default();
 | 
			
		||||
        let tokens = crate::token::lexer(r#"5 + "a""#, module_id).unwrap();
 | 
			
		||||
        let actual = crate::parser::Parser::new(tokens).ast().unwrap().inner.body;
 | 
			
		||||
        let actual = crate::parser::parse_str(r#"5 + "a""#, module_id).unwrap().inner.body;
 | 
			
		||||
        let expr = Node::boxed(
 | 
			
		||||
            BinaryExpression {
 | 
			
		||||
                operator: BinaryOperator::Add,
 | 
			
		||||
@ -2991,8 +2990,7 @@ const mySk1 = startSketchAt([0, 0])"#;
 | 
			
		||||
    fn test_abstract_syntax_tree() {
 | 
			
		||||
        let code = "5 +6";
 | 
			
		||||
        let module_id = ModuleId::default();
 | 
			
		||||
        let parser = crate::parser::Parser::new(crate::token::lexer(code, module_id).unwrap());
 | 
			
		||||
        let result = parser.ast().unwrap();
 | 
			
		||||
        let result = crate::parser::parse_str(code, module_id).unwrap();
 | 
			
		||||
        let expected_result = Node::new(
 | 
			
		||||
            Program {
 | 
			
		||||
                body: vec![BodyItem::ExpressionStatement(Node::new(
 | 
			
		||||
@ -3140,9 +3138,7 @@ const secondExtrude = startSketchOn('XY')
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_parse_greater_bang() {
 | 
			
		||||
        let module_id = ModuleId::default();
 | 
			
		||||
        let tokens = crate::token::lexer(">!", module_id).unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let err = parser.ast().unwrap_err();
 | 
			
		||||
        let err = crate::parser::parse_str(">!", module_id).unwrap_err();
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            err.to_string(),
 | 
			
		||||
            r#"syntax: KclErrorDetails { source_ranges: [SourceRange([0, 1, 0])], message: "Unexpected token: >" }"#
 | 
			
		||||
@ -3152,12 +3148,9 @@ const secondExtrude = startSketchOn('XY')
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_parse_z_percent_parens() {
 | 
			
		||||
        let module_id = ModuleId::default();
 | 
			
		||||
        let tokens = crate::token::lexer("z%)", module_id).unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let result = parser.ast();
 | 
			
		||||
        assert!(result.is_err());
 | 
			
		||||
        let err = crate::parser::parse_str("z%)", module_id).unwrap_err();
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            result.err().unwrap().to_string(),
 | 
			
		||||
            err.to_string(),
 | 
			
		||||
            r#"syntax: KclErrorDetails { source_ranges: [SourceRange([1, 2, 0])], message: "Unexpected token: %" }"#
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
@ -3165,12 +3158,11 @@ const secondExtrude = startSketchOn('XY')
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_parse_parens_unicode() {
 | 
			
		||||
        let module_id = ModuleId::default();
 | 
			
		||||
        let result = crate::token::lexer("(ޜ", module_id);
 | 
			
		||||
        let err = crate::parser::parse_str("(ޜ", module_id).unwrap_err();
 | 
			
		||||
        // TODO: Better errors when program cannot tokenize.
 | 
			
		||||
        // https://github.com/KittyCAD/modeling-app/issues/696
 | 
			
		||||
        assert!(result.is_err());
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            result.err().unwrap().to_string(),
 | 
			
		||||
            err.to_string(),
 | 
			
		||||
            r#"lexical: KclErrorDetails { source_ranges: [SourceRange([1, 2, 0])], message: "found unknown token 'ޜ'" }"#
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
@ -3187,96 +3179,64 @@ const bracket = [-leg2 + thickness, 0]
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_parse_nested_open_brackets() {
 | 
			
		||||
        let module_id = ModuleId::default();
 | 
			
		||||
        let tokens = crate::token::lexer(
 | 
			
		||||
        crate::parser::top_level_parse(
 | 
			
		||||
            r#"
 | 
			
		||||
z(-[["#,
 | 
			
		||||
            module_id,
 | 
			
		||||
        )
 | 
			
		||||
        .unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let result = parser.ast();
 | 
			
		||||
        assert!(result.is_err());
 | 
			
		||||
        .unwrap_err();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_parse_weird_new_line_function() {
 | 
			
		||||
        let module_id = ModuleId::default();
 | 
			
		||||
        let tokens = crate::token::lexer(
 | 
			
		||||
        let err = crate::parser::top_level_parse(
 | 
			
		||||
            r#"z
 | 
			
		||||
 (--#"#,
 | 
			
		||||
            module_id,
 | 
			
		||||
        )
 | 
			
		||||
        .unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let result = parser.ast();
 | 
			
		||||
        assert!(result.is_err());
 | 
			
		||||
        .unwrap_err();
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            result.err().unwrap().to_string(),
 | 
			
		||||
            err.to_string(),
 | 
			
		||||
            r#"syntax: KclErrorDetails { source_ranges: [SourceRange([3, 4, 0])], message: "Unexpected token: (" }"#
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_parse_weird_lots_of_fancy_brackets() {
 | 
			
		||||
        let module_id = ModuleId::default();
 | 
			
		||||
        let tokens = crate::token::lexer(r#"zz({{{{{{{{)iegAng{{{{{{{##"#, module_id).unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let result = parser.ast();
 | 
			
		||||
        assert!(result.is_err());
 | 
			
		||||
        let err = crate::parser::top_level_parse(r#"zz({{{{{{{{)iegAng{{{{{{{##"#).unwrap_err();
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            result.err().unwrap().to_string(),
 | 
			
		||||
            err.to_string(),
 | 
			
		||||
            r#"syntax: KclErrorDetails { source_ranges: [SourceRange([2, 3, 0])], message: "Unexpected token: (" }"#
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_parse_weird_close_before_open() {
 | 
			
		||||
        let module_id = ModuleId::default();
 | 
			
		||||
        let tokens = crate::token::lexer(
 | 
			
		||||
        let err = crate::parser::top_level_parse(
 | 
			
		||||
            r#"fn)n
 | 
			
		||||
e
 | 
			
		||||
["#,
 | 
			
		||||
            module_id,
 | 
			
		||||
        )
 | 
			
		||||
        .unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let result = parser.ast();
 | 
			
		||||
        assert!(result.is_err());
 | 
			
		||||
        assert!(result
 | 
			
		||||
            .err()
 | 
			
		||||
            .unwrap()
 | 
			
		||||
        .unwrap_err();
 | 
			
		||||
        assert!(err
 | 
			
		||||
            .to_string()
 | 
			
		||||
            .contains("expected whitespace, found ')' which is brace"));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_parse_weird_close_before_nada() {
 | 
			
		||||
        let module_id = ModuleId::default();
 | 
			
		||||
        let tokens = crate::token::lexer(r#"fn)n-"#, module_id).unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let result = parser.ast();
 | 
			
		||||
        assert!(result.is_err());
 | 
			
		||||
        assert!(result
 | 
			
		||||
            .err()
 | 
			
		||||
            .unwrap()
 | 
			
		||||
        let err = crate::parser::top_level_parse(r#"fn)n-"#).unwrap_err();
 | 
			
		||||
        assert!(err
 | 
			
		||||
            .to_string()
 | 
			
		||||
            .contains("expected whitespace, found ')' which is brace"));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_parse_weird_lots_of_slashes() {
 | 
			
		||||
        let module_id = ModuleId::default();
 | 
			
		||||
        let tokens = crate::token::lexer(
 | 
			
		||||
        let err = crate::parser::top_level_parse(
 | 
			
		||||
            r#"J///////////o//+///////////P++++*++++++P///////˟
 | 
			
		||||
++4"#,
 | 
			
		||||
            module_id,
 | 
			
		||||
        )
 | 
			
		||||
        .unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let result = parser.ast();
 | 
			
		||||
        assert!(result.is_err());
 | 
			
		||||
        let actual = result.err().unwrap().to_string();
 | 
			
		||||
        .unwrap_err();
 | 
			
		||||
        let actual = err.to_string();
 | 
			
		||||
        assert!(actual.contains("Unexpected token: +"), "actual={actual:?}");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -3364,76 +3324,60 @@ e
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_error_keyword_in_variable() {
 | 
			
		||||
        let some_program_string = r#"const let = "thing""#;
 | 
			
		||||
        let module_id = ModuleId::default();
 | 
			
		||||
        let tokens = crate::token::lexer(some_program_string, module_id).unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let result = parser.ast();
 | 
			
		||||
        assert!(result.is_err());
 | 
			
		||||
        let err = crate::parser::top_level_parse(r#"const let = "thing""#).unwrap_err();
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            result.err().unwrap().to_string(),
 | 
			
		||||
            err.to_string(),
 | 
			
		||||
            r#"syntax: KclErrorDetails { source_ranges: [SourceRange([6, 9, 0])], message: "Cannot assign a variable to a reserved keyword: let" }"#
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_error_keyword_in_fn_name() {
 | 
			
		||||
        let some_program_string = r#"fn let = () {}"#;
 | 
			
		||||
        let module_id = ModuleId::default();
 | 
			
		||||
        let tokens = crate::token::lexer(some_program_string, module_id).unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let result = parser.ast();
 | 
			
		||||
        assert!(result.is_err());
 | 
			
		||||
        let err = crate::parser::top_level_parse(r#"fn let = () {}"#).unwrap_err();
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            result.err().unwrap().to_string(),
 | 
			
		||||
            err.to_string(),
 | 
			
		||||
            r#"syntax: KclErrorDetails { source_ranges: [SourceRange([3, 6, 0])], message: "Cannot assign a variable to a reserved keyword: let" }"#
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_error_stdlib_in_fn_name() {
 | 
			
		||||
        let some_program_string = r#"fn cos = () => {
 | 
			
		||||
        let err = crate::parser::top_level_parse(
 | 
			
		||||
            r#"fn cos = () => {
 | 
			
		||||
            return 1
 | 
			
		||||
        }"#;
 | 
			
		||||
        let module_id = ModuleId::default();
 | 
			
		||||
        let tokens = crate::token::lexer(some_program_string, module_id).unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let result = parser.ast();
 | 
			
		||||
        assert!(result.is_err());
 | 
			
		||||
        }"#,
 | 
			
		||||
        )
 | 
			
		||||
        .unwrap_err();
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            result.err().unwrap().to_string(),
 | 
			
		||||
            err.to_string(),
 | 
			
		||||
            r#"syntax: KclErrorDetails { source_ranges: [SourceRange([3, 6, 0])], message: "Cannot assign a variable to a reserved keyword: cos" }"#
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_error_keyword_in_fn_args() {
 | 
			
		||||
        let some_program_string = r#"fn thing = (let) => {
 | 
			
		||||
        let err = crate::parser::top_level_parse(
 | 
			
		||||
            r#"fn thing = (let) => {
 | 
			
		||||
    return 1
 | 
			
		||||
}"#;
 | 
			
		||||
        let module_id = ModuleId::default();
 | 
			
		||||
        let tokens = crate::token::lexer(some_program_string, module_id).unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let result = parser.ast();
 | 
			
		||||
        assert!(result.is_err());
 | 
			
		||||
}"#,
 | 
			
		||||
        )
 | 
			
		||||
        .unwrap_err();
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            result.err().unwrap().to_string(),
 | 
			
		||||
            err.to_string(),
 | 
			
		||||
            r#"syntax: KclErrorDetails { source_ranges: [SourceRange([12, 15, 0])], message: "Cannot assign a variable to a reserved keyword: let" }"#
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_error_stdlib_in_fn_args() {
 | 
			
		||||
        let some_program_string = r#"fn thing = (cos) => {
 | 
			
		||||
        let err = crate::parser::top_level_parse(
 | 
			
		||||
            r#"fn thing = (cos) => {
 | 
			
		||||
    return 1
 | 
			
		||||
}"#;
 | 
			
		||||
        let module_id = ModuleId::default();
 | 
			
		||||
        let tokens = crate::token::lexer(some_program_string, module_id).unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let result = parser.ast();
 | 
			
		||||
        assert!(result.is_err());
 | 
			
		||||
}"#,
 | 
			
		||||
        )
 | 
			
		||||
        .unwrap_err();
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            result.err().unwrap().to_string(),
 | 
			
		||||
            err.to_string(),
 | 
			
		||||
            r#"syntax: KclErrorDetails { source_ranges: [SourceRange([12, 15, 0])], message: "Cannot assign a variable to a reserved keyword: cos" }"#
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
@ -3547,13 +3491,9 @@ thing(false)
 | 
			
		||||
"#,
 | 
			
		||||
                name
 | 
			
		||||
            );
 | 
			
		||||
            let module_id = ModuleId::default();
 | 
			
		||||
            let tokens = crate::token::lexer(&some_program_string, module_id).unwrap();
 | 
			
		||||
            let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
            let result = parser.ast();
 | 
			
		||||
            assert!(result.is_err());
 | 
			
		||||
            let err = crate::parser::top_level_parse(&some_program_string).unwrap_err();
 | 
			
		||||
            assert_eq!(
 | 
			
		||||
                result.err().unwrap().to_string(),
 | 
			
		||||
                err.to_string(),
 | 
			
		||||
                format!(
 | 
			
		||||
                    r#"syntax: KclErrorDetails {{ source_ranges: [SourceRange([0, {}, 0])], message: "Expected a `fn` variable kind, found: `const`" }}"#,
 | 
			
		||||
                    name.len(),
 | 
			
		||||
@ -3565,16 +3505,12 @@ thing(false)
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_error_define_var_as_function() {
 | 
			
		||||
        let some_program_string = r#"fn thing = "thing""#;
 | 
			
		||||
        let module_id = ModuleId::default();
 | 
			
		||||
        let tokens = crate::token::lexer(some_program_string, module_id).unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let result = parser.ast();
 | 
			
		||||
        assert!(result.is_err());
 | 
			
		||||
        let err = crate::parser::top_level_parse(some_program_string).unwrap_err();
 | 
			
		||||
        // TODO: https://github.com/KittyCAD/modeling-app/issues/784
 | 
			
		||||
        // Improve this error message.
 | 
			
		||||
        // It should say that the compiler is expecting a function expression on the RHS.
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            result.err().unwrap().to_string(),
 | 
			
		||||
            err.to_string(),
 | 
			
		||||
            r#"syntax: KclErrorDetails { source_ranges: [SourceRange([11, 18, 0])], message: "Unexpected token: \"thing\"" }"#
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
@ -3589,11 +3525,7 @@ thing(false)
 | 
			
		||||
    |> line([-5.09, 12.33], %)
 | 
			
		||||
    asdasd
 | 
			
		||||
"#;
 | 
			
		||||
        let module_id = ModuleId::default();
 | 
			
		||||
        let tokens = crate::token::lexer(test_program, module_id).unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let result = parser.ast();
 | 
			
		||||
        let _e = result.unwrap_err();
 | 
			
		||||
        crate::parser::top_level_parse(test_program).unwrap_err();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
@ -3647,10 +3579,7 @@ let myBox = box([0,0], -3, -16, -10)
 | 
			
		||||
        foo()
 | 
			
		||||
            |> bar(2)
 | 
			
		||||
        "#;
 | 
			
		||||
        let module_id = ModuleId::default();
 | 
			
		||||
        let tokens = crate::token::lexer(some_program_string, module_id).unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let err = parser.ast().unwrap_err();
 | 
			
		||||
        let err = crate::parser::top_level_parse(some_program_string).unwrap_err();
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            err.to_string(),
 | 
			
		||||
            r#"syntax: KclErrorDetails { source_ranges: [SourceRange([30, 36, 0])], message: "All expressions in a pipeline must use the % (substitution operator)" }"#
 | 
			
		||||
 | 
			
		||||
@ -3,7 +3,6 @@ use insta::rounded_redaction;
 | 
			
		||||
use crate::{
 | 
			
		||||
    ast::types::{ModuleId, Node, Program},
 | 
			
		||||
    errors::KclError,
 | 
			
		||||
    parser::Parser,
 | 
			
		||||
    token::Token,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -61,7 +60,7 @@ fn parse(test_name: &str) {
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // Parse the tokens into an AST.
 | 
			
		||||
    let parse_res = Parser::new(tokens).ast();
 | 
			
		||||
    let parse_res = crate::parser::parse_tokens(tokens);
 | 
			
		||||
    assert_snapshot(test_name, "Result of parsing", || {
 | 
			
		||||
        insta::assert_json_snapshot!("ast", parse_res);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user