From 6f2cec0a6ac72777893120a2ce02ffd9ff4f76b8 Mon Sep 17 00:00:00 2001 From: Mingun Date: Mon, 22 Nov 2021 21:30:26 +0500 Subject: [PATCH] More strict function parsing Currently original KSC can parse more wider range of call expressions then it can handle, actually it can handle only `[expr.]$name($args)` construction, but it can parse, for example: - ()(args) - [](args) - 42(args) - ""(args) - true(args) This is fixed in the original compiler in the https://github.com/kaitai-io/kaitai_struct_compiler/pull/227 but it still not merged --- src/model/expressions.rs | 7 +- src/parser/expressions.rs | 175 +++++++++++++++++++++++++++++++++++--- 2 files changed, 170 insertions(+), 12 deletions(-) diff --git a/src/model/expressions.rs b/src/model/expressions.rs index 5c794ba..3a05584 100644 --- a/src/model/expressions.rs +++ b/src/model/expressions.rs @@ -56,10 +56,12 @@ pub enum OwningNode { bit: bool, }, - /// Calling function or method: `${expr}(${args})`. + /// Calling function or method: `[${callee}.]${method}(${args})`. Call { /// Expression which is called callee: Box, + /// Name of method to call + method: FieldName, /// Arguments of method call args: Vec, }, @@ -146,8 +148,9 @@ impl OwningNode { Node::SizeOf { type_, bit } => SizeOf { type_: type_.into(), bit }, - Node::Call { callee, args } => Call { + Node::Call { callee, method, args } => Call { callee: Box::new(Self::validate(*callee)?), + method: FieldName::valid(method), args: Self::validate_all(args)?, }, Node::Cast { expr, to_type } => Cast { diff --git a/src/parser/expressions.rs b/src/parser/expressions.rs index decb7c8..a895b9f 100644 --- a/src/parser/expressions.rs +++ b/src/parser/expressions.rs @@ -52,10 +52,12 @@ pub enum Node<'input> { bit: bool, }, - /// Calling function or method: `${expr}(${args})`. + /// Calling function or method: `[${callee}.]${method}(${args})`. Call { /// Expression which is called callee: Box>, + /// Name of method to call + method: &'input str, /// Arguments of method call args: Vec>, }, @@ -392,7 +394,7 @@ pub struct ProcessAlgo<'input> { /// then converted to `Node` with `make_chain` call. enum Postfix<'input> { /// Calling function or method with specified arguments. - Args(Vec>), + Call(&'input str, Vec>), /// Conversion to specified type. CastTo(TypeRef<'input>), /// Index expression @@ -440,10 +442,10 @@ fn to_escaped(ch: &str) -> char { fn make_chain<'i>(mut expr: Node<'i>, tail: Vec>) -> Node<'i> { for p in tail { expr = match p { - Postfix::Args(args) => Node::Call { callee: Box::new(expr), args }, - Postfix::CastTo(to_type) => Node::Cast { expr: Box::new(expr), to_type }, - Postfix::Index(index) => Node::Index { expr: Box::new(expr), index: Box::new(index) }, - Postfix::Field(attr) => Node::Access { expr: Box::new(expr), attr }, + Postfix::Call(method, args) => Node::Call { callee: Box::new(expr), method, args }, + Postfix::CastTo(to_type) => Node::Cast { expr: Box::new(expr), to_type }, + Postfix::Index(index) => Node::Index { expr: Box::new(expr), index: Box::new(index) }, + Postfix::Field(attr) => Node::Access { expr: Box::new(expr), attr }, } } expr @@ -654,10 +656,10 @@ peg::parser! { / "_" { Node::SpecialName(SpecialName::Value) } ; rule postfix() -> Postfix<'input> - = "(" _ a:args() _ ")" { Postfix::Args(a) }// call - / "[" _ e:expr() _ "]" { Postfix::Index(e) }// indexing - / "." _ "as" _ "<" _ t:type_ref() _ ">" { Postfix::CastTo(t) }// type cast - / "." _ n:name() { Postfix::Field(n) }// attribute access + = "[" _ e:expr() _ "]" { Postfix::Index(e) }// indexing + / "." _ "as" _ "<" _ t:type_ref() _ ">" { Postfix::CastTo(t) }// type cast + / "." _ n:name() _ "(" _ a:args() _ ")" { Postfix::Call(n, a) }// call + / "." _ n:name() { Postfix::Field(n) }// attribute access ; /// List of names, delimited by `::`, with an optional leading `::`. @@ -1587,6 +1589,159 @@ mod parse { } } + mod call { + // Colorful diffs in assertions - resolve ambiguous + use pretty_assertions::assert_eq; + use super::*; + + #[test] + fn without_params() { + assert_eq!(parse_single("123.bar()"), Ok(Call { + callee: Box::new(Int(123.into())), + method: "bar", + args: vec![], + })); + assert_eq!(parse_single("123. bar()"), Ok(Call { + callee: Box::new(Int(123.into())), + method: "bar", + args: vec![], + })); + assert_eq!(parse_single("123.\nbar()"), Ok(Call { + callee: Box::new(Int(123.into())), + method: "bar", + args: vec![], + })); + assert_eq!(parse_single("foo.bar()"), Ok(Call { + callee: Box::new(Attr("foo")), + method: "bar", + args: vec![], + })); + } + + #[test] + fn with_params() { + assert_eq!(parse_single("123.bar(42)"), Ok(Call { + callee: Box::new(Int(123.into())), + method: "bar", + args: vec![Int(42.into())], + })); + assert_eq!(parse_single("123. bar(42)"), Ok(Call { + callee: Box::new(Int(123.into())), + method: "bar", + args: vec![Int(42.into())], + })); + assert_eq!(parse_single("123.\nbar(42)"), Ok(Call { + callee: Box::new(Int(123.into())), + method: "bar", + args: vec![Int(42.into())], + })); + assert_eq!(parse_single("foo.bar(42)"), Ok(Call { + callee: Box::new(Attr("foo")), + method: "bar", + args: vec![Int(42.into())], + })); + } + + #[test] + fn on_strings() { + assert_eq!(parse_single(r#""foo".bar(42)"#), Ok(Call { + callee: Box::new(Str("foo".into())), + method: "bar", + args: vec![Int(42.into())], + })); + assert_eq!(parse_single(r#"'foo'.bar(42)"#), Ok(Call { + callee: Box::new(Str("foo".into())), + method: "bar", + args: vec![Int(42.into())], + })); + } + + #[test] + fn on_booleans() { + assert_eq!(parse_single("true.bar(42)"), Ok(Call { + callee: Box::new(Bool(true)), + method: "bar", + args: vec![Int(42.into())], + })); + assert_eq!(parse_single("false.bar(42)"), Ok(Call { + callee: Box::new(Bool(false)), + method: "bar", + args: vec![Int(42.into())], + })); + } + + #[test] + fn on_integer() { + assert_eq!(parse_single("42.bar(42)"), Ok(Call { + callee: Box::new(Int(42.into())), + method: "bar", + args: vec![Int(42.into())], + })); + } + + #[test] + fn on_float() { + assert_eq!(parse_single("42.0.bar(42)"), Ok(Call { + callee: Box::new(Float(42.into())), + method: "bar", + args: vec![Int(42.into())], + })); + } + + #[test] + fn on_array() { + assert_eq!(parse_single("[].bar(42)"), Ok(Call { + callee: Box::new(List(vec![])), + method: "bar", + args: vec![Int(42.into())], + })); + } + + #[test] + fn on_slice() { + assert_eq!(parse_single("foo[1].bar(42)"), Ok(Call { + callee: Box::new(Index { + expr: Box::new(Attr("foo")), + index: Box::new(Int(1.into())), + }), + method: "bar", + args: vec![Int(42.into())], + })); + } + + #[test] + fn on_group() { + assert_eq!(parse_single("(42).bar(42)"), Ok(Call { + callee: Box::new(Int(42.into())), + method: "bar", + args: vec![Int(42.into())], + })); + } + + #[test] + fn on_expression() { + assert_eq!(parse_single("(1+2).bar(42)"), Ok(Call { + callee: Box::new(Binary { + op: Add, + left: Box::new(Int(1.into())), + right: Box::new(Int(2.into())), + }), + method: "bar", + args: vec![Int(42.into())], + })); + } + + #[test] + fn should_fail() { + assert!(parse_single("''(42)").is_err()); + assert!(parse_single("()(42)").is_err()); + assert!(parse_single("[](42)").is_err()); + assert!(parse_single("12(42)").is_err()); + assert!(parse_single("12.3(42)").is_err()); + assert!(parse_single(r#"""(42)"#).is_err()); + } + } + /// Tests for parsing of attributes' type reference definitions mod type_ref { use super::*;