diff --git a/src/syntax/ast.rs b/src/syntax/ast.rs index 01b3a90..fc78da6 100644 --- a/src/syntax/ast.rs +++ b/src/syntax/ast.rs @@ -232,6 +232,10 @@ impl TypeRestrictions { restrictions: vec![], } } + + pub fn is_empty(&self) -> bool { + self.restrictions.is_empty() + } } #[derive(Debug)] diff --git a/src/syntax/parse.rs b/src/syntax/parse.rs index b0527ea..d7cfc06 100644 --- a/src/syntax/parse.rs +++ b/src/syntax/parse.rs @@ -261,6 +261,15 @@ impl<'lexer> Parser<'lexer> { } } + #[allow(unused)] + fn print_next_token(&mut self, comment: &str) { + let token = self.next().expect("can get token"); + println!("[{comment}] next token will be {:?}", token.as_ref().map(|x| x.token.clone())); + if let Some(token) = token { + self.save(token); + } + } + /// Parse a definition in a file (structure, enumeration, value, etc.). /// /// This will read a definition. If there's an error, it's very likely the @@ -394,6 +403,7 @@ impl<'lexer> Parser<'lexer> { let next = self .next()? .ok_or_else(|| self.bad_eof("looking for definition body"))?; + self.save(next.clone()); if let Ok(structure) = self.parse_structure() { return Ok(Def::Structure(structure)); diff --git a/src/syntax/parser_tests.rs b/src/syntax/parser_tests.rs index fd7c577..05c3e0a 100644 --- a/src/syntax/parser_tests.rs +++ b/src/syntax/parser_tests.rs @@ -1001,3 +1001,86 @@ fn patterns() { type_name.as_printed() == "Enumeration" && variant_name.as_printed() == "Value")))); } + +#[test] +fn definitions() { + let parse_def = |str| { + let lexer = Lexer::from(str); + let mut result = Parser::new("test", lexer); + result.parse_definition() + }; + + assert!(matches!( + parse_def("x = 1;"), + Ok(Definition { export: ExportClass::Private, type_restrictions, definition, .. }) if + type_restrictions.is_empty() && + matches!(&definition, Def::Value(ValueDef { name, mtype: None, value, .. }) if + name.as_printed() == "x" && + matches!(value, Expression::Value(ConstantValue::Integer(_, iwb)) if + iwb.value == 1)))); + assert!(parse_def("x = 1").is_err()); + assert!(matches!( + parse_def("x: Integer = 1;"), + Ok(Definition { export: ExportClass::Private, type_restrictions, definition, .. }) if + type_restrictions.is_empty() && + matches!(&definition, Def::Value(ValueDef { name, mtype: Some(_), value, .. }) if + name.as_printed() == "x" && + matches!(value, Expression::Value(ConstantValue::Integer(_, iwb)) if + iwb.value == 1)))); + assert!(matches!( + parse_def("export x: Integer = 1;"), + Ok(Definition { export: ExportClass::Public, type_restrictions, definition, .. }) if + type_restrictions.is_empty() && + matches!(&definition, Def::Value(ValueDef { name, mtype: Some(_), value, .. }) if + name.as_printed() == "x" && + matches!(value, Expression::Value(ConstantValue::Integer(_, iwb)) if + iwb.value == 1)))); + assert!(matches!( + parse_def("export restrict() x: Integer = 1;"), + Ok(Definition { export: ExportClass::Public, type_restrictions, definition, .. }) if + type_restrictions.is_empty() && + matches!(&definition, Def::Value(ValueDef { name, mtype: Some(_), value, .. }) if + name.as_printed() == "x" && + matches!(value, Expression::Value(ConstantValue::Integer(_, iwb)) if + iwb.value == 1)))); + assert!(matches!( + parse_def("export restrict(Numeric a) x: a = 1;"), + Ok(Definition { export: ExportClass::Public, type_restrictions, definition, .. }) if + matches!(&type_restrictions, TypeRestrictions { restrictions } if + restrictions.len() == 1 && + matches!(restrictions.first(), Some(TypeRestriction{ constructor, arguments }) if + matches!(constructor, Type::Constructor(_, n) if n.as_printed() == "Numeric") && + matches!(arguments.as_slice(), [Type::Variable(_, n)] if n.as_printed() == "a"))) && + matches!(&definition, Def::Value(ValueDef { name, mtype: Some(t), value, .. }) if + name.as_printed() == "x" && + matches!(t, Type::Variable(_, n) if n.as_printed() == "a") && + matches!(value, Expression::Value(ConstantValue::Integer(_, iwb)) if + iwb.value == 1)))); + assert!(matches!( + parse_def("function() { }"), + Ok(Definition { export: ExportClass::Private, type_restrictions, definition, .. }) if + type_restrictions.is_empty() && + matches!(&definition, Def::Function(FunctionDef { name, arguments, return_type, body, .. }) if + name.as_printed() == "function" && + arguments.is_empty() && + return_type.is_none() && + body.len() == 1))); + assert!(matches!( + parse_def("function() { 1 }"), + Ok(Definition { export: ExportClass::Private, type_restrictions, definition, .. }) if + type_restrictions.is_empty() && + matches!(&definition, Def::Function(FunctionDef { name, arguments, return_type, body, .. }) if + name.as_printed() == "function" && + arguments.is_empty() && + return_type.is_none() && + body.len() == 1))); + assert!(matches!( + parse_def("function(): Integer { 1 }"), + Ok(Definition { export: ExportClass::Private, type_restrictions, definition, .. }) if + type_restrictions.is_empty() && + matches!(&definition, Def::Function(FunctionDef { name, arguments, return_type, body, .. }) if + name.as_printed() == "function" && + arguments.is_empty() && + return_type.is_some() && + body.len() == 1))); +}