diff --git a/rust/feed/src/oid/mod.rs b/rust/feed/src/oid/mod.rs index 1231598ad4..fbeb480542 100644 --- a/rust/feed/src/oid/mod.rs +++ b/rust/feed/src/oid/mod.rs @@ -7,7 +7,7 @@ use std::{fs::File, io::Read}; use nasl_interpreter::{AsBufReader, Loader}; -use nasl_syntax::{IdentifierType, Statement, TokenCategory}; +use nasl_syntax::{IdentifierType, Statement, StatementKind, TokenCategory}; use crate::{ update, @@ -40,10 +40,11 @@ where } fn script_oid(stmt: &Statement) -> Option { - match stmt { - Statement::Call(t, stms, _) => match t.category() { + match stmt.kind() { + StatementKind::Call(param) => match stmt.start().category() { TokenCategory::Identifier(IdentifierType::Undefined(s)) => match s as &str { - "script_oid" => stms.first().map(|x| x.to_string()), + // maybe switch from children to patternmatching? + "script_oid" => param.children().first().map(|x| x.to_string()), _ => None, }, _ => None, @@ -56,8 +57,8 @@ where fn single(&self, key: String) -> Result { let code = self.loader.load(key.as_ref())?; for stmt in nasl_syntax::parse(&code) { - if let Statement::If(_, _, stmts, _, _) = stmt? { - if let Statement::Block(_, x, _) = &*stmts { + if let StatementKind::If(_, stmts, _, _) = stmt?.kind() { + if let StatementKind::Block(x) = stmts.kind() { for stmt in x { if let Some(oid) = Self::script_oid(stmt) { return Ok(oid); diff --git a/rust/feed/src/transpile/mod.rs b/rust/feed/src/transpile/mod.rs index a4eedc6a87..801b485e41 100644 --- a/rust/feed/src/transpile/mod.rs +++ b/rust/feed/src/transpile/mod.rs @@ -2,7 +2,7 @@ use std::error::Error; -use nasl_syntax::Statement; +use nasl_syntax::{Statement, StatementKind}; use crate::{verify, NaslFileFinder}; @@ -136,11 +136,11 @@ impl Matcher for CallMatcher { // Although Exit and Include are handled differently they share the call nature and hence // are treated equially. matches!( - s, - &Statement::FunctionDeclaration(..) - | &Statement::Call(..) - | &Statement::Exit(..) - | &Statement::Include(..) + s.kind(), + &StatementKind::FunctionDeclaration(..) + | &StatementKind::Call(..) + | &StatementKind::Exit(..) + | &StatementKind::Include(..) ) } } @@ -151,50 +151,68 @@ struct FunctionNameMatcher<'a> { parameter: Option<&'a [FindParameter]>, } -impl<'a> Matcher for FunctionNameMatcher<'a> { - fn matches(&self, s: &Statement) -> bool { - if !match s { - Statement::Exit(t, ..) - | Statement::Include(t, ..) - | Statement::Call(t, ..) - | Statement::FunctionDeclaration(_, t, ..) => match t.category() { - nasl_syntax::TokenCategory::Identifier(nasl_syntax::IdentifierType::Undefined( - aname, - )) => self.name.as_ref().map(|name| name == aname).unwrap_or(true), - nasl_syntax::TokenCategory::Identifier(nasl_syntax::IdentifierType::Exit) => { - self.name.map(|name| name == "exit").unwrap_or(true) +impl<'a> FunctionNameMatcher<'a> { + fn is_function(&self, s: &Statement) -> bool { + match s.kind() { + StatementKind::Exit(..) => self.name.map(|x| x == "exit").unwrap_or(true), + StatementKind::Include(..) => self.name.map(|x| x == "include").unwrap_or(true), + StatementKind::Call(..) => { + if let nasl_syntax::TokenCategory::Identifier( + nasl_syntax::IdentifierType::Undefined(ref x), + ) = s.start().category() + { + self.name.map(|y| x == y).unwrap_or(true) + } else { + false } - nasl_syntax::TokenCategory::Identifier(nasl_syntax::IdentifierType::Include) => { - self.name.map(|name| name == "include").unwrap_or(true) + } + StatementKind::FunctionDeclaration(id, ..) => { + if let nasl_syntax::TokenCategory::Identifier( + nasl_syntax::IdentifierType::Undefined(ref x), + ) = id.category() + { + self.name.map(|y| x == y).unwrap_or(true) + } else { + false } - - _ => false, - }, + } _ => false, - } { + } + } +} + +impl<'a> Matcher for FunctionNameMatcher<'a> { + fn matches(&self, s: &Statement) -> bool { + if !self.is_function(s) { return false; } + if self.parameter.is_none() { return true; } let wanted = unsafe { self.parameter.unwrap_unchecked() }; - let (named, anon) = match s { - Statement::Include(..) | Statement::Exit(..) => (vec![], 1), - Statement::Call(_, p, ..) => { + let (named, anon) = match s.kind() { + StatementKind::Include(..) | StatementKind::Exit(..) => (vec![], 1), + StatementKind::Call(p) => { let named = p + .children() .iter() - .filter_map(|p| match p { - Statement::NamedParameter(name, value) => { - Some((name.category().to_string(), value.to_string())) + .filter_map(|p| match p.kind() { + StatementKind::NamedParameter(value) => { + Some((p.start().category().to_string(), value.to_string())) } _ => None, }) .collect(); - let anon = p.iter().filter(|p| p.is_returnable()).count(); + let anon = p + .children() + .iter() + .filter(|p| p.kind().is_returnable()) + .count(); (named, anon) } - Statement::FunctionDeclaration(_, _, p, _, _block) => { + StatementKind::FunctionDeclaration(_, p, _block) => { let anon = { // we don't know how many anon parameter an declared method is using. // Theoretically we could guess by checking _block for _FC_ANON_ARGS and return @@ -225,9 +243,10 @@ impl<'a> Matcher for FunctionNameMatcher<'a> { 0 }; let named = p + .children() .iter() - .filter_map(|p| match p { - Statement::Variable(name) => Some(name.category().to_string()), + .filter_map(|p| match p.kind() { + StatementKind::Variable => Some(p.start().category().to_string()), _ => None, }) .map(|x| (x, "".to_owned())) @@ -315,29 +334,27 @@ impl CodeReplacer { (start, end) } - fn find_named_parameter<'a>(s: &'a Statement, wanted: &str) -> Option<&'a Statement> { - match s { - Statement::FunctionDeclaration(_, _, stmts, ..) | Statement::Call(_, stmts, ..) => { - use nasl_syntax::IdentifierType::Undefined; - use nasl_syntax::TokenCategory::Identifier; - for s in stmts { - match s { - Statement::Variable(t) | Statement::NamedParameter(t, _) => { - if let nasl_syntax::Token { - category: Identifier(Undefined(name)), - .. - } = t - { - if name == wanted { - return Some(s); - } - } + fn find_named_parameter<'a>( + stmts: &'a [Statement], + wanted: &str, + ) -> Option<(usize, &'a Statement)> { + use nasl_syntax::IdentifierType::Undefined; + use nasl_syntax::TokenCategory::Identifier; + for (i, s) in stmts.iter().enumerate() { + match s.kind() { + StatementKind::Variable | StatementKind::NamedParameter(_) => { + if let nasl_syntax::Token { + category: Identifier(Undefined(name)), + .. + } = s.start() + { + if name == wanted { + return Some((i, s)); } - _ => {} } } + _ => {} } - _ => {} } None } @@ -372,31 +389,25 @@ impl CodeReplacer { self.replace_range_with_offset("", &s.position()); Ok(()) } - Replace::Name(name) => match s { - Statement::FunctionDeclaration(_, n, ..) - | Statement::Call(n, ..) - | Statement::Exit(n, ..) - | Statement::Include(n, ..) => { + Replace::Name(name) => match s.kind() { + // TODO introduce a id method on statement so that one has not + // to differentiate when renaming + StatementKind::FunctionDeclaration(n, ..) => { self.replace_range_with_offset(name, &n.position); Ok(()) } + StatementKind::Call(..) | StatementKind::Exit(..) | StatementKind::Include(..) => { + self.replace_range_with_offset(name, &s.start().position); + Ok(()) + } _ => Err(ReplaceError::Unsupported(r.clone(), s.clone())), }, Replace::Parameter(params) => { - let range = match s { - Statement::FunctionDeclaration(_, _, stmts, ..) - | Statement::Call(_, stmts, ..) => match &stmts[..] { - &[] => None, - [first, ref tail @ ..] => { - let first = first.range(); - let end = tail.last().map(|x| x.range().end).unwrap_or(first.end); - let start = first.start; - Some((start, end)) - } - }, - Statement::Exit(_, stmt, ..) | Statement::Include(_, stmt, ..) => { - Some(stmt.position()) - } + let parameter = match s.kind() { + StatementKind::FunctionDeclaration(_, stmt, ..) + | StatementKind::Call(stmt) + | StatementKind::Exit(stmt, ..) + | StatementKind::Include(stmt, ..) => stmt, _ => return Err(ReplaceError::Unsupported(r.clone(), s.clone())), }; @@ -412,9 +423,12 @@ impl CodeReplacer { self.rename_parameter(s, previous, new) } ParameterOperation::RemoveAll => { - if let Some(range) = range { - self.replace_range_with_offset("", &range); - } + let range = { + let ps = parameter.start(); + let pe = parameter.end(); + (ps.position.1, pe.position.0) + }; + self.replace_range_with_offset("", &range); } }; @@ -485,12 +499,16 @@ impl CodeReplacer { }) } - if let Some((pos, np)) = match s { - Statement::FunctionDeclaration(_, _, args, rp, _) => { - calculate_fn_decl(p, args.is_empty()).map(|x| (rp.position, x)) + if let Some((pos, np)) = match s.kind() { + StatementKind::FunctionDeclaration(_, args, _) => { + let rp = args.end(); + + calculate_fn_decl(p, args.children().is_empty()).map(|x| (rp.position, x)) } - Statement::Call(_, args, rp) => { - calculate_call(p, args.is_empty()).map(|x| (rp.position, x)) + StatementKind::Call(args) => { + let rp = args.end(); + + calculate_call(p, args.children().is_empty()).map(|y| (rp.position, y)) } _ => None, } { @@ -503,19 +521,24 @@ impl CodeReplacer { fn add_parameter(&mut self, s: &Statement, i: usize, p: &Parameter) { fn calculate_known_index(s: &Statement, p: &Parameter) -> Option { - match (matches!(s, Statement::Call(..)), p) { + match (matches!(s.kind(), StatementKind::Call(..)), p) { (true, Parameter::Named(n, v)) => Some(format!("{n}: {v}, ")), (true, Parameter::Anon(s)) => Some(format!("{s}, ")), (false, Parameter::Named(n, _)) => Some(format!("{n}, ")), (false, Parameter::Anon(_)) => None, } } + fn calculate_unknown_index( s: &Statement, p: &Parameter, params: &[Statement], ) -> Option { - let np = match (matches!(s, Statement::Call(..)), params.is_empty(), p) { + let np = match ( + matches!(s.kind(), StatementKind::Call(..)), + params.is_empty(), + p, + ) { (true, true, Parameter::Named(n, v)) => { format!("{n}: {v}") } @@ -527,6 +550,7 @@ impl CodeReplacer { format!(", {s}") } (true, true, Parameter::Anon(s)) => s.to_owned(), + // declaration (false, true, Parameter::Named(n, _)) => n.to_owned(), (false, false, Parameter::Named(n, _)) => format!(", {n}"), @@ -534,69 +558,67 @@ impl CodeReplacer { }; Some(np) } - match s { - Statement::FunctionDeclaration(_, _, params, end, _) - | Statement::Call(_, params, end) - if i <= params.len() || i == 0 => + match s.kind() { + // TODO change params from Vec to a struct either to make it easier to identify start and end + StatementKind::FunctionDeclaration(_, params, _) | StatementKind::Call(params) + if i <= params.children().len() || i == 0 => { - let index_exits = params - .get(i) - .iter() - .flat_map(|s| s.as_token()) - .map(|t| t.position) - .next(); + let get = ¶ms.children().get(i); + let index_exits = get.iter().map(|t| t.position()).next(); let np = if index_exits.is_some() { calculate_known_index(s, p) } else { - calculate_unknown_index(s, p, params) + calculate_unknown_index(s, p, params.children()) }; - if let Some(s) = np { - let position = index_exits.unwrap_or(end.position); + if let Some(stringus) = np { + let position = index_exits.unwrap_or_else(|| { + // TODO reduct on empty + params.end().position + }); let new_position = self.range_with_offset(&position); let before = &self.code[new_position.0..new_position.1]; - self.replace_range(&new_position, &format!("{s}{before}"), &position); + self.replace_range(&new_position, &format!("{stringus}{before}"), &position); } } _ => {} } } - fn remove_parameter(&mut self, s: &Statement) { - let position = s.position(); - let (start, end) = self.range_with_offset(&position); + fn remove_parameter(&mut self, children: &[Statement], idx: usize, s: &Statement) { let new_position = { - let (count, last) = self - .code - .chars() - .skip(end) - .take_while(|x| x.is_whitespace() || x == &',' || x == &')') - .fold((0, '0'), |a, b| (a.0 + 1, b)); - // unless it is the last parameter - if last == ')' { - let (count, last) = self.code[0..start] - .chars() - .rev() - .take_while(|c| c.is_whitespace() || c == &',' || c == &'(') - .fold((0, '0'), |a, b| (a.0 + 1, b)); - let is_only_parameter = last == '('; - if is_only_parameter { - (start, end) - } else { - (start - count, end) - } + // if it is the last parameter and not the only parameter we need + // to remove from previous element end start to current element end start + // so that we remove previous separator ',' but keep the last separator ')'. + if idx == children.len() - 1 && idx != 0 { + let pe = children[idx - 1].end(); + let se = s.end(); + (pe.position.0, se.position.0) + } else if idx < children.len() - 1 { + // to remove unnecessary whitespaces we need to remove from + // current start to next start + let sst = s.start(); + let ns = children[idx + 1].start(); + (sst.position.0, ns.position.0) } else { - (start, end + count) + // for the last element we need remove + let sst = s.start(); + let se = s.end(); + (sst.position.0, se.position.0) } }; + let new_position = self.range_with_offset(&new_position); + self.replace_range(&new_position, "", &new_position); } fn remove_indexed_parameter(&mut self, s: &Statement, i: usize) { - match s { - Statement::FunctionDeclaration(_, _, stmts, ..) | Statement::Call(_, stmts, ..) => { - if let Some(x) = stmts.get(i) { - self.remove_parameter(x); + match s.kind() { + StatementKind::FunctionDeclaration(_, stmts, ..) | StatementKind::Call(stmts) => { + let children = stmts.children(); + + if let Some(x) = children.get(i) { + self.remove_parameter(children, i, x) } } _ => {} @@ -604,16 +626,20 @@ impl CodeReplacer { } fn remove_named_parameter(&mut self, s: &Statement, wanted: &str) { - Self::find_named_parameter(s, wanted).iter().for_each(|s| { - self.remove_parameter(s); - }) + let stmts = s.children(); + Self::find_named_parameter(stmts, wanted) + .iter() + .for_each(|(i, s)| { + self.remove_parameter(stmts, *i, s); + }) } fn rename_parameter(&mut self, s: &Statement, previous: &str, new: &str) { - Self::find_named_parameter(s, previous) + let stmts = s.children(); + Self::find_named_parameter(stmts, previous) .iter() - .for_each(|s| { - let pos = s.as_token().map(|x| x.position).unwrap_or_default(); + .for_each(|(_, s)| { + let pos = s.start().position; self.replace_range_with_offset(new, &pos) }) } @@ -927,6 +953,12 @@ if (user_ports = get_kb_list("sophos/xg_firewall/http-user/port")) { ParameterOperation::Remove(1), "function my_call(a, c){};" ); + + parameter_check!( + "function my_call(a){};", + ParameterOperation::Remove(0), + "function my_call(){};" + ); } #[test] @@ -1076,7 +1108,7 @@ if (user_ports = get_kb_list("sophos/xg_firewall/http-user/port")) { } funkerino(a: 42); funker(a: 42, b: 3); - ; + aha(b: 42); "#; @@ -1104,6 +1136,7 @@ if (user_ports = get_kb_list("sophos/xg_firewall/http-user/port")) { }, ]; let result = CodeReplacer::replace(code, &replaces).unwrap(); + assert_eq!(result, expected.to_owned(),); } @@ -1145,6 +1178,7 @@ if (user_ports = get_kb_list("sophos/xg_firewall/http-user/port")) { }, ]; let result = CodeReplacer::replace(code, &replaces).unwrap(); + assert_eq!( result, //code.replace("funker", "funkerino") diff --git a/rust/nasl-builtin-cryptographic/tests/helper/mod.rs b/rust/nasl-builtin-cryptographic/tests/helper/mod.rs index 800546005d..197469a59e 100644 --- a/rust/nasl-builtin-cryptographic/tests/helper/mod.rs +++ b/rust/nasl-builtin-cryptographic/tests/helper/mod.rs @@ -1,5 +1,6 @@ use std::num::ParseIntError; -pub(crate) fn decode_hex(s: &str) -> Result, ParseIntError> { + +pub fn decode_hex(s: &str) -> Result, ParseIntError> { (0..s.len()) .step_by(2) .map(|i| u8::from_str_radix(&s[i..i + 2], 16)) diff --git a/rust/nasl-cli/src/error.rs b/rust/nasl-cli/src/error.rs index 5cf9d7e79f..e1272573d5 100644 --- a/rust/nasl-cli/src/error.rs +++ b/rust/nasl-cli/src/error.rs @@ -29,7 +29,7 @@ impl CliErrorKind { pub fn as_token(&self) -> Option<&Token> { match self { CliErrorKind::InterpretError(e) => match &e.origin { - Some(s) => s.as_token(), + Some(s) => Some(s.as_token()), None => None, }, CliErrorKind::SyntaxError(e) => e.as_token(), diff --git a/rust/nasl-interpreter/src/assign.rs b/rust/nasl-interpreter/src/assign.rs index 12cdc2e82e..daf8daa56f 100644 --- a/rust/nasl-interpreter/src/assign.rs +++ b/rust/nasl-interpreter/src/assign.rs @@ -9,7 +9,7 @@ use nasl_syntax::{AssignOrder, Statement, TokenCategory}; use crate::{error::InterpretError, interpreter::InterpretResult, Interpreter}; use nasl_builtin_utils::ContextType; use nasl_syntax::NaslValue; -use Statement::*; +use nasl_syntax::StatementKind::*; /// Is a trait to handle function assignments within nasl. pub(crate) trait AssignExtension { @@ -198,12 +198,13 @@ where right: &Statement, ) -> InterpretResult { let (key, lookup) = { - match left { - Variable(ref token) => (Self::identifier(token)?, None), - Array(ref token, Some(stmt), _) => { - (Self::identifier(token)?, Some(self.resolve(stmt)?)) - } - Array(ref token, None, _) => (Self::identifier(token)?, None), + match left.kind() { + Variable => (Self::identifier(left.as_token())?, None), + Array(Some(stmt)) => ( + Self::identifier(left.as_token())?, + Some(self.resolve(stmt)?), + ), + Array(None) => (Self::identifier(left.as_token())?, None), _ => return Err(InterpretError::unsupported(left, "Array or Variable")), } }; diff --git a/rust/nasl-interpreter/src/call.rs b/rust/nasl-interpreter/src/call.rs index 7b21d729fd..5ed245b7a7 100644 --- a/rust/nasl-interpreter/src/call.rs +++ b/rust/nasl-interpreter/src/call.rs @@ -3,7 +3,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later use nasl_builtin_utils::lookup_keys::FC_ANON_ARGS; -use nasl_syntax::{Statement, Statement::*, Token}; +use nasl_syntax::{Statement, StatementKind::*, Token}; use crate::{ error::{FunctionError, InterpretError}, @@ -31,14 +31,14 @@ where let mut position = vec![]; // TODO simplify for p in arguments { - match p { - NamedParameter(token, val) => { + match p.kind() { + NamedParameter(val) => { let val = self.resolve(val)?; - let name = Self::identifier(token)?; + let name = Self::identifier(p.as_token())?; named.insert(name, ContextType::Value(val)); } - val => { - let val = self.resolve(val)?; + _ => { + let val = self.resolve(p)?; position.push(val); } } diff --git a/rust/nasl-interpreter/src/declare.rs b/rust/nasl-interpreter/src/declare.rs index 561480523f..c3af45ea84 100644 --- a/rust/nasl-interpreter/src/declare.rs +++ b/rust/nasl-interpreter/src/declare.rs @@ -2,7 +2,7 @@ // // SPDX-License-Identifier: GPL-2.0-or-later -use nasl_syntax::{Statement, Token, TokenCategory}; +use nasl_syntax::{Statement, StatementKind, Token, TokenCategory}; use crate::{error::InterpretError, interpreter::InterpretResult, Interpreter}; use nasl_builtin_utils::ContextType; @@ -31,9 +31,9 @@ where let name = &Self::identifier(name)?; let mut names = vec![]; for a in arguments { - match a { - Statement::Variable(token) => { - let param_name = &Self::identifier(token)?; + match a.kind() { + StatementKind::Variable => { + let param_name = &Self::identifier(a.as_token())?; names.push(param_name.to_owned()); } _ => return Err(InterpretError::unsupported(a, "variable")), @@ -68,8 +68,8 @@ impl<'a, K> DeclareVariableExtension for Interpreter<'a, K> { }; for stmt in stmts { - if let Statement::Variable(ref token) = stmt { - if let TokenCategory::Identifier(name) = token.category() { + if let StatementKind::Variable = stmt.kind() { + if let TokenCategory::Identifier(name) = stmt.as_token().category() { add(&name.to_string()); } }; diff --git a/rust/nasl-interpreter/src/error.rs b/rust/nasl-interpreter/src/error.rs index 85e98b5dee..ca2e42a97e 100644 --- a/rust/nasl-interpreter/src/error.rs +++ b/rust/nasl-interpreter/src/error.rs @@ -172,7 +172,7 @@ impl InterpretError { pub fn line_column(&self) -> (usize, usize) { self.origin .as_ref() - .and_then(|stmt| stmt.as_token()) + .map(|stmt| stmt.as_token()) .map(|x| x.line_column) .unwrap_or_default() } diff --git a/rust/nasl-interpreter/src/include.rs b/rust/nasl-interpreter/src/include.rs index e8797098e0..efd20180ca 100644 --- a/rust/nasl-interpreter/src/include.rs +++ b/rust/nasl-interpreter/src/include.rs @@ -2,41 +2,11 @@ // // SPDX-License-Identifier: GPL-2.0-or-later -use nasl_syntax::{parse, Statement}; -use crate::{error::InterpretError, interpreter::InterpretResult, Interpreter}; -use nasl_syntax::NaslValue; -/// Is a trait to declare include functionality -pub(crate) trait IncludeExtension { - fn include(&mut self, name: &Statement) -> InterpretResult; -} -impl<'a, K> IncludeExtension for Interpreter<'a, K> -where - K: AsRef, -{ - fn include(&mut self, name: &Statement) -> InterpretResult { - match self.resolve(name)? { - NaslValue::String(key) => { - let code = self.ctxconfigs.loader().load(&key)?; - let mut inter = Interpreter::new(self.registrat, self.ctxconfigs); - let result = parse(&code) - .map(|parsed| match parsed { - Ok(stmt) => inter.resolve(&stmt), - Err(err) => Err(InterpretError::include_syntax_error(&key, err)), - }) - .find(|e| e.is_err()); - match result { - Some(e) => e, - None => Ok(NaslValue::Null), - } - } - _ => Err(InterpretError::unsupported(name, "string")), - } - } -} + #[cfg(test)] mod tests { @@ -56,7 +26,7 @@ mod tests { .ok_or_else(|| LoadError::NotFound(String::default())) } fn root_path(&self) -> Result { - Ok(String::default()) + Ok(String::default()) } } diff --git a/rust/nasl-interpreter/src/interpreter.rs b/rust/nasl-interpreter/src/interpreter.rs index 947cddce1d..44a51865ee 100644 --- a/rust/nasl-interpreter/src/interpreter.rs +++ b/rust/nasl-interpreter/src/interpreter.rs @@ -5,7 +5,7 @@ use std::{collections::HashMap, io}; use nasl_syntax::{ - IdentifierType, LoadError, NaslValue, Statement, Statement::*, Token, TokenCategory, + IdentifierType, LoadError, NaslValue, StatementKind::*, Token, TokenCategory, Statement, }; use storage::StorageError; @@ -13,7 +13,6 @@ use crate::{ assign::AssignExtension, call::CallExtension, declare::{DeclareFunctionExtension, DeclareVariableExtension}, - include::IncludeExtension, loop_extension::LoopExtension, operator::OperatorExtension, InterpretError, InterpretErrorKind, @@ -51,6 +50,27 @@ where } } + + fn include(&mut self, name: &Statement) -> InterpretResult { + match self.resolve(name)? { + NaslValue::String(key) => { + let code = self.ctxconfigs.loader().load(&key)?; + let mut inter = Interpreter::new(self.registrat, self.ctxconfigs); + let result = nasl_syntax::parse(&code) + .map(|parsed| match parsed { + Ok(stmt) => inter.resolve(&stmt), + Err(err) => Err(InterpretError::include_syntax_error(&key, err)), + }) + .find(|e| e.is_err()); + match result { + Some(e) => e, + None => Ok(NaslValue::Null), + } + } + _ => Err(InterpretError::unsupported(name, "string")), + } + } + /// Tries to interpret a statement and retries n times on a retry error /// /// When encountering a retrievable error: @@ -83,9 +103,9 @@ where /// Interprets a Statement pub fn resolve(&mut self, statement: &Statement) -> InterpretResult { - match statement { - Array(name, position, _) => { - let name = Self::identifier(name)?; + match statement.kind(){ + Array(position) => { + let name = Self::identifier(statement.start())?; let val = self .registrat .named(&name) @@ -95,6 +115,7 @@ where match (position, val) { (None, ContextType::Value(v)) => Ok(v), (Some(p), ContextType::Value(NaslValue::Array(x))) => { + let p: &Statement = p; let position = self.resolve(p)?; let position = i64::from(&position) as usize; let result = x.get(position).unwrap_or(&NaslValue::Null); @@ -112,31 +133,31 @@ where } } } - Exit(_, stmt, _) => { + Exit(stmt) => { let rc = self.resolve(stmt)?; match rc { NaslValue::Number(rc) => Ok(NaslValue::Exit(rc)), _ => Err(InterpretError::unsupported(stmt, "numeric")), } } - Return(_, stmt) => { + Return(stmt) => { let rc = self.resolve(stmt)?; Ok(NaslValue::Return(Box::new(rc))) } - Include(_, inc, _) => self.include(inc), + Include(inc ) => self.include(inc), NamedParameter(..) => { unreachable!("named parameter should not be an executable statement.") } - For(_, assignment, condition, update, body) => { + For(assignment, condition, update, body) => { self.for_loop(assignment, condition, update, body) } - While(_, condition, body) => self.while_loop(condition, body), - Repeat(_, body, condition) => self.repeat_loop(body, condition), - ForEach(_, variable, iterable, body) => self.for_each_loop(variable, iterable, body), - FunctionDeclaration(_, name, args, _, exec) => self.declare_function(name, args, exec), - Primitive(token) => TryFrom::try_from(token).map_err(|e: TokenCategory| e.into()), - Variable(token) => { - let name: NaslValue = TryFrom::try_from(token)?; + While(condition, body) => self.while_loop(condition, body), + Repeat(body, condition) => self.repeat_loop(body, condition), + ForEach(variable, iterable, body) => self.for_each_loop(variable, iterable, body), + FunctionDeclaration(name, args, exec) => self.declare_function(name, args.children(), exec), + Primitive => TryFrom::try_from(statement.as_token()).map_err(|e: TokenCategory| e.into()), + Variable => { + let name: NaslValue = TryFrom::try_from(statement.as_token())?; match self.registrat.named(&name.to_string()) { Some(ContextType::Value(result)) => Ok(result.clone()), None => Ok(NaslValue::Null), @@ -145,8 +166,8 @@ where } } } - Call(name, arguments, _) => self.call(name, arguments), - Declare(scope, stmts) => self.declare_variable(scope, stmts), + Call(arguments) => self.call(statement.as_token(), arguments.children()), + Declare(stmts) => self.declare_variable(statement.as_token(), stmts), // array creation Parameter(x) => { let mut result = vec![]; @@ -158,7 +179,7 @@ where } Assign(cat, order, left, right) => self.assign(cat, order, left, right), Operator(sign, stmts) => self.operator(sign, stmts), - If(_, condition, if_block, _, else_block) => match self.resolve(condition) { + If(condition, if_block, _, else_block) => match self.resolve(condition) { Ok(value) => { if bool::from(value) { return self.resolve(if_block); @@ -169,7 +190,7 @@ where } Err(err) => Err(err), }, - Block(_, blocks, _) => { + Block(blocks) => { self.registrat.create_child(HashMap::default()); for stmt in blocks { match self.resolve(stmt) { @@ -192,19 +213,19 @@ where // currently blocks don't return something Ok(NaslValue::Null) } - NoOp(_) => Ok(NaslValue::Null), + NoOp => Ok(NaslValue::Null), EoF => Ok(NaslValue::Null), - AttackCategory(t) => { - match t.category() { + AttackCategory => { + match statement.as_token().category() { TokenCategory::Identifier(IdentifierType::ACT(cat)) => Ok(NaslValue::AttackCategory(*cat)), - _ => unreachable!("AttackCategory must have ACT token but got {t:?}, this is an bug within the lexer.") + _ => unreachable!("AttackCategory must have ACT token but got {:?}, this is an bug within the lexer.", statement.as_token()) } }, - Continue(_) => Ok(NaslValue::Continue), - Break(_) => Ok(NaslValue::Break), + Continue => Ok(NaslValue::Continue), + Break => Ok(NaslValue::Break), } .map_err(|e| { if e.origin.is_none() { diff --git a/rust/nasl-syntax/src/error.rs b/rust/nasl-syntax/src/error.rs index 9e2f2eacb6..e63e11481e 100644 --- a/rust/nasl-syntax/src/error.rs +++ b/rust/nasl-syntax/src/error.rs @@ -47,9 +47,9 @@ impl SyntaxError { match &self.kind { ErrorKind::UnexpectedToken(t) => Some(t), ErrorKind::UnclosedToken(t) => Some(t), - ErrorKind::UnexpectedStatement(s) => s.as_token(), - ErrorKind::MissingSemicolon(s) => s.as_token(), - ErrorKind::UnclosedStatement(s) => s.as_token(), + ErrorKind::UnexpectedStatement(s) => Some(s.as_token()), + ErrorKind::MissingSemicolon(s) => Some(s.as_token()), + ErrorKind::UnclosedStatement(s) => Some(s.as_token()), ErrorKind::EoF => None, ErrorKind::IOError(_) => None, ErrorKind::MaxRecursionDepth(_) => None, @@ -108,8 +108,8 @@ macro_rules! unexpected_token { /// /// Basic usage: /// ```rust -/// use nasl_syntax::{unexpected_statement, Statement}; -/// unexpected_statement!(Statement::EoF); +/// use nasl_syntax::{unexpected_statement, Statement, StatementKind}; +/// unexpected_statement!(Statement::without_token(StatementKind::EoF)); /// ``` #[macro_export] macro_rules! unexpected_statement { @@ -126,8 +126,8 @@ macro_rules! unexpected_statement { /// /// Basic usage: /// ```rust -/// use nasl_syntax::{unexpected_statement, Statement}; -/// unexpected_statement!(Statement::EoF); +/// use nasl_syntax::{unclosed_statement, Statement, StatementKind}; +/// unclosed_statement!(Statement::without_token(StatementKind::EoF)); /// ``` #[macro_export] macro_rules! unclosed_statement { @@ -255,7 +255,7 @@ mod tests { fn test_for_missing_semicolon(code: &str) { let result = parse(code).next().unwrap(); match result { - Ok(_) => panic!("expected test to return Err"), + Ok(_) => panic!("expected test to return Err for {code}"), Err(e) => match e.kind { ErrorKind::MissingSemicolon(_) => {} _ => panic!("Expected MissingSemicolon but got: {e:?}"), diff --git a/rust/nasl-syntax/src/grouping_extension.rs b/rust/nasl-syntax/src/grouping_extension.rs index 631b7aa9b5..faae912f18 100644 --- a/rust/nasl-syntax/src/grouping_extension.rs +++ b/rust/nasl-syntax/src/grouping_extension.rs @@ -6,59 +6,63 @@ use crate::{ error::SyntaxError, lexer::{End, Lexer}, token::{Category, Token}, - unclosed_token, unexpected_token, - variable_extension::CommaGroup, - AssignOrder, Statement, + unclosed_token, unexpected_token, AssignOrder, Statement, StatementKind, }; pub(crate) trait Grouping { /// Parses (...) fn parse_paren(&mut self, token: Token) -> Result; /// Parses {...} - fn parse_block(&mut self, token: Token) -> Result<(Token, Statement), SyntaxError>; + fn parse_block(&mut self, token: Token) -> Result; /// General Grouping parsing. Is called within prefix_extension. fn parse_grouping(&mut self, token: Token) -> Result<(End, Statement), SyntaxError>; } -impl<'a> Lexer<'a> { - fn parse_brace(&mut self, token: Token) -> Result { - let (end, right) = self.parse_comma_group(Category::RightBrace)?; - match end { - End::Done(_end) => Ok(Statement::Parameter(right)), - End::Continue => Err(unclosed_token!(token)), - } - } -} - impl<'a> Grouping for Lexer<'a> { fn parse_paren(&mut self, token: Token) -> Result { let (end, right) = self.statement(0, &|cat| cat == &Category::RightParen)?; - if !end { - Err(unclosed_token!(token)) - } else { - self.depth = 0; - match right { - Statement::Assign(category, _, variable, stmt) => Ok(Statement::Assign( - category, - AssignOrder::AssignReturn, - variable, - stmt, - )), - _ => Ok(right), + + match end { + End::Done(end) => { + self.depth = 0; + Ok(match right.kind() { + StatementKind::Assign(cat, _, first, second) => { + Statement::with_start_end_token( + token, + end, + StatementKind::Assign( + cat.clone(), + AssignOrder::AssignReturn, + first.clone(), + second.clone(), + ), + ) + } + + _ => right, + }) } + + End::Continue => Err(unclosed_token!(token)), } } - fn parse_block(&mut self, kw: Token) -> Result<(Token, Statement), SyntaxError> { + fn parse_block(&mut self, kw: Token) -> Result { let mut results = vec![]; while let Some(token) = self.peek() { if token.category() == &Category::RightCurlyBracket { - self.token(); + let _ = self.token(); + self.depth = 0; - return Ok((token.clone(), Statement::Block(kw, results, token))); + let stmt = Statement::with_start_end_token( + kw, + token.clone(), + StatementKind::Block(results), + ); + return Ok(stmt); } let (end, stmt) = self.statement(0, &|cat| cat == &Category::Semicolon)?; - if end.is_done() && !matches!(stmt, Statement::NoOp(_)) { + if end.is_done() && !matches!(stmt.kind(), StatementKind::NoOp) { results.push(stmt); } } @@ -66,12 +70,30 @@ impl<'a> Grouping for Lexer<'a> { } fn parse_grouping(&mut self, token: Token) -> Result<(End, Statement), SyntaxError> { + fn as_done(stmt: Statement) -> (End, Statement) { + (End::Done(stmt.end().clone()), stmt) + } + + fn as_con(stmt: Statement) -> (End, Statement) { + (End::Continue, stmt) + } match token.category() { - Category::LeftParen => self.parse_paren(token).map(|stmt| (End::Continue, stmt)), - Category::LeftCurlyBracket => self - .parse_block(token) - .map(|(end, stmt)| (End::Done(end), stmt)), - Category::LeftBrace => self.parse_brace(token).map(|stmt| (End::Continue, stmt)), + Category::LeftParen => self.parse_paren(token).map(as_con), + Category::LeftCurlyBracket => self.parse_block(token).map(as_done), + Category::LeftBrace => { + let (end, right) = self.parse_comma_group(Category::RightBrace)?; + match end { + End::Done(end) => Ok(( + End::Continue, + Statement::with_start_end_token( + token, + end, + StatementKind::Parameter(right), + ), + )), + End::Continue => Err(unclosed_token!(token)), + } + } _ => Err(unexpected_token!(token)), } } @@ -79,12 +101,12 @@ impl<'a> Grouping for Lexer<'a> { #[cfg(test)] mod test { - use crate::{parse, Statement}; + use crate::{parse, StatementKind}; - use Statement::*; + use StatementKind::*; - fn result(code: &str) -> Statement { - parse(code).next().unwrap().unwrap() + fn result(code: &str) -> StatementKind { + parse(code).next().unwrap().unwrap().kind().clone() } #[test] diff --git a/rust/nasl-syntax/src/infix_extension.rs b/rust/nasl-syntax/src/infix_extension.rs index 6a23717102..d229e84795 100644 --- a/rust/nasl-syntax/src/infix_extension.rs +++ b/rust/nasl-syntax/src/infix_extension.rs @@ -9,13 +9,13 @@ use crate::{ lexer::{End, Lexer}, operation::Operation, token::{Category, Token}, - unexpected_end, unexpected_statement, {AssignOrder, Statement}, + unexpected_end, unexpected_statement, Statement, {AssignOrder, StatementKind}, }; pub(crate) trait Infix { /// Returns true when an Operation needs a infix handling. /// /// This is separated in two methods to prevent unnecessary clones of a previous statement. - fn needs_infix(&self, op: Operation, min_bp: u8) -> Option; + fn needs_infix(&self, op: &Operation, min_bp: u8) -> Option; /// Is the actual handling of infix. The caller must ensure that needs_infix is called previously. fn infix_statement( @@ -32,7 +32,7 @@ pub(crate) trait Infix { /// The binding power is used to express the order of a statement. /// Because the binding power of e,g. Plus is lower than Star the Star operation gets calculate before. /// The first number represents the left hand, the second number the right hand binding power -fn infix_binding_power(op: Operation) -> Option<(u8, u8)> { +fn infix_binding_power(op: &Operation) -> Option<(u8, u8)> { use self::Operation::*; use Category::*; let res = match op { @@ -70,69 +70,100 @@ impl<'a> Infix for Lexer<'a> { Ok({ // binding power of the right side let (_, right_bp) = - infix_binding_power(op.clone()).expect("handle_infix should be called first"); - let (end, rhs) = self.statement(right_bp, abort)?; + infix_binding_power(&op).expect("handle_infix should be called first"); + // probably better to change EoF to error instead of Ok(Continue) to not have to double check each time + let (mut end, rhs) = self.statement(right_bp, abort)?; + // this feels like a hack ... maybe better to use an own abort condition for the right side instead? + // TODO set end token to token when done + if let End::Done(ref x) = end { + if !abort(x.category()) { + end = End::Continue; + } + } + if matches!(rhs.kind(), StatementKind::EoF) { + return Ok((End::Done(token), rhs)); + } + + let end_token = match &end { + End::Done(x) => x.clone(), + End::Continue => rhs.end().clone(), + }; + let start_token = lhs.start().clone(); + let build_stmt = |k| Statement::with_start_end_token(start_token, end_token, k); + let stmt = match op { // DoublePoint operation needs to be changed to NamedParameter statement - Operation::Assign(Category::DoublePoint) => match lhs { - Statement::Variable(left) => { - // if the right side is a parameter we need to transform the NamedParameter - // from the atomic params and assign the first one to the NamedParameter instead - // of Statement::Parameter and put it upfront - match rhs { - Statement::Parameter(params) => { - first_element_as_named_parameter(params, left)? - } + Operation::Assign(Category::DoublePoint) => { + match lhs.kind() { + StatementKind::Variable => { + // if the right side is a parameter we need to transform the NamedParameter + // from the atomic params and assign the first one to the NamedParameter instead + // of Statement::Parameter and put it upfront + match rhs.kind() { + StatementKind::Parameter(params) => { + // TODO flatten + first_element_as_named_parameter(params.clone())? + } - _ => Statement::NamedParameter(left, Box::new(rhs)), + _ => build_stmt(StatementKind::NamedParameter(Box::new(rhs))), + } } + StatementKind::Parameter(params) => match rhs.kind() { + StatementKind::Parameter(right_params) => { + let mut params = params.clone(); + params.extend_from_slice(right_params); + build_stmt(StatementKind::Parameter(params)) + } + _ => { + let mut params = params.clone(); + params.push(rhs); + build_stmt(StatementKind::Parameter(params)) + } + }, + _ => return Err(unexpected_statement!(lhs)), } - Statement::Parameter(mut params) => match rhs { - Statement::Parameter(right_params) => { - params.extend_from_slice(&right_params); - Statement::Parameter(params) - } - _ => { - params.push(rhs); - Statement::Parameter(params) - } - }, - _ => return Err(unexpected_statement!(lhs)), - }, + } // Assign needs to be translated due handle the return cases for e.g. ( a = 1) * 2 - Operation::Assign(category) => match lhs { - Statement::Variable(ref var) => { - // when the right side is a parameter list than it is an array - let lhs = { - match rhs { - Statement::Parameter(..) => { - Statement::Array(var.clone(), None, None) - } - _ => lhs, - } + Operation::Assign(category) => match lhs.kind() { + StatementKind::Variable => { + let lhs = match rhs.kind() { + StatementKind::Parameter(..) => Statement::with_start_end_token( + lhs.start().clone(), + rhs.end().clone(), + StatementKind::Array(None), + ), + _ => lhs, }; - Statement::Assign( + + build_stmt(StatementKind::Assign( category, AssignOrder::AssignReturn, Box::new(lhs), Box::new(rhs), - ) + )) } - Statement::Array(_, _, _) => Statement::Assign( + StatementKind::Array(..) => build_stmt(StatementKind::Assign( category, AssignOrder::AssignReturn, Box::new(lhs), Box::new(rhs), - ), - _ => Statement::Operator(token.category().clone(), vec![lhs, rhs]), + )), + + _ => build_stmt(StatementKind::Operator( + token.category().clone(), + vec![lhs, rhs], + )), }, - _ => Statement::Operator(token.category().clone(), vec![lhs, rhs]), + _ => build_stmt(StatementKind::Operator( + token.category().clone(), + vec![lhs, rhs], + )), }; (end, stmt) }) } - fn needs_infix(&self, op: Operation, min_bp: u8) -> Option { + fn needs_infix(&self, op: &Operation, min_bp: u8) -> Option { let (l_bp, _) = infix_binding_power(op)?; if l_bp < min_bp { Some(false) @@ -145,44 +176,42 @@ impl<'a> Infix for Lexer<'a> { // if the right side is a parameter we need to transform the NamedParameter // from the atomic params and assign the first one to the NamedParameter instead // of Statement::Parameter and put it upfront -fn first_element_as_named_parameter( - mut params: Vec, - left: Token, -) -> Result { +fn first_element_as_named_parameter(mut params: Vec) -> Result { params.reverse(); let value = params .pop() .ok_or_else(|| unexpected_end!("while getting value of named parameter"))?; - let np = Statement::NamedParameter(left, Box::new(value)); - params.push(np); + let np = StatementKind::NamedParameter(Box::new(value)); + params.push(Statement::without_token(np)); params.reverse(); - Ok(Statement::Parameter(params)) + Ok(Statement::without_token(StatementKind::Parameter(params))) } #[cfg(test)] mod test { + use core::panic; + use super::*; use crate::token::Category::*; - use crate::token::Token; - use crate::IdentifierType::Undefined; - use Statement::*; + + + use StatementKind::*; // simplified resolve method to verify a calculate with a given statement - fn resolve(s: Statement) -> i64 { - let callable = |mut stmts: Vec, calculus: Box i64>| -> i64 { - let right = stmts.pop().unwrap(); - let left = stmts.pop().unwrap(); + fn resolve(s: &Statement) -> i64 { + let callable = |stmts: &[Statement], calculus: Box i64>| -> i64 { + let right = &stmts[1]; + let left = &stmts[0]; calculus(resolve(left), resolve(right)) }; - let single_callable = - |mut stmts: Vec, calculus: Box i64>| -> i64 { - let left = stmts.pop().unwrap(); - calculus(resolve(left)) - }; - match s { - Primitive(token) => match token.category() { + let single_callable = |stmts: &[Statement], calculus: Box i64>| -> i64 { + let left = &stmts[0]; + calculus(resolve(left)) + }; + match s.kind() { + Primitive => match s.start().category() { Number(num) => *num, String(_) => todo!(), _ => todo!(), @@ -231,7 +260,7 @@ mod test { macro_rules! calculated_test { ($code:expr, $expected:expr) => { let expr = crate::parse($code).next().unwrap().unwrap(); - assert_eq!(resolve(expr), $expected); + assert_eq!(resolve(&expr), $expected); }; } @@ -245,6 +274,7 @@ mod test { #[test] fn grouping() { + //calculated_test!("2 * (2 + 5);", 13); calculated_test!("(2 + 5) * 2;", 14); } @@ -264,153 +294,77 @@ mod test { calculated_test!("~1 | 0;", -2); calculated_test!("1 ^ 1;", 0); } + #[test] fn operator_assignment() { use Category::*; - use Statement::*; - fn expected(category: Category, shift: usize) -> Statement { - let a = Token { - category: Identifier(Undefined("a".to_owned())), - line_column: (1, 1), - position: (0, 1), - }; - let no = Token { - category: Number(1), - line_column: (1, 6 + shift), - position: (5 + shift, 6 + shift), - }; - - Assign( - category, - AssignOrder::AssignReturn, - Box::new(Variable(a)), - Box::new(Primitive(no)), - ) + fn expected(stmt: Statement, category: Category) { + match stmt.kind() { + StatementKind::Assign(cat, AssignOrder::AssignReturn, ..) => { + assert_eq!(cat, &category); + } + kind => panic!("Expected Assign, got: {:?}", kind), + } } - assert_eq!(result("a += 1;"), expected(PlusEqual, 0)); - assert_eq!(result("a -= 1;"), expected(MinusEqual, 0)); - assert_eq!(result("a /= 1;"), expected(SlashEqual, 0)); - assert_eq!(result("a *= 1;"), expected(StarEqual, 0)); - assert_eq!(result("a %= 1;"), expected(PercentEqual, 0)); - assert_eq!(result("a >>= 1;"), expected(GreaterGreaterEqual, 1)); - assert_eq!(result("a <<= 1;"), expected(LessLessEqual, 1)); - assert_eq!(result("a >>>= 1;"), expected(GreaterGreaterGreaterEqual, 2)); + expected(result("a += 1;"), PlusEqual); + expected(result("a -= 1;"), MinusEqual); + expected(result("a /= 1;"), SlashEqual); + expected(result("a *= 1;"), StarEqual); + expected(result("a %= 1;"), PercentEqual); + expected(result("a >>= 1;"), GreaterGreaterEqual); + expected(result("a <<= 1;"), LessLessEqual); + expected(result("a >>>= 1;"), GreaterGreaterGreaterEqual); } #[test] fn compare_operator() { use Category::*; - use Statement::*; - fn expected(category: Category, shift: i32) -> Statement { - let a = Token { - category: Identifier(Undefined("a".to_owned())), - line_column: (1, 1), - position: (0, 1), - }; - let data = Token { - category: Data(vec![49]), - line_column: (1, (6 + shift) as usize), - position: ((5 + shift) as usize, (8 + shift) as usize), - }; - Operator(category, vec![Variable(a), Primitive(data)]) + fn expected(stmt: Statement, category: Category) { + match stmt.kind() { + StatementKind::Operator(cat, ..) => { + assert_eq!(cat, &category); + } + kind => panic!("Expected Operator, got: {:?}", kind), + } } - assert_eq!(result("a !~ '1';"), expected(BangTilde, 0)); - assert_eq!(result("a =~ '1';"), expected(EqualTilde, 0)); - assert_eq!(result("a >< '1';"), expected(GreaterLess, 0)); - assert_eq!(result("a >!< '1';"), expected(GreaterBangLess, 1)); - assert_eq!(result("a == '1';"), expected(EqualEqual, 0)); - assert_eq!(result("a != '1';"), expected(BangEqual, 0)); - assert_eq!(result("a > '1';"), expected(Greater, -1)); - assert_eq!(result("a < '1';"), expected(Less, -1)); - assert_eq!(result("a >= '1';"), expected(GreaterEqual, 0)); - assert_eq!(result("a <= '1';"), expected(LessEqual, 0)); + expected(result("a !~ '1';"), BangTilde); + expected(result("a =~ '1';"), EqualTilde); + expected(result("a >< '1';"), GreaterLess); + expected(result("a >!< '1';"), GreaterBangLess); + expected(result("a == '1';"), EqualEqual); + expected(result("a != '1';"), BangEqual); + expected(result("a > '1';"), Greater); + expected(result("a < '1';"), Less); + expected(result("a >= '1';"), GreaterEqual); + expected(result("a <= '1';"), LessEqual); + expected(result("x() x 2;"), X); } #[test] fn logical_operator() { - fn expected(category: Category, shift: usize) -> Statement { - let a = Token { - category: Identifier(Undefined("a".to_owned())), - line_column: (1, 1), - position: (0, 1), - }; - - let no = Token { - category: Number(1), - line_column: (1, 6 + shift), - position: (5 + shift, 6 + shift), - }; - Operator(category, vec![Variable(a), Primitive(no)]) + fn expected(stmt: Statement, category: Category) { + match stmt.kind() { + StatementKind::Operator(cat, ..) => { + assert_eq!(cat, &category); + } + kind => panic!("Expected Operator, got: {:?}", kind), + } } - assert_eq!(result("a && 1;"), expected(AmpersandAmpersand, 0)); - assert_eq!(result("a || 1;"), expected(PipePipe, 0)); + expected(result("a && 1;"), AmpersandAmpersand); + expected(result("a || 1;"), PipePipe); } #[test] fn assignment() { - let a = Token { - category: Identifier(Undefined("a".to_owned())), - line_column: (1, 1), - position: (0, 1), - }; - let no = Token { - category: Number(1), - line_column: (1, 5), - position: (4, 5), - }; - assert_eq!( - result("a = 1;"), - Assign( - Category::Equal, - AssignOrder::AssignReturn, - Box::new(Variable(a)), - Box::new(Primitive(no)) - ) - ); - let a = Token { - category: Identifier(Undefined("a".to_owned())), - line_column: (1, 2), - position: (1, 2), - }; - let no = Token { - category: Number(1), - line_column: (1, 6), - position: (5, 6), - }; - - assert_eq!( - result("(a = 1);"), - Assign( - Category::Equal, - AssignOrder::AssignReturn, - Box::new(Variable(a)), - Box::new(Primitive(no)) - ) - ); - } - - #[test] - fn repeat_call() { - let x = Token { - category: Identifier(Undefined("x".to_owned())), - line_column: (1, 1), - position: (0, 1), - }; - let end = Token { - category: RightParen, - line_column: (1, 3), - position: (2, 3), - }; - let no = Token { - category: Number(2), - line_column: (1, 7), - position: (6, 7), - }; - - assert_eq!( - result("x() x 2;"), - Operator(X, vec![Call(x, vec![], end,), Primitive(no)]) - ); + fn expected(stmt: Statement, category: Category) { + match stmt.kind() { + StatementKind::Assign(cat, AssignOrder::AssignReturn, ..) => { + assert_eq!(cat, &category); + } + kind => panic!("Expected Assign, got: {:?}", kind), + } + } + expected(result("(a = 1);"), Category::Equal); } } diff --git a/rust/nasl-syntax/src/keyword_extension.rs b/rust/nasl-syntax/src/keyword_extension.rs index e4dcef775b..218841eb25 100644 --- a/rust/nasl-syntax/src/keyword_extension.rs +++ b/rust/nasl-syntax/src/keyword_extension.rs @@ -8,8 +8,7 @@ use crate::{ lexer::{End, Lexer}, token::{Category, IdentifierType, Token}, unclosed_statement, unclosed_token, unexpected_end, unexpected_statement, unexpected_token, - variable_extension::CommaGroup, - Statement, + Statement, StatementKind, }; pub(crate) trait Keywords { @@ -22,21 +21,24 @@ pub(crate) trait Keywords { } impl<'a> Lexer<'a> { - fn parse_declaration(&mut self, token: Token) -> Result<(End, Statement), SyntaxError> { + fn parse_declaration(&mut self, token: Token) -> Result { let (end, params) = self.parse_comma_group(Category::Semicolon)?; - if end == End::Continue { - return Err(unexpected_end!("expected a finished statement.")); - } - if let Some(errstmt) = params - .iter() - .find(|stmt| !matches!(stmt, Statement::Variable(_))) - { - return Err(unexpected_statement!(errstmt.clone())); + match end { + End::Done(end) => { + if let Some(errstmt) = params + .iter() + .find(|stmt| !matches!(stmt.kind(), StatementKind::Variable)) + { + return Err(unexpected_statement!(errstmt.clone())); + } + let result = + Statement::with_start_end_token(token, end, StatementKind::Declare(params)); + Ok(result) + } + End::Continue => Err(unexpected_end!("expected a finished statement.")), } - let result = Statement::Declare(token, params); - Ok((end, result)) } - fn parse_if(&mut self, kw: Token) -> Result<(End, Statement), SyntaxError> { + fn parse_if(&mut self, kw: Token) -> Result { let ptoken = self.token().ok_or_else(|| unexpected_end!("if parsing"))?; let condition = match ptoken.category() { Category::LeftParen => self.parse_paren(ptoken.clone())?, @@ -44,35 +46,41 @@ impl<'a> Lexer<'a> { } .as_returnable_or_err()?; let (end, body) = self.statement(0, &|cat| cat == &Category::Semicolon)?; - if end == End::Continue { - return Err(unclosed_token!(ptoken)); - } + let end = { + match end { + End::Done(end) => end, + End::Continue => return Err(unclosed_token!(ptoken)), + } + }; + let (ekw, r#else, end) = { match self.peek() { Some(token) => match token.category() { Category::Identifier(IdentifierType::Else) => { self.token(); let (end, stmt) = self.statement(0, &|cat| cat == &Category::Semicolon)?; - if end == End::Continue { - return Err(unexpected_statement!(stmt)); + + match end { + End::Done(end) => (Some(token), Some(stmt), end), + End::Continue => return Err(unexpected_statement!(stmt)), } - (Some(token), Some(stmt), end) } _ => (None, None, end), }, None => (None, None, end), } }; - Ok(( - end, - Statement::If( - kw, + let result = Statement::with_start_end_token( + kw, + end.clone(), + StatementKind::If( Box::new(condition), Box::new(body), ekw, r#else.map(Box::new), ), - )) + ); + Ok(result) } fn jump_to_left_parenthesis(&mut self) -> Result<(), SyntaxError> { @@ -86,53 +94,50 @@ impl<'a> Lexer<'a> { } } - fn parse_call_return_params(&mut self) -> Result<(End, Statement), SyntaxError> { + fn parse_call_return_params(&mut self) -> Result { self.jump_to_left_parenthesis()?; let (end, parameter) = self.statement(0, &|cat| cat == &Category::RightParen)?; let parameter = parameter.as_returnable_or_err()?; match end { - End::Done(end) => Ok((End::Done(end), parameter)), + End::Done(_) => Ok(parameter), End::Continue => Err(unexpected_end!("exit")), } } - fn parse_exit(&mut self, token: Token) -> Result<(End, Statement), SyntaxError> { + fn parse_exit(&mut self, token: Token) -> Result { // TODO maybe refactor to reuse function call and hindsight verification - let (end, parameter) = self.parse_call_return_params()?; + let parameter = self.parse_call_return_params()?; let (_, should_be_semicolon) = self.statement(0, &|cat| cat == &Category::Semicolon)?; - if !matches!(should_be_semicolon, Statement::NoOp(_)) { + if !matches!(should_be_semicolon.kind(), &StatementKind::NoOp) { // exit must be followed by ; nothing else return Err(unexpected_statement!(should_be_semicolon)); } - match end { - End::Done(end) => Ok(( - End::Done(end.clone()), - Statement::Exit(token, Box::new(parameter), end), - )), - End::Continue => Err(unexpected_statement!(parameter)), - } + + Ok(Statement::with_start_end_token( + token, + should_be_semicolon.end().clone(), + StatementKind::Exit(Box::new(parameter)), + )) } - fn parse_include(&mut self, token: Token) -> Result<(End, Statement), SyntaxError> { + fn parse_include(&mut self, token: Token) -> Result { // TODO maybe refactor to reuse function call and hindsight verification - let (end, parameter) = self.parse_call_return_params()?; + let parameter = self.parse_call_return_params()?; let (_, should_be_semicolon) = self.statement(0, &|cat| cat == &Category::Semicolon)?; - if !matches!(should_be_semicolon, Statement::NoOp(_)) { + if !matches!(should_be_semicolon.kind(), &StatementKind::NoOp) { // exit must be followed by ; nothing else return Err(unexpected_statement!(should_be_semicolon)); } - match end { - End::Done(end) => Ok(( - End::Done(end.clone()), - Statement::Include(token, Box::new(parameter), end), - )), - End::Continue => Err(unexpected_statement!(parameter)), - } + Ok(Statement::with_start_end_token( + token, + should_be_semicolon.end().clone(), + StatementKind::Include(Box::new(parameter)), + )) } - fn parse_function(&mut self, token: Token) -> Result<(End, Statement), SyntaxError> { + fn parse_function(&mut self, token: Token) -> Result { let id = self .token() .ok_or_else(|| unexpected_end!("parse_function"))?; @@ -153,6 +158,11 @@ impl<'a> Lexer<'a> { End::Done(t) => t, End::Continue => return Err(unclosed_token!(token)), }; + let parameter = Statement::with_start_end_token( + paren, + parameter_end_token, + StatementKind::Parameter(parameter), + ); let block = self .token() @@ -160,58 +170,62 @@ impl<'a> Lexer<'a> { if !matches!(block.category(), Category::LeftCurlyBracket) { return Err(unexpected_token!(block)); } - let (end, block) = self.parse_block(block)?; - Ok(( - End::Done(end), - Statement::FunctionDeclaration( - token, - id, - parameter, - parameter_end_token, - Box::new(block), - ), + let block = self.parse_block(block)?; + Ok(Statement::with_start_end_token( + token, + block.end().clone(), + StatementKind::FunctionDeclaration(id, Box::new(parameter), Box::new(block)), )) } - fn parse_return(&mut self, token: Token) -> Result<(End, Statement), SyntaxError> { + fn parse_return(&mut self, token: Token) -> Result { if let Some(sc) = self.peek() { if matches!(sc.category(), Category::Semicolon) { self.token(); - return Ok(( - End::Done(sc.clone()), - Statement::Return(token, Box::new(Statement::NoOp(Some(sc)))), + return Ok(Statement::with_start_end_token( + token, + sc.clone(), + StatementKind::Return(Box::new(Statement::without_token(StatementKind::NoOp))), )); } } let (end, parameter) = self.statement(0, &|cat| cat == &Category::Semicolon)?; let parameter = parameter.as_returnable_or_err()?; - if let End::Done(cat) = end { - Ok(( - End::Done(cat), - Statement::Return(token, Box::new(parameter)), - )) - } else { - Err(unexpected_end!("exit")) + match end { + End::Done(end) => Ok(Statement::with_start_end_token( + token, + end, + StatementKind::Return(Box::new(parameter)), + )), + End::Continue => Err(unclosed_statement!(parameter)), } } - fn parse_continue(&mut self, kw: Token) -> Result<(End, Statement), SyntaxError> { + fn parse_continue(&mut self, kw: Token) -> Result { let token = self.peek(); if let Some(token) = token { if matches!(token.category(), Category::Semicolon) { self.token(); - return Ok((End::Done(token), Statement::Continue(kw))); + return Ok(Statement::with_start_end_token( + kw, + token, + StatementKind::Continue, + )); } else { return Err(unexpected_token!(token)); } } Err(unexpected_end!("exit")) } - fn parse_break(&mut self, kw: Token) -> Result<(End, Statement), SyntaxError> { + fn parse_break(&mut self, kw: Token) -> Result { let token = self.peek(); if let Some(token) = token { if matches!(token.category(), Category::Semicolon) { self.token(); - return Ok((End::Done(token), Statement::Break(kw))); + return Ok(Statement::with_start_end_token( + kw, + token, + StatementKind::Break, + )); } else { return Err(unexpected_token!(token)); } @@ -230,12 +244,19 @@ impl<'a> Lexer<'a> { } } - fn parse_for(&mut self, kw: Token) -> Result<(End, Statement), SyntaxError> { + fn is_end_of_category(end: &End, category: Category) -> bool { + match end { + End::Done(x) => x.category() == &category, + End::Continue => false, + } + } + + fn parse_for(&mut self, kw: Token) -> Result { self.jump_to_left_parenthesis()?; let (end, assignment) = self.statement(0, &|c| c == &Category::Semicolon)?; if !matches!( - assignment, - Statement::Assign(_, _, _, _) | Statement::NoOp(_) + assignment.kind(), + StatementKind::Assign(..) | StatementKind::NoOp ) { return Err(unexpected_statement!(assignment)); } @@ -262,67 +283,69 @@ impl<'a> Lexer<'a> { line_column, position, }), - Statement::NoOp(None), + Statement::without_token(StatementKind::NoOp), ) } _ => self .statement(0, &|c| c == &Category::RightParen) .map_err(Self::map_syntax_error_to_unclosed_left_paren)?, }; - if !matches!(end.category(), Some(Category::RightParen)) { + if !Self::is_end_of_category(&end, Category::RightParen) { + dbg!(end, update.end().category()); let ut = update.as_token(); return Err(unclosed_token!(Token { category: Category::LeftParen, - line_column: ut.map_or_else(|| (0, 0), |t| t.line_column), - position: ut.map_or_else(|| (0, 0), |t| t.position) + line_column: ut.line_column, + position: ut.position })); } let (end, body) = self.statement(0, &|c| c == &Category::Semicolon)?; match end { - End::Done(cat) => Ok(( - End::Done(cat), - Statement::For( - kw, + End::Continue => Err(unclosed_statement!(body)), + End::Done(end) => Ok(Statement::with_start_end_token( + kw, + end.clone(), + StatementKind::For( Box::new(assignment), Box::new(condition), Box::new(update), Box::new(body), ), )), - End::Continue => Err(unclosed_statement!(body)), } } - fn parse_while(&mut self, token: Token) -> Result<(End, Statement), SyntaxError> { + fn parse_while(&mut self, token: Token) -> Result { self.jump_to_left_parenthesis()?; let (end, condition) = self .statement(0, &|c| c == &Category::RightParen) .map_err(Self::map_syntax_error_to_unclosed_left_paren)?; let ct = condition.as_token(); - if !matches!(end.category(), Some(Category::RightParen)) { + if !Self::is_end_of_category(&end, Category::RightParen) { return Err(unclosed_token!(Token { category: Category::LeftParen, - line_column: ct.map_or_else(|| (0, 0), |t| t.line_column), - position: ct.map_or_else(|| (0, 0), |t| t.position), + line_column: ct.line_column, + position: ct.position, })); } let condition = condition.as_returnable_or_err()?; let (end, body) = self.statement(0, &|c| c == &Category::Semicolon)?; match end { - End::Done(end) => Ok(( - End::Done(end), - Statement::While(token, Box::new(condition), Box::new(body)), + End::Done(end) => Ok(Statement::with_start_end_token( + token, + end.clone(), + StatementKind::While(Box::new(condition), Box::new(body)), )), End::Continue => Err(unclosed_token!(token)), } } - fn parse_repeat(&mut self, token: Token) -> Result<(End, Statement), SyntaxError> { + fn parse_repeat(&mut self, token: Token) -> Result { let (end, body) = self.statement(0, &|c| c == &Category::Semicolon)?; if !end { return Err(unclosed_token!(token)); } - let (until, end): (Statement, Token) = { + let (until, end) = { match self.token() { Some(token) => match token.category() { Category::Identifier(IdentifierType::Until) => { @@ -338,13 +361,14 @@ impl<'a> Lexer<'a> { }? }; let until = until.as_returnable_or_err()?; - Ok(( - End::Done(end), - Statement::Repeat(token, Box::new(body), Box::new(until)), + Ok(Statement::with_start_end_token( + token, + end.clone(), + StatementKind::Repeat(Box::new(body), Box::new(until)), )) } - fn parse_foreach(&mut self, token: Token) -> Result<(End, Statement), SyntaxError> { + fn parse_foreach(&mut self, token: Token) -> Result { let variable: Token = { match self.token() { Some(token) => match token.category() { @@ -365,14 +389,16 @@ impl<'a> Lexer<'a> { }; let (end, block) = self.statement(0, &|cat| cat == &Category::Semicolon)?; match end { - End::Done(end) => Ok(( - End::Done(end), - Statement::ForEach(token, variable, Box::new(r#in), Box::new(block)), + End::Done(end) => Ok(Statement::with_start_end_token( + token, + end, + StatementKind::ForEach(variable, Box::new(r#in), Box::new(block)), )), + End::Continue => Err(unclosed_token!(token)), } } - fn parse_fct_anon_args(&mut self, keyword: Token) -> Result<(End, Statement), SyntaxError> { + fn parse_fct_anon_args(&mut self, keyword: Token) -> Result { match self.peek() { Some(token) => match token.category() { Category::LeftBrace => { @@ -380,14 +406,18 @@ impl<'a> Lexer<'a> { let (end, lookup) = self.statement(0, &|c| c == &Category::RightBrace)?; let lookup = lookup.as_returnable_or_err()?; match end { - End::Done(end) => Ok(( - End::Continue, - Statement::Array(keyword, Some(Box::new(lookup)), Some(end)), + End::Done(end) => Ok(Statement::with_start_end_token( + keyword, + end, + StatementKind::Array(Some(Box::new(lookup))), )), End::Continue => Err(unclosed_token!(token)), } } - _ => Ok((End::Continue, Statement::Array(keyword, None, None))), + _ => Ok(Statement::with_start_token( + keyword, + StatementKind::Array(None), + )), }, None => Err(unexpected_end!("in fct_anon_args")), } @@ -401,26 +431,57 @@ impl<'a> Keywords for Lexer<'a> { token: Token, ) -> Result<(End, Statement), SyntaxError> { match keyword { - IdentifierType::For => self.parse_for(token), - IdentifierType::ForEach => self.parse_foreach(token), - IdentifierType::If => self.parse_if(token), + IdentifierType::For => self + .parse_for(token) + .map(|stmt| (End::Done(stmt.end().clone()), stmt)), + IdentifierType::ForEach => self + .parse_foreach(token) + .map(|stmt| (End::Done(stmt.end().clone()), stmt)), + IdentifierType::If => self + .parse_if(token) + .map(|stmt| (End::Done(stmt.end().clone()), stmt)), IdentifierType::Else => Err(unexpected_token!(token)), // handled in if - IdentifierType::While => self.parse_while(token), - IdentifierType::Repeat => self.parse_repeat(token), + IdentifierType::While => self + .parse_while(token) + .map(|stmt| (End::Done(stmt.end().clone()), stmt)), + IdentifierType::Repeat => self + .parse_repeat(token) + .map(|stmt| (End::Done(stmt.end().clone()), stmt)), IdentifierType::Until => Err(unexpected_token!(token)), // handled in repeat - IdentifierType::LocalVar | IdentifierType::GlobalVar => self.parse_declaration(token), - IdentifierType::Null => Ok((End::Continue, Statement::Primitive(token))), - IdentifierType::Return => self.parse_return(token), - IdentifierType::Include => self.parse_include(token), - IdentifierType::Exit => self.parse_exit(token), - IdentifierType::FCTAnonArgs => self.parse_fct_anon_args(token), - IdentifierType::True => Ok((End::Continue, Statement::Primitive(token))), - IdentifierType::False => Ok((End::Continue, Statement::Primitive(token))), - IdentifierType::Function => self.parse_function(token), - IdentifierType::ACT(_) => Ok((End::Continue, Statement::AttackCategory(token))), + IdentifierType::LocalVar | IdentifierType::GlobalVar => self + .parse_declaration(token) + .map(|stmt| (End::Done(stmt.end().clone()), stmt)), + IdentifierType::Return => self + .parse_return(token) + .map(|stmt| (End::Done(stmt.end().clone()), stmt)), + IdentifierType::Include => self + .parse_include(token) + .map(|stmt| (End::Done(stmt.end().clone()), stmt)), + IdentifierType::Exit => self + .parse_exit(token) + .map(|stmt| (End::Done(stmt.end().clone()), stmt)), + IdentifierType::FCTAnonArgs => self + .parse_fct_anon_args(token) + .map(|stmt| (End::Continue, stmt)), + IdentifierType::Null | IdentifierType::True | IdentifierType::False => Ok(( + End::Continue, + Statement::with_start_token(token, StatementKind::Primitive), + )), + IdentifierType::ACT(_) => Ok(( + End::Continue, + Statement::with_start_token(token, StatementKind::AttackCategory), + )), + IdentifierType::Function => self + .parse_function(token) + .map(|stmt| (End::Done(stmt.end().clone()), stmt)), + IdentifierType::Continue => self + .parse_continue(token) + .map(|stmt| (End::Done(stmt.end().clone()), stmt)), + + IdentifierType::Break => self + .parse_break(token) + .map(|stmt| (End::Done(stmt.end().clone()), stmt)), IdentifierType::Undefined(_) => Err(unexpected_token!(token)), - IdentifierType::Continue => self.parse_continue(token), - IdentifierType::Break => self.parse_break(token), } } } @@ -430,11 +491,11 @@ mod test { use crate::{ parse, - token::{Category, IdentifierType, Token}, + token::{Category, IdentifierType}, Statement, }; - use crate::Statement::*; + use crate::StatementKind::*; use crate::TokenCategory::*; #[test] @@ -443,8 +504,8 @@ mod test { .next() .unwrap() .unwrap(); - match actual { - If(_, _, _, Some(_), Some(_)) => {} + match actual.kind() { + If(_, _, Some(_), Some(_)) => {} _ => unreachable!("{actual} must be if with else stmt."), } @@ -452,8 +513,8 @@ mod test { .next() .unwrap() .unwrap(); - match actual { - If(_, _, _, None, None) => {} + match actual.kind() { + If(_, _, None, None) => {} _ => unreachable!("{actual} must be if without else stmt."), } } @@ -461,10 +522,10 @@ mod test { #[test] fn if_block() { let actual = parse("if (description) { ; }").next().unwrap().unwrap(); - match actual { - If(_, _, b, _, _) => match *b { - Block(_, v, _) => { - assert_eq!(v, vec![]); + match actual.kind() { + If(_, b, _, _) => match b.kind() { + Block(v) => { + assert_eq!(v, &vec![]); } _ => unreachable!("{b} must be a block stmt."), }, @@ -474,9 +535,9 @@ mod test { #[test] fn local_var() { - let expected = |actual: Statement, scope: Category| match actual { - Declare(a, vars) => { - assert_eq!(a.category(), &scope); + let expected = |actual: Statement, scope: Category| match actual.kind() { + Declare(vars) => { + assert_eq!(actual.as_token().category(), &scope); assert_eq!(vars.len(), 3); } _ => unreachable!("{actual} must be an declare stmt."), @@ -493,41 +554,30 @@ mod test { #[test] fn null() { - match parse("NULL;").next().unwrap().unwrap() { - Primitive(Token { - category: Identifier(IdentifierType::Null), - line_column: _, - position: _, - }) => { - // correct - } - actual => unreachable!("{actual} must be a primitive stmt."), - } + let result = parse("NULL;").next().unwrap().unwrap(); + assert_eq!(result.kind(), &Primitive); + assert_eq!( + result.as_token().category(), + &Identifier(IdentifierType::Null) + ); } #[test] fn boolean() { - match parse("TRUE;").next().unwrap().unwrap() { - Primitive(Token { - category: Identifier(IdentifierType::True), - line_column: _, - position: _, - }) => { - // correct - } - actual => unreachable!("{actual} must be a primitive stmt."), - } - match parse("FALSE;").next().unwrap().unwrap() { - Primitive(Token { - category: Identifier(IdentifierType::False), - line_column: _, - position: _, - }) => { - // correct - } - actual => unreachable!("{actual} must be a primitive stmt."), - } + let result = parse("TRUE;").next().unwrap().unwrap(); + assert_eq!(result.kind(), &Primitive); + assert_eq!( + result.as_token().category(), + &Identifier(IdentifierType::True) + ); + let result = parse("FALSE;").next().unwrap().unwrap(); + assert_eq!(result.kind(), &Primitive); + assert_eq!( + result.as_token().category(), + &Identifier(IdentifierType::False) + ); } + #[test] fn exit() { let test_cases = [ @@ -538,14 +588,8 @@ mod test { "exit((4 * 5))", ]; for call in test_cases { - assert!( - matches!( - parse(&format!("{call};")).next().unwrap().unwrap(), - Exit(..), - ), - "{}", - call - ); + let result = parse(&format!("{call};")).next().unwrap().unwrap(); + assert!(matches!(result.kind(), &Exit(..),), "{}", call); } } @@ -559,35 +603,41 @@ mod test { "return (4 * 5)", ]; for call in test_cases { - assert!( - matches!( - parse(&format!("{call};")).next().unwrap().unwrap(), - Return(..), - ), - "{}", - call - ); + let result = parse(&format!("{call};")).next().unwrap().unwrap(); + assert!(matches!(result.kind(), &Return(..),), "{}", call); } } #[test] fn for_loop() { let code = "for (i = 0; i < 10; i++) display('hi');"; - assert!(matches!(parse(code).next().unwrap().unwrap(), For(..))); + assert!(matches!( + parse(code).next().unwrap().unwrap().kind(), + &For(..) + )); let code = "for (i = 0; i < 10; ) i = 10;"; - assert!(matches!(parse(code).next().unwrap().unwrap(), For(..))) + assert!(matches!( + parse(code).next().unwrap().unwrap().kind(), + &For(..) + )) } #[test] fn while_loop() { let code = "while (TRUE) ;"; - assert!(matches!(parse(code).next().unwrap().unwrap(), While(..))) + assert!(matches!( + parse(code).next().unwrap().unwrap().kind(), + &While(..) + )) } #[test] fn repeat_loop() { let code = "repeat ; until 1 == 1;"; - assert!(matches!(parse(code).next().unwrap().unwrap(), Repeat(..))) + assert!(matches!( + parse(code).next().unwrap().unwrap().kind(), + &Repeat(..) + )) } #[test] @@ -599,8 +649,8 @@ mod test { for call in test_cases { assert!( matches!( - parse(&format!("{call};")).next().unwrap().unwrap(), - ForEach(..), + parse(&format!("{call};")).next().unwrap().unwrap().kind(), + &ForEach(..), ), "{}", call @@ -611,8 +661,12 @@ mod test { #[test] fn include() { assert!(matches!( - parse("include('test.inc');").next().unwrap().unwrap(), - Include(..) + parse("include('test.inc');") + .next() + .unwrap() + .unwrap() + .kind(), + &Include(..) )) } @@ -622,44 +676,35 @@ mod test { parse("function register_packages( buf ) { return 1; }") .next() .unwrap() - .unwrap(), - FunctionDeclaration(..) + .unwrap() + .kind(), + &FunctionDeclaration(..) )); assert!(matches!( parse("function register_packages( ) { return 1; }") .next() .unwrap() - .unwrap(), - FunctionDeclaration(..) + .unwrap() + .kind(), + &FunctionDeclaration(..) )); } #[test] fn fct_anon_args() { - match parse("_FCT_ANON_ARGS[0];").next().unwrap().unwrap() { - Array( - Token { - category: Category::Identifier(IdentifierType::FCTAnonArgs), - line_column: _, - position: _, - }, - Some(_), - Some(_), - ) => {} - actual => unreachable!("{actual} must be an array."), - } - match parse("_FCT_ANON_ARGS;").next().unwrap().unwrap() { - Array( - Token { - category: Category::Identifier(IdentifierType::FCTAnonArgs), - line_column: _, - position: _, - }, - None, - None, - ) => {} - actual => unreachable!("{actual} must be an array."), - } + let result = parse("_FCT_ANON_ARGS[0];").next().unwrap().unwrap(); + assert!(matches!(result.kind(), &Array(Some(_)))); + assert_eq!( + result.as_token().category(), + &Identifier(IdentifierType::FCTAnonArgs) + ); + + let result = parse("_FCT_ANON_ARGS;").next().unwrap().unwrap(); + assert!(matches!(result.kind(), &Array(None))); + assert_eq!( + result.as_token().category(), + &Identifier(IdentifierType::FCTAnonArgs) + ); } #[test] diff --git a/rust/nasl-syntax/src/lexer.rs b/rust/nasl-syntax/src/lexer.rs index b111419744..6a2c1655db 100644 --- a/rust/nasl-syntax/src/lexer.rs +++ b/rust/nasl-syntax/src/lexer.rs @@ -13,7 +13,7 @@ use crate::{ postfix_extension::Postfix, prefix_extension::Prefix, token::{Category, Token, Tokenizer}, - unexpected_statement, unexpected_token, Statement, + unexpected_statement, unexpected_token, Statement, StatementKind, }; /// Is used to parse Token to Statement @@ -30,6 +30,7 @@ pub struct Lexer<'a> { #[derive(Clone, Debug, PartialEq, Eq)] pub enum End { + // TODO remove Token from Done as it is in the returned Statement Done(Token), Continue, } @@ -45,12 +46,12 @@ impl End { } } - pub fn category(&self) -> Option { - match self { - End::Done(t) => Some(t.category.clone()), - End::Continue => None, - } - } + // pub fn category(&self) -> &Option { + // match self { + // End::Done(t) => &Some(t.category), + // End::Continue => &None, + // } + // } } impl Not for End { @@ -89,6 +90,38 @@ impl<'a> Lexer<'a> { } None } + + pub(crate) fn parse_comma_group( + &mut self, + category: Category, + ) -> Result<(End, Vec), SyntaxError> { + let mut params = vec![]; + let mut end = End::Continue; + while let Some(token) = self.peek() { + if *token.category() == category { + self.token(); + end = End::Done(token); + break; + } + let (stmtend, param) = + self.statement(0, &|c| c == &category || c == &Category::Comma)?; + match param.kind() { + StatementKind::Parameter(nparams) => params.extend_from_slice(nparams), + _ => params.push(param), + } + match stmtend { + End::Done(endcat) => { + if endcat.category() == &category { + end = End::Done(endcat); + break; + } + } + End::Continue => {} + }; + } + Ok((end, params)) + } + /// Returns the next expression. /// /// It uses a prefix_extension to verify if a token is prefix relevant and if parsing should continue @@ -107,6 +140,14 @@ impl<'a> Lexer<'a> { if self.depth >= MAX_DEPTH { return Err(max_recursion!(MAX_DEPTH)); } + fn done(token: Token, mut left: Statement) -> Result<(End, Statement), SyntaxError> { + left.set_end(token.clone()); + Ok((End::Done(token), left)) + } + + fn cont(left: Statement) -> Result<(End, Statement), SyntaxError> { + Ok((End::Continue, left)) + } // reset unhandled_token when min_bp is 0 let (state, mut left) = self .token() @@ -115,25 +156,27 @@ impl<'a> Lexer<'a> { return Err(unexpected_token!(token)); } if abort(token.category()) { - return Ok((End::Done(token.clone()), Statement::NoOp(Some(token)))); + let result = Statement::with_start_token(token.clone(), StatementKind::NoOp); + return done(token, result); } self.prefix_statement(token, abort) }) - .unwrap_or(Ok((End::Done(Token::unexpected_none()), Statement::EoF)))?; + .unwrap_or(Ok(( + End::Done(Token::unexpected_none()), + Statement::without_token(StatementKind::EoF), + )))?; match state { End::Continue => {} - end => { - return Ok((end, left)); + End::Done(x) => { + return done(x, left); } } - let mut end_statement = End::Continue; while let Some(token) = self.peek() { if abort(token.category()) { self.token(); - end_statement = End::Done(token.clone()); self.depth = 0; - break; + return done(token, left); } let op = Operation::new(token.clone()).ok_or_else(|| unexpected_token!(token.clone()))?; @@ -146,23 +189,22 @@ impl<'a> Lexer<'a> { left = stmt; if let End::Done(cat) = end { self.depth = 0; - end_statement = End::Done(cat); - break; + return done(cat, left); } continue; } - if let Some(min_bp_reached) = self.needs_infix(op.clone(), min_binding_power) { + if let Some(min_bp_reached) = self.needs_infix(&op, min_binding_power) { if !min_bp_reached { - break; + // TODO could be changed to unexpected statement so that it doesn't need to be done in the iterator + return cont(left); } self.token(); let (end, nl) = self.infix_statement(op, token, left, abort)?; left = nl; if let End::Done(cat) = end { self.depth = 0; - end_statement = End::Done(cat); - break; + return done(cat, left); } else { // jump to the next without handling it as an error continue; @@ -172,7 +214,7 @@ impl<'a> Lexer<'a> { return Err(unexpected_token!(token)); } - Ok((end_statement, left)) + Ok((End::Continue, left)) } } @@ -187,15 +229,18 @@ impl<'a> Iterator for Lexer<'a> { } match result { - Ok((_, Statement::EoF)) => None, - Ok((End::Done(_), stmt)) => Some(Ok(stmt)), - Ok((End::Continue, stmt)) => { - if matches!(stmt, Statement::NoOp(_)) { - Some(Ok(stmt)) - } else { + Ok((end, stmt)) => { + if matches!(stmt.kind(), &StatementKind::EoF) { + return None; + } + if matches!(stmt.kind(), &StatementKind::NoOp) { + return Some(Ok(stmt)); + } + match end { + End::Done(_) => Some(Ok(stmt)), // This verifies if a statement was not finished yet; this can happen on assignments // and missing semicolons. - Some(Err(unexpected_statement!(stmt))) + End::Continue => Some(Err(unexpected_statement!(stmt))), } } Err(x) => Some(Err(x)), diff --git a/rust/nasl-syntax/src/lib.rs b/rust/nasl-syntax/src/lib.rs index f0565f8877..78a4389469 100644 --- a/rust/nasl-syntax/src/lib.rs +++ b/rust/nasl-syntax/src/lib.rs @@ -53,7 +53,6 @@ mod tests { use crate::{ cursor::Cursor, token::{Category, IdentifierType, Token, Tokenizer}, - AssignOrder, Statement, SyntaxError, }; #[test] @@ -101,42 +100,13 @@ mod tests { #[test] fn use_parser() { - use Category::*; - use Statement::*; - let statements = - super::parse("a = 23;b = 1;").collect::>>(); - assert_eq!( - statements, - vec![ - Ok(Assign( - Equal, - AssignOrder::AssignReturn, - Box::new(Variable(Token { - category: Identifier(IdentifierType::Undefined("a".to_owned())), - line_column: (1, 1), - position: (0, 1), - },)), - Box::new(Primitive(Token { - category: Number(23), - line_column: (1, 5), - position: (4, 6), - })) - )), - Ok(Assign( - Equal, - AssignOrder::AssignReturn, - Box::new(Variable(Token { - category: Identifier(IdentifierType::Undefined("b".to_owned())), - line_column: (1, 8), - position: (7, 8), - },)), - Box::new(Primitive(Token { - category: Number(1), - line_column: (1, 12), - position: (11, 12), - })) - )) - ] - ); + let code = "a = 23;b = 1;"; + let expected = ["a = 23;", "b = 1;"]; + for (i, s) in super::parse(code).enumerate() { + let stmt = s.unwrap(); + dbg!(stmt.kind()); + //assert!(matches!(stmt.kind(), Assign(..))); + assert_eq!(&code[stmt.range()], expected[i]); + } } } diff --git a/rust/nasl-syntax/src/postfix_extension.rs b/rust/nasl-syntax/src/postfix_extension.rs index 1031c4afae..846ca609f8 100644 --- a/rust/nasl-syntax/src/postfix_extension.rs +++ b/rust/nasl-syntax/src/postfix_extension.rs @@ -8,7 +8,7 @@ use crate::{ lexer::{End, Lexer}, operation::Operation, token::{Category, Token}, - unexpected_token, AssignOrder, Statement, + unexpected_token, AssignOrder, Statement, StatementKind, }; /// Is a trait to handle postfix statements. @@ -32,23 +32,18 @@ impl<'a> Lexer<'a> { token: Token, assign: Category, ) -> Option> { - match lhs { - Statement::Variable(token) => Some(Ok(( + match lhs.kind() { + StatementKind::Variable | StatementKind::Array(..) => Some(Ok(( End::Continue, - Statement::Assign( - assign, - AssignOrder::ReturnAssign, - Box::new(Statement::Variable(token)), - Box::new(Statement::NoOp(None)), - ), - ))), - Statement::Array(token, resolver, end) => Some(Ok(( - End::Continue, - Statement::Assign( - assign, - AssignOrder::ReturnAssign, - Box::new(Statement::Array(token, resolver, end)), - Box::new(Statement::NoOp(None)), + Statement::with_start_end_token( + lhs.end().clone(), + token, + StatementKind::Assign( + assign, + AssignOrder::ReturnAssign, + Box::new(lhs), + Box::new(Statement::without_token(StatementKind::NoOp)), + ), ), ))), _ => Some(Err(unexpected_token!(token))), @@ -86,14 +81,8 @@ impl<'a> Postfix for Lexer<'a> { #[cfg(test)] mod test { - use crate::{ - parse, - token::{Category, Token}, - AssignOrder, Statement, - }; + use crate::{parse, token::Category, AssignOrder, Statement, StatementKind}; - use crate::IdentifierType::Undefined; - use crate::Statement::*; use Category::*; fn result(code: &str) -> Statement { @@ -102,70 +91,15 @@ mod test { #[test] fn variable_assignment_operator() { - let expected = |assign_operator: Category| { - Operator( - Plus, - vec![ - Primitive(Token { - category: Number(1), - line_column: (1, 1), - position: (0, 1), - }), - Operator( - Star, - vec![ - Assign( - assign_operator, - AssignOrder::ReturnAssign, - Box::new(Variable(Token { - category: Identifier(Undefined("a".to_owned())), - line_column: (1, 5), - position: (4, 5), - })), - Box::new(NoOp(None)), - ), - Primitive(Token { - category: Number(1), - line_column: (1, 11), - position: (10, 11), - }), - ], - ), - ], - ) - }; - assert_eq!(result("1 + a++ * 1;"), expected(PlusPlus)); - assert_eq!(result("1 + a-- * 1;"), expected(MinusMinus)); - } - - #[test] - fn array_assignment_operator() { - use AssignOrder::*; - let expected = |assign_operator: Category| { - Assign( - assign_operator, - ReturnAssign, - Box::new(Array( - Token { - category: Identifier(Undefined("a".to_owned())), - line_column: (1, 1), - position: (0, 1), - }, - Some(Box::new(Primitive(Token { - category: Number(1), - line_column: (1, 3), - position: (2, 3), - }))), - Some(Token { - category: RightBrace, - line_column: (1, 4), - position: (3, 4), - }), - )), - Box::new(NoOp(None)), - ) + let expected = |stmt: Statement, assign_operator: Category| match stmt.kind() { + StatementKind::Assign(operator, AssignOrder::ReturnAssign, _, _) => { + assert_eq!(operator, &assign_operator) + } + kind => panic!("expected Assign, but got: {:?}", kind), }; - assert_eq!(result("a[1]++;"), expected(PlusPlus)); - assert_eq!(result("a[1]--;"), expected(MinusMinus)); + expected(result("a++;"), PlusPlus); + expected(result("a--;"), MinusMinus); + expected(result("a[1]++;"), PlusPlus); + expected(result("a[1]--;"), MinusMinus); } } diff --git a/rust/nasl-syntax/src/prefix_extension.rs b/rust/nasl-syntax/src/prefix_extension.rs index e3b1807f43..bc4a6f2b07 100644 --- a/rust/nasl-syntax/src/prefix_extension.rs +++ b/rust/nasl-syntax/src/prefix_extension.rs @@ -10,9 +10,7 @@ use crate::{ lexer::{End, Lexer}, operation::Operation, token::{Category, Token}, - unexpected_end, unexpected_token, - variable_extension::Variables, - {AssignOrder, Statement}, + unclosed_token, unexpected_end, unexpected_token, Statement, {AssignOrder, StatementKind}, }; pub(crate) trait Prefix { /// Handles statements before operation statements get handled. @@ -26,14 +24,60 @@ pub(crate) trait Prefix { } /// Is used to verify operations. -fn prefix_binding_power(token: Token) -> Result { +fn prefix_binding_power(token: &Token) -> Result { match token.category() { Category::Plus | Category::Minus | Category::Tilde | Category::Bang => Ok(21), - _ => Err(unexpected_token!(token)), + _ => Err(unexpected_token!(token.clone())), } } impl<'a> Lexer<'a> { + fn parse_variable(&mut self, token: Token) -> Result<(End, Statement), SyntaxError> { + if !matches!( + token.category(), + Category::Identifier(crate::IdentifierType::Undefined(_)) + ) { + return Err(unexpected_token!(token)); + } + use End::*; + let (kind, end) = { + if let Some(nt) = self.peek() { + match nt.category() { + Category::LeftParen => { + self.token(); + let (end, params) = self.parse_comma_group(Category::RightParen)?; + match end { + Done(end) => { + let params = Statement::with_start_end_token( + nt, + end.clone(), + StatementKind::Parameter(params), + ); + Ok((StatementKind::Call(Box::new(params)), end)) + } + Continue => Err(unclosed_token!(nt)), + } + } + Category::LeftBrace => { + self.token(); + let (end, lookup) = self.statement(0, &|c| c == &Category::RightBrace)?; + let lookup = lookup.as_returnable_or_err()?; + match end { + Done(end) => Ok((StatementKind::Array(Some(Box::new(lookup))), end)), + Continue => Err(unclosed_token!(token.clone())), + } + } + _ => Ok((StatementKind::Variable, token.clone())), + } + } else { + Ok((StatementKind::Variable, token.clone())) + } + }?; + let stmt = Statement::with_start_end_token(token, end, kind); + + Ok((Continue, stmt)) + } + /// Parses Operations that have an prefix (e.g. -1) fn parse_prefix_assign_operator( &mut self, @@ -43,21 +87,23 @@ impl<'a> Lexer<'a> { let next = self .token() .ok_or_else(|| unexpected_end!("parsing prefix statement"))?; - match self.parse_variable(next)? { - (_, Statement::Variable(value)) => Ok(Statement::Assign( - assign, - AssignOrder::AssignReturn, - Box::new(Statement::Variable(value)), - Box::new(Statement::NoOp(None)), - )), - (_, Statement::Array(token, resolver, end)) => Ok(Statement::Assign( + let (_, stmt) = self.parse_variable(next)?; + if !matches!( + stmt.kind(), + StatementKind::Variable | StatementKind::Array(..) + ) { + return Err(unexpected_token!(token)); + } + Ok(Statement::with_start_end_token( + token.clone(), + stmt.end().clone(), + StatementKind::Assign( assign, AssignOrder::AssignReturn, - Box::new(Statement::Array(token, resolver, end)), - Box::new(Statement::NoOp(None)), - )), - _ => Err(unexpected_token!(token)), - } + Box::new(stmt), + Box::new(Statement::without_token(StatementKind::NoOp)), + ), + )) } } @@ -71,11 +117,19 @@ impl<'a> Prefix for Lexer<'a> { let op = Operation::new(token.clone()).ok_or_else(|| unexpected_token!(token.clone()))?; match op { Operation::Operator(kind) => { - let bp = prefix_binding_power(token)?; + let bp = prefix_binding_power(&token)?; let (end, right) = self.statement(bp, abort)?; - Ok((end, Statement::Operator(kind, vec![right]))) + let stmt = Statement::with_start_end_token( + token, + right.end().clone(), + StatementKind::Operator(kind, vec![right]), + ); + Ok((end, stmt)) } - Operation::Primitive => Ok((Continue, Statement::Primitive(token))), + Operation::Primitive => Ok(( + Continue, + Statement::with_start_token(token, StatementKind::Primitive), + )), Operation::Variable => self.parse_variable(token), Operation::Grouping(_) => self.parse_grouping(token), Operation::Assign(Category::MinusMinus) => self @@ -86,7 +140,10 @@ impl<'a> Prefix for Lexer<'a> { .map(|stmt| (Continue, stmt)), Operation::Assign(_) => Err(unexpected_token!(token)), Operation::Keyword(keyword) => self.parse_keyword(keyword, token), - Operation::NoOp => Ok((Done(token.clone()), Statement::NoOp(Some(token)))), + Operation::NoOp => Ok(( + Done(token.clone()), + Statement::with_start_token(token, StatementKind::NoOp), + )), } } } @@ -97,12 +154,11 @@ mod test { use crate::{ parse, token::{Category, Token}, - AssignOrder, Statement, + AssignOrder, Statement, StatementKind, }; - use crate::IdentifierType::Undefined; use Category::*; - use Statement::*; + use StatementKind::*; fn result(code: &str) -> Statement { parse(code).next().unwrap().unwrap() @@ -110,19 +166,15 @@ mod test { #[test] fn operations() { - let no = Token { - category: Number(1), - line_column: (1, 2), - position: (1, 2), - }; - let expected = |category: Category| -> Statement { - Statement::Operator(category, vec![Statement::Primitive(no.clone())]) + let expected = |stmt: Statement, category: Category| match stmt.kind() { + StatementKind::Operator(cat, _) => assert_eq!(cat, &category), + kind => panic!("expected Operator, but got: {:?}", kind), }; - assert_eq!(result("-1;"), expected(Category::Minus)); - assert_eq!(result("+1;"), expected(Category::Plus)); - assert_eq!(result("~1;"), expected(Category::Tilde)); - assert_eq!(result("!1;"), expected(Category::Bang)); + expected(result("-1;"), Category::Minus); + expected(result("+1;"), Category::Plus); + expected(result("~1;"), Category::Tilde); + expected(result("!1;"), Category::Bang); } #[test] @@ -137,76 +189,25 @@ mod test { line_column: (1, 1), position: (0, 3), }; - - assert_eq!(result("1;"), Primitive(no)); - assert_eq!(result("'a';"), Primitive(data)); + let one = result("1;"); + assert_eq!(one.kind(), &Primitive); + assert_eq!(one.start(), &no); + let second = result("'a';"); + assert_eq!(second.kind(), &Primitive); + assert_eq!(second.start(), &data); } #[test] fn assignment_operator() { - let expected = |assign_operator: Category| { - Operator( - Plus, - vec![ - Primitive(Token { - category: Number(1), - line_column: (1, 1), - position: (0, 1), - }), - Operator( - Star, - vec![ - Assign( - assign_operator, - AssignOrder::AssignReturn, - Box::new(Variable(Token { - category: Identifier(Undefined("a".to_owned())), - line_column: (1, 7), - position: (6, 7), - })), - Box::new(NoOp(None)), - ), - Primitive(Token { - category: Number(1), - line_column: (1, 11), - position: (10, 11), - }), - ], - ), - ], - ) - }; - assert_eq!(result("1 + ++a * 1;"), expected(PlusPlus)); - assert_eq!(result("1 + --a * 1;"), expected(MinusMinus)); - } - #[test] - fn assignment_array_operator() { - use AssignOrder::*; - let expected = |assign_operator: Category| { - Assign( - assign_operator, - AssignReturn, - Box::new(Array( - Token { - category: Identifier(Undefined("a".to_owned())), - line_column: (1, 3), - position: (2, 3), - }, - Some(Box::new(Primitive(Token { - category: Number(0), - line_column: (1, 5), - position: (4, 5), - }))), - Some(Token { - category: RightBrace, - line_column: (1, 6), - position: (5, 6), - }), - )), - Box::new(NoOp(None)), - ) + let expected = |stmt: Statement, assign_operator: Category| match stmt.kind() { + StatementKind::Assign(operator, AssignOrder::AssignReturn, _, _) => { + assert_eq!(operator, &assign_operator) + } + kind => panic!("expected Assign, but got: {:?}", kind), }; - assert_eq!(result("++a[0];"), expected(PlusPlus)); - assert_eq!(result("--a[0];"), expected(MinusMinus)); + expected(result("++a;"), Category::PlusPlus); + expected(result("--a;"), Category::MinusMinus); + expected(result("++a[0];"), Category::PlusPlus); + expected(result("--a[0];"), Category::MinusMinus); } } diff --git a/rust/nasl-syntax/src/statement.rs b/rust/nasl-syntax/src/statement.rs index 69668be601..ae1ee5eae7 100644 --- a/rust/nasl-syntax/src/statement.rs +++ b/rust/nasl-syntax/src/statement.rs @@ -18,44 +18,39 @@ pub enum AssignOrder { /// Is a executable step. #[derive(Clone, Debug, PartialEq, Eq)] -// TODO: change from enum to struct that contains a Kind. This would allow us to redefine Statement -// to contain start an end token, may comments so that a future formatter can just depend on -// Statement rather than have to reimplement logic -pub enum Statement { +pub enum StatementKind { /// Either a Number, String, Boolean or Null - Primitive(Token), + Primitive, /// Attack category set by script_category - AttackCategory(Token), + AttackCategory, /// Is a variable - Variable(Token), + Variable, /// Is a array variable, it contains the lookup token as well as an optional lookup statement - Array(Token, Option>, Option), + Array(Option>), /// Is a call of a function - // TODO: change to Box and use Parameter - Call(Token, Vec, Token), + Call(Box), /// Special exit call - Exit(Token, Box, Token), + Exit(Box), /// Special Return statement - Return(Token, Box), + Return(Box), /// Special Break statement - Break(Token), + Break, /// Special Continue statement - Continue(Token), + Continue, /// Special include call - Include(Token, Box, Token), + Include(Box), /// Declares a new variable in either global or local scope - Declare(Token, Vec), + Declare(Vec), /// Parameter within a function Parameter(Vec), /// Named parameter on a function - NamedParameter(Token, Box), + NamedParameter(Box), /// Assignment to a variable Assign(TokenCategory, AssignOrder, Box, Box), /// An Operator (e.g. +, -, *) Operator(TokenCategory, Vec), /// If statement, containing a condition, expression to be executed when the condition is true and an optional else expression If( - Token, Box, Box, Option, @@ -64,100 +59,54 @@ pub enum Statement { /// For statement, containing a declaration/assignment, a condition, a execution per round before body execution, body execution /// e.g. `for (i = 0; i < 10; i++) display("hi");` For( - Token, Box, Box, Box, Box, ), /// While statement, containing a condition and a block - While(Token, Box, Box), + While(Box, Box), /// repeat statement, containing a block and a condition - Repeat(Token, Box, Box), + Repeat(Box, Box), /// foreach statement, containing a variable in array and a block - ForEach(Token, Token, Box, Box), + ForEach(Token, Box, Box), /// A set of expression within { ... } - Block(Token, Vec, Token), + Block(Vec), /// Function declaration; contains an identifier token, parameter statement and a block statement - // TODO: change to Box as Parameter statement for statements instead of - // Vec - FunctionDeclaration(Token, Token, Vec, Token, Box), + // The third token can be deleted as it is end statement end token + FunctionDeclaration(Token, Box, Box), /// An empty operation, e.g. ; - NoOp(Option), + NoOp, /// End of File EoF, } +#[derive(Clone, Debug, PartialEq, Eq)] +/// Is the definition of a Statement +/// +/// start returns a token of the beginning of that statement while end contains +/// the end of the statement. So as an example of the statement: +/// 'my_function(1);' start will point to 'my_function' and end to ';'. +pub struct Statement { + kind: StatementKind, + start: Token, + end: Option, +} impl Statement { - /// Returns true when Statement may returns something + /// Returns the StatementKind. /// - /// Since nasl is a dynamic, typeless language there is no guarantee. - /// In uncertain things like a function it returns true. - pub fn is_returnable(&self) -> bool { - matches!( - self, - Statement::Primitive(_) - | Statement::Variable(_) - | Statement::Call(_, _, _) - | Statement::Return(_, _) - | Statement::Assign( - _, - AssignOrder::AssignReturn | AssignOrder::ReturnAssign, - _, - _ - ) - | Statement::Array(_, _, _) - | Statement::Operator(_, _) - ) - } - - /// Returns Self when it is returnable otherwise a unexpected statement error - pub fn as_returnable_or_err(self) -> Result { - if self.is_returnable() { - Ok(self) - } else { - Err(unexpected_statement!(self)) - } - } - - fn first_stmts_token(stmts: &[Statement]) -> Option<&Token> { - match stmts.first() { - Some(stmt) => stmt.as_token(), - None => None, - } + /// A StatementKind is used for execution and contains all necessary data + /// for an interpreter to execute. + pub fn kind(&self) -> &StatementKind { + &self.kind } /// Retrieves the stored token in a Statement. /// /// If a Statement contains multiple Statements (e.g. Declare) than just the first one is returned. /// Returns None on EoF, when a slice of vectors is empty or on AttackCategory - pub fn as_token(&self) -> Option<&Token> { - match self { - Statement::Continue(token) - | Statement::Break(token) - | Statement::AttackCategory(token) - | Statement::Primitive(token) => Some(token), - Statement::Variable(token) => Some(token), - Statement::Array(token, _, _) => Some(token), - Statement::Call(token, _, _) => Some(token), - Statement::Exit(_, stmt, _) => stmt.as_token(), - Statement::Return(_, stmt) => stmt.as_token(), - Statement::Include(_, stmt, _) => stmt.as_token(), - Statement::Declare(_, stmts) => Statement::first_stmts_token(stmts), - Statement::Parameter(stmts) => Statement::first_stmts_token(stmts), - Statement::NamedParameter(token, _) => Some(token), - Statement::Assign(_, _, stmt, _) => stmt.as_token(), - Statement::Operator(_, stmts) => Statement::first_stmts_token(stmts), - Statement::FunctionDeclaration(kw, _, _, _, _) - | Statement::Block(kw, _, _) - | Statement::If(kw, _, _, _, _) - | Statement::While(kw, _, _) - | Statement::Repeat(kw, _, _) - | Statement::ForEach(kw, _, _, _) - | Statement::For(kw, _, _, _, _) => Some(kw), - Statement::NoOp(token) => token.as_ref(), - Statement::EoF => None, - } + pub fn as_token(&self) -> &Token { + &self.start } /// Retrieves the stored token in a Statement. @@ -165,125 +114,119 @@ impl Statement { /// If a Statement contains multiple Statements (e.g. Declare) than just the first one is returned. /// Returns None on EoF, when a slice of vectors is empty or on AttackCategory pub fn as_tokens(&self) -> Vec<&Token> { - match self { - Statement::AttackCategory(token) - | Statement::Continue(token) - | Statement::Break(token) - | Statement::NoOp(Some(token)) - | Statement::Array(token, None, _) - | Statement::Primitive(token) - | Statement::Variable(token) => vec![token], - Statement::Array(token, Some(stmt), end) => { - let mut results = vec![token]; - results.extend(stmt.as_tokens()); - if let Some(end) = end { - results.push(end) - } - results - } - Statement::Block(kw, stmts, end) | Statement::Call(kw, stmts, end) => { - let mut results = Vec::with_capacity(stmts.len() + 2); - results.push(kw); - for stmt in stmts { - results.extend(stmt.as_tokens()); - } - results.push(end); - results - } - Statement::Include(kw, stmt, end) | Statement::Exit(kw, stmt, end) => { - let mut results = Vec::with_capacity(3); - results.push(kw); - results.extend(stmt.as_tokens()); - results.push(end); - results + let mut results = vec![&self.start]; + match self.kind() { + StatementKind::Primitive + | StatementKind::AttackCategory + | StatementKind::Variable + | StatementKind::NoOp + | StatementKind::Break + | StatementKind::Continue + | StatementKind::Array(None) + | StatementKind::EoF => { + // doesn't contain further statements } - Statement::NamedParameter(kw, stmt) | Statement::Return(kw, stmt) => { - let mut results = Vec::with_capacity(2); - results.push(kw); - results.extend(stmt.as_tokens()); - results + StatementKind::NamedParameter(x) + | StatementKind::Exit(x) + | StatementKind::Return(x) + | StatementKind::Include(x) + | StatementKind::Call(x) + | StatementKind::Array(Some(x)) => { + results.extend(x.as_tokens()); } - Statement::Declare(kw, stmts) => { - let mut results = Vec::with_capacity(2); - results.push(kw); - for stmt in stmts { + StatementKind::Block(x) + | StatementKind::Operator(_, x) + | StatementKind::Parameter(x) + | StatementKind::Declare(x) => { + for stmt in x { results.extend(stmt.as_tokens()); } - results - } - Statement::Parameter(stmts) => stmts.iter().flat_map(|stmt| stmt.as_tokens()).collect(), - Statement::Assign(_, _, stmt1, stmt2) => { - let mut tokens = stmt1.as_tokens(); - tokens.extend(stmt2.as_tokens()); - tokens } - Statement::Operator(_, stmts) => { - let mut results = Vec::with_capacity(stmts.len()); - for stmt in stmts { - results.extend(stmt.as_tokens()); - } - results + StatementKind::While(x, y) + | StatementKind::Repeat(x, y) + | StatementKind::Assign(_, _, x, y) => { + results.extend(x.as_tokens()); + results.extend(y.as_tokens()); } - Statement::If(kw, cond, stmt, ekw, estmt) => { - let mut results = vec![kw]; - results.extend(cond.as_tokens()); - results.extend(stmt.as_tokens()); - if let Some(ekw) = ekw { - results.push(ekw); + StatementKind::If(r, x, y, z) => { + results.extend(r.as_tokens()); + results.extend(x.as_tokens()); + if let Some(y) = y { + results.push(y); } - if let Some(estmt) = estmt { - results.extend(estmt.as_tokens()); + if let Some(z) = z { + results.extend(z.as_tokens()); } - results - } - Statement::For(kw, decl, cond, post, stmt) => { - let mut results = vec![kw]; - results.extend(decl.as_tokens()); - results.extend(cond.as_tokens()); - results.extend(post.as_tokens()); - results.extend(stmt.as_tokens()); - results } - Statement::Repeat(kw, cond, stmt) | Statement::While(kw, cond, stmt) => { - let mut results = vec![kw]; - results.extend(cond.as_tokens()); - results.extend(stmt.as_tokens()); - results + StatementKind::For(r, x, y, z) => { + results.extend(r.as_tokens()); + results.extend(x.as_tokens()); + results.extend(y.as_tokens()); + results.extend(z.as_tokens()); } - Statement::ForEach(kw, token, arr, stmt) => { - let mut results = vec![kw, token]; - results.extend(arr.as_tokens()); - results.extend(stmt.as_tokens()); - results + StatementKind::ForEach(x, y, z) => { + results.push(x); + results.extend(y.as_tokens()); + results.extend(z.as_tokens()); } - Statement::FunctionDeclaration(kw, name, params, rp, stmt) => { - let mut results = vec![kw, name]; - for stmt in params { - results.extend(stmt.as_tokens()); - } - results.push(rp); - results.extend(stmt.as_tokens()); - results + StatementKind::FunctionDeclaration(x, y, w) => { + results.push(x); + results.extend(y.as_tokens()); + results.extend(w.as_tokens()); } - Statement::EoF | Statement::NoOp(None) => vec![], + }; + if let Some(t) = self.end.as_ref() { + results.push(t); + } + results + } + + /// Returns the end token + pub fn end(&self) -> &Token { + self.end.as_ref().unwrap_or(&self.start) + } + + /// Returns children of blocks and calls. + pub fn children(&self) -> &[Statement] { + match self.kind() { + StatementKind::If(..) + | StatementKind::For(..) + | StatementKind::ForEach(..) + | StatementKind::While(..) + | StatementKind::Repeat(..) + | StatementKind::Assign(..) + | StatementKind::NamedParameter(_) + | StatementKind::Exit(_) + | StatementKind::Return(_) + | StatementKind::Include(_) + | StatementKind::Array(_) + | StatementKind::Primitive + | StatementKind::AttackCategory + | StatementKind::Variable + | StatementKind::NoOp + | StatementKind::Break + | StatementKind::Continue + | StatementKind::EoF => &[], + + // contains Parameter + StatementKind::Call(x) | StatementKind::FunctionDeclaration(_, x, _) => x.children(), + + StatementKind::Block(x) + | StatementKind::Operator(_, x) + | StatementKind::Parameter(x) + | StatementKind::Declare(x) => x, } } /// Calculates the position of the statement pub fn position(&self) -> (usize, usize) { - match self { - Statement::Array(id, _, Some(end)) | Statement::Call(id, _, end) => { - (id.position.0, end.position.1) - } - _ => { - let tokens = self.as_tokens(); - if let (Some(t1), Some(t2)) = (tokens.first(), tokens.last()) { - (t1.position.0, t2.position.1) - } else { - (0, 0) - } - } - } + ( + self.start.position.0, + self.end + .as_ref() + .map(|x| x.position.1) + .unwrap_or_else(|| self.start.position.1), + ) } /// Calculates the byte range of the statement @@ -312,7 +255,7 @@ impl Statement { /// "#; /// let results: usize = nasl_syntax::parse(code) /// .filter_map(|s| s.ok()) - /// .map(|s| s.find(&|s| matches!(s, nasl_syntax::Statement::Call(..))).len()) + /// .map(|s| s.find(&|s| matches!(s.kind(), nasl_syntax::StatementKind::Call(..))).len()) /// .sum(); /// /// assert_eq!(results, 10); @@ -327,66 +270,117 @@ impl Statement { vec![self] } else { let mut results = vec![]; - match self { - Statement::Primitive(_) - | Statement::AttackCategory(_) - | Statement::Variable(_) - | Statement::NoOp(_) - | Statement::EoF - | Statement::Break(_) - | Statement::Array(_, None, _) - | Statement::Continue(_) => { + match self.kind() { + StatementKind::Primitive + | StatementKind::AttackCategory + | StatementKind::Variable + | StatementKind::NoOp + | StatementKind::Break + | StatementKind::Continue + | StatementKind::Array(None) + | StatementKind::EoF => { // doesn't contain further statements } - Statement::Parameter(stmts) - | Statement::Call(_, stmts, _) - | Statement::Declare(_, stmts) - | Statement::Operator(_, stmts) - | Statement::Block(_, stmts, _) => { - for s in stmts { - results.extend(Self::find(s, wanted)) - } + StatementKind::NamedParameter(x) + | StatementKind::Exit(x) + | StatementKind::Return(x) + | StatementKind::Include(x) + | StatementKind::Call(x) + | StatementKind::Array(Some(x)) => { + results.extend(Self::find(x, wanted)); } - Statement::NamedParameter(_, stmt) - | Statement::Exit(_, stmt, _) - | Statement::Return(_, stmt) - | Statement::Include(_, stmt, _) - | Statement::Array(_, Some(stmt), _) => { - results.extend(Self::find(stmt, wanted)); + StatementKind::Block(x) + | StatementKind::Operator(_, x) + | StatementKind::Parameter(x) + | StatementKind::Declare(x) => { + for stmt in x { + results.extend(Self::find(stmt, wanted)); + } } - Statement::While(_, stmt, stmt2) - | Statement::Repeat(_, stmt, stmt2) - | Statement::ForEach(_, _, stmt, stmt2) - | Statement::Assign(_, _, stmt, stmt2) => { - results.extend(Self::find(stmt, wanted)); - results.extend(Self::find(stmt2, wanted)); + StatementKind::While(x, y) + | StatementKind::Repeat(x, y) + | StatementKind::Assign(_, _, x, y) => { + results.extend(Self::find(x, wanted)); + results.extend(Self::find(y, wanted)); } - Statement::If(_, stmt, stmt2, _, stmt3) => { - results.extend(Self::find(stmt, wanted)); - results.extend(Self::find(stmt2, wanted)); - if let Some(stmt3) = stmt3 { - results.extend(Self::find(stmt3, wanted)); + StatementKind::If(r, x, _, z) => { + results.extend(Self::find(r, wanted)); + results.extend(Self::find(x, wanted)); + + if let Some(z) = z { + results.extend(Self::find(z, wanted)); } } - Statement::For(_, stmt, stmt2, stmt3, stmt4) => { - results.extend(Self::find(stmt, wanted)); - results.extend(Self::find(stmt2, wanted)); - results.extend(Self::find(stmt3, wanted)); - results.extend(Self::find(stmt4, wanted)); + StatementKind::For(r, x, y, z) => { + results.extend(Self::find(r, wanted)); + results.extend(Self::find(x, wanted)); + results.extend(Self::find(y, wanted)); + results.extend(Self::find(z, wanted)); } - Statement::FunctionDeclaration(_, _, stmts, _, stmt) => { - results.extend(Self::find(stmt, wanted)); - for stmt in stmts { - results.extend(Self::find(stmt, wanted)); - } + StatementKind::ForEach(_, y, z) => { + results.extend(Self::find(y, wanted)); + results.extend(Self::find(z, wanted)); + } + StatementKind::FunctionDeclaration(_, y, w) => { + results.extend(Self::find(y, wanted)); + results.extend(Self::find(w, wanted)); } }; + results } } + + /// Returns the initial token of a Statement + pub fn start(&self) -> &Token { + &self.start + } + + /// Returns self if it is a returnable or an SyntaxError otherwise + pub fn as_returnable_or_err(self) -> Result { + if self.kind().is_returnable() { + Ok(self) + } else { + Err(unexpected_statement!(self)) + } + } + + /// Creates a statement with the same start and end token + pub fn with_start_token(token: Token, kind: StatementKind) -> Self { + Self { + kind, + start: token, + end: None, + } + } + + /// Creates a statement with the start and end token + pub fn with_start_end_token(start: Token, end: Token, kind: StatementKind) -> Self { + Self { + kind, + start, + end: Some(end), + } + } + + /// Creates a Statement without a token. + /// + /// This should only be used when artificially creating a Statement without + /// code relevance e.g. expanding i++ to 'return i and then add 1 to i'. + pub fn without_token(kind: StatementKind) -> Self { + Self { + kind, + start: Token::default(), + end: None, + } + } + + pub(crate) fn set_end(&mut self, cat: Token) { + self.end = Some(cat) + } } -impl fmt::Display for Statement { +impl std::fmt::Display for Statement { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let as_str_list = |v: &[Statement]| { v.iter() @@ -394,38 +388,39 @@ impl fmt::Display for Statement { .reduce(|a, b| format!("{a}, {b}")) .unwrap_or_default() }; - match self { - Statement::Primitive(x) => write!(f, "{}", x.category()), - Statement::AttackCategory(x) => write!(f, "{x:?}"), - Statement::Variable(x) => write!(f, "{}", x.category()), - Statement::Array(x, e, _) => match e { + let x = self.start(); + match self.kind() { + StatementKind::Primitive => write!(f, "{}", x.category()), + StatementKind::AttackCategory => write!(f, "{x:?}"), + StatementKind::Variable => write!(f, "{}", x.category()), + StatementKind::Array(e) => match e { Some(e) => { write!(f, "{}[{e}]", x.category()) } None => write!(f, "{}", x.category()), }, - Statement::Call(name, args, _) => { - write!(f, "{}({});", name.category(), as_str_list(args)) + StatementKind::Call(args) => { + write!(f, "{}{};", x.category(), args) } - Statement::Exit(_, x, _) => write!(f, "exit({x});"), - Statement::Return(_, x) => write!(f, "return {x};"), - Statement::Include(_, x, _) => write!(f, "include({x});"), - Statement::Declare(s, x) => { - write!(f, "{s} {}", as_str_list(x),) + StatementKind::Exit(x) => write!(f, "exit({x});"), + StatementKind::Return(x) => write!(f, "return {x};"), + StatementKind::Include(x) => write!(f, "include({x});"), + StatementKind::Declare(y) => { + write!(f, "{x} {}", as_str_list(y),) } - Statement::Parameter(x) => write!(f, "({})", as_str_list(x),), - Statement::NamedParameter(n, s) => write!(f, "{}: {s}", n.category()), - Statement::Assign(c, o, l, r) => match (o, &**r) { - (AssignOrder::AssignReturn, Statement::NoOp(_)) => write!(f, "{c}{l}"), - (AssignOrder::ReturnAssign, Statement::NoOp(_)) => write!(f, "{l}{c}"), + StatementKind::Parameter(x) => write!(f, "({})", as_str_list(x),), + StatementKind::NamedParameter(s) => write!(f, "{}: {s}", x.category()), + StatementKind::Assign(c, o, l, r) => match (o, r.kind().clone()) { + (AssignOrder::AssignReturn, StatementKind::NoOp) => write!(f, "{c}{l}"), + (AssignOrder::ReturnAssign, StatementKind::NoOp) => write!(f, "{l}{c}"), _ => write!(f, "{l} {c} {r}"), }, - Statement::Operator(o, args) => match &args[..] { + StatementKind::Operator(o, args) => match &args[..] { [l, r] => write!(f, "{l} {o} {r}"), [l] => write!(f, "{o}{l}"), _ => write!(f, "({o} ({}))", as_str_list(args)), }, - Statement::If(_, c, x, _, e) => { + StatementKind::If(c, x, _, e) => { let r = write!(f, "if ({c}) {x}"); if let Some(e) = e { write!(f, " else {e}") @@ -433,22 +428,48 @@ impl fmt::Display for Statement { r } } - Statement::For(_, i, c, u, e) => write!(f, "for ({i}; {c}; {u}) {{ {e} }}"), - Statement::While(_, c, e) => write!(f, "while ({c}) {{{e}}}"), - Statement::Repeat(_, e, c) => write!(f, "repeat {e} until {c}"), - Statement::ForEach(_, v, a, e) => write!(f, "foreach {}({a}) {{{e}}}", v.category()), - Statement::Block(..) => write!(f, "{{ ... }}"), - Statement::FunctionDeclaration(_, n, p, _, _) => { - write!(f, "function {}({}) {{ ... }}", n.category(), as_str_list(p)) + StatementKind::For(i, c, u, e) => write!(f, "for ({i}; {c}; {u}) {{ {e} }}"), + StatementKind::While(c, e) => write!(f, "while ({c}) {{{e}}}"), + StatementKind::Repeat(e, c) => write!(f, "repeat {e} until {c}"), + StatementKind::ForEach(v, a, e) => { + write!(f, "foreach {}({a}) {{{e}}}", v.category()) } - Statement::NoOp(_) => write!(f, "NoOp"), - Statement::EoF => write!(f, "EoF"), - Statement::Break(_) => write!(f, "break"), - Statement::Continue(_) => write!(f, "continue"), + StatementKind::Block(..) => write!(f, "{{ ... }}"), + StatementKind::FunctionDeclaration(n, p, _) => { + write!(f, "function {}({}) {{ ... }}", n.category(), p) + } + StatementKind::NoOp => write!(f, "NoOp"), + StatementKind::EoF => write!(f, "EoF"), + StatementKind::Break => write!(f, "break"), + StatementKind::Continue => write!(f, "continue"), } } } +impl StatementKind { + /// Returns true when Statement may returns something + /// + /// Since nasl is a dynamic, typeless language there is no guarantee. + /// In uncertain things like a function it returns true. + pub fn is_returnable(&self) -> bool { + matches!( + self, + StatementKind::Primitive + | StatementKind::Variable + | StatementKind::Call(..) + | StatementKind::Return(..) + | StatementKind::Assign( + _, + AssignOrder::AssignReturn | AssignOrder::ReturnAssign, + _, + _ + ) + | StatementKind::Array(..) + | StatementKind::Operator(..) + ) + } +} + #[cfg(test)] mod position { use crate::parse; @@ -479,34 +500,38 @@ mod position { "#; let parser = parse(code); let expected = [ - "a = 1 + 1", - "b = 2 * 2", - "a = ++a", - "arr = mkarray(a, b, c )", - "arr[++a]", - "exit(1)", - "return 1", - "include('test.inc')", - "local_var a, b, c", - "global_var a, b, c", - "if (a) display(1); else display(2)", - "for (i = 1; i < 10; i++) display(i)", - "while(TRUE) display(i)", - "foreach a(q) display(a)", - "repeat display(\"q\"); until 1", + "a = 1 + 1;", + "b = 2 * 2;", + "a = ++a;", + "arr = mkarray(a, b, c );", + "arr[++a];", + "exit(1);", + "return 1;", + "include('test.inc');", + "local_var a, b, c;", + "global_var a, b, c;", + "if (a) display(1); else display(2);", + "for (i = 1; i < 10; i++) display(i);", + "while(TRUE) display(i);", + "foreach a(q) display(a);", + "repeat display(\"q\"); until 1;", r#"{ a; b; }"#, "function register_packages( buf ) { return 1; }", ]; - let ranges: Vec<_> = parser.map(|x| x.unwrap().range()).collect(); + let mut tests = 0; let mut ri = expected.iter(); - assert_eq!(ranges.len(), expected.len()); - for range in ranges { + for stmt in parser { + let stmt = stmt.unwrap(); let a: &str = ri.next().unwrap(); + let range = stmt.range(); + tests += 1; assert_eq!(&code[range], a); } + + assert_eq!(tests, expected.len()); } } diff --git a/rust/nasl-syntax/src/token.rs b/rust/nasl-syntax/src/token.rs index 020577f6e1..5acf804e78 100644 --- a/rust/nasl-syntax/src/token.rs +++ b/rust/nasl-syntax/src/token.rs @@ -406,6 +406,16 @@ pub struct Token { pub position: (usize, usize), } +impl Default for Token { + fn default() -> Self { + Token { + category: Category::UnknownSymbol, + line_column: (0, 0), + position: (0, 0), + } + } +} + impl Token { /// Returns UnknownSymbol without line column or position pub fn unexpected_none() -> Self { diff --git a/rust/nasl-syntax/src/variable_extension.rs b/rust/nasl-syntax/src/variable_extension.rs index 2580f85f32..4cb2fd9089 100644 --- a/rust/nasl-syntax/src/variable_extension.rs +++ b/rust/nasl-syntax/src/variable_extension.rs @@ -5,15 +5,9 @@ use crate::{ error::SyntaxError, lexer::{End, Lexer}, - token::{Category, Token}, - unclosed_token, unexpected_token, Statement, + token::{Category}, Statement, StatementKind, }; -pub(crate) trait Variables { - /// Parses variables, function calls. - fn parse_variable(&mut self, token: Token) -> Result<(End, Statement), SyntaxError>; -} - pub(crate) trait CommaGroup { fn parse_comma_group( &mut self, @@ -36,9 +30,9 @@ impl<'a> CommaGroup for Lexer<'a> { } let (stmtend, param) = self.statement(0, &|c| c == &category || c == &Category::Comma)?; - match param { - Statement::Parameter(nparams) => params.extend_from_slice(&nparams), - param => params.push(param), + match param.kind() { + StatementKind::Parameter(nparams) => params.extend_from_slice(nparams), + _ => params.push(param), } match stmtend { End::Done(endcat) => { @@ -56,52 +50,13 @@ impl<'a> CommaGroup for Lexer<'a> { } } -impl<'a> Variables for Lexer<'a> { - fn parse_variable(&mut self, token: Token) -> Result<(End, Statement), SyntaxError> { - if !matches!( - token.category(), - Category::Identifier(crate::IdentifierType::Undefined(_)) - ) { - return Err(unexpected_token!(token)); - } - use End::*; - - if let Some(nt) = self.peek() { - match nt.category() { - Category::LeftParen => { - self.token(); - let (end, params) = self.parse_comma_group(Category::RightParen)?; - return match end { - Done(end) => Ok((Continue, Statement::Call(token, params, end))), - Continue => Err(unclosed_token!(nt)), - }; - } - Category::LeftBrace => { - self.token(); - let (end, lookup) = self.statement(0, &|c| c == &Category::RightBrace)?; - let lookup = lookup.as_returnable_or_err()?; - return match end { - Done(end) => Ok(( - Continue, - Statement::Array(token, Some(Box::new(lookup)), Some(end)), - )), - Continue => Err(unclosed_token!(token)), - }; - } - _ => {} - } - } - Ok((Continue, Statement::Variable(token))) - } -} - #[cfg(test)] mod test { use crate::{ - parse, {AssignOrder, Statement}, + parse, Statement, {AssignOrder, StatementKind}, }; - use Statement::*; + use StatementKind::*; fn result(code: &str) -> Statement { parse(code).next().unwrap().unwrap() @@ -109,37 +64,39 @@ mod test { #[test] fn variables() { - assert!(matches!(result("a;"), Variable(_))); + assert_eq!(result("a;").kind(), &StatementKind::Variable) } #[test] fn arrays() { - assert!(matches!(result("a[0];"), Array(..))); - match result("a = [1, 2, 3];") { + assert!(matches!(result("a[0];").kind(), Array(Some(_)))); + let re = result("a = [1, 2, 3];"); + match re.kind() { Assign(super::Category::Equal, AssignOrder::AssignReturn, arr, _) => { - assert!(matches!(*arr, Array(..))) + assert!(matches!(arr.kind(), Array(None))) } - actual => unreachable!("{actual} must be an assign statement"), + _ => panic!("{re} must be an assign statement"), } - match result("a[0] = [1, 2, 4];") { + let re = result("a[0] = [1, 2, 4];"); + match re.kind() { Assign(super::Category::Equal, AssignOrder::AssignReturn, arr, _) => { - assert!(matches!(*arr, Array(..))) + assert!(matches!(arr.kind(), &Array(Some(_)))) } - actual => unreachable!("{actual} must be an assign statement"), + _ => panic!("{re} must be an assign statement"), } } #[test] fn anon_function_call() { - assert!(matches!(result("a(1, 2, 3);"), Call(..))) + assert!(matches!(result("a(1, 2, 3);").kind(), &Call(..))) } #[test] fn named_function_call() { assert!(matches!( - result("script_tag(name:\"cvss_base\", value:1 + 1 % 2);"), - Call(..) + result("script_tag(name:\"cvss_base\", value:1 + 1 % 2);").kind(), + &Call(..) )); } }