From a88d1a144938b45a2f569bedacf69c3efc90c873 Mon Sep 17 00:00:00 2001 From: Philipp Eder Date: Wed, 22 Nov 2023 11:14:43 +0100 Subject: [PATCH] Change: overhaul Statement Redefines Statement from an enum containing all information to a struct. This Statement struct contains a start and end token as well as a `StatementKind`. The kind contains all necessary information for execution. The Statement also contains information about start and end to make it also useful when working with code manipulation e.g. transpiling. This refactoring allows us to extend Statement information, for e.g. a formatter, while not having to change the interpreter itself. Overall this should give us more freedom to extend Statements. --- rust/feed/src/oid/mod.rs | 13 +- rust/feed/src/transpile/mod.rs | 294 +++++---- .../tests/helper/mod.rs | 3 +- rust/nasl-cli/src/error.rs | 2 +- rust/nasl-interpreter/src/assign.rs | 15 +- rust/nasl-interpreter/src/call.rs | 12 +- rust/nasl-interpreter/src/declare.rs | 12 +- rust/nasl-interpreter/src/error.rs | 2 +- rust/nasl-interpreter/src/include.rs | 34 +- rust/nasl-interpreter/src/interpreter.rs | 73 ++- rust/nasl-syntax/src/error.rs | 16 +- rust/nasl-syntax/src/grouping_extension.rs | 100 +-- rust/nasl-syntax/src/infix_extension.rs | 342 +++++----- rust/nasl-syntax/src/keyword_extension.rs | 481 +++++++------- rust/nasl-syntax/src/lexer.rs | 101 ++- rust/nasl-syntax/src/lib.rs | 46 +- rust/nasl-syntax/src/postfix_extension.rs | 110 +--- rust/nasl-syntax/src/prefix_extension.rs | 205 +++--- rust/nasl-syntax/src/statement.rs | 595 +++++++++--------- rust/nasl-syntax/src/token.rs | 10 + rust/nasl-syntax/src/variable_extension.rs | 81 +-- 21 files changed, 1269 insertions(+), 1278 deletions(-) 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(..) )); } }