From b1d516b268264631b98976e07e4e447f34388490 Mon Sep 17 00:00:00 2001 From: Deukhoofd Date: Thu, 7 Apr 2022 20:00:29 +0200 Subject: [PATCH] Adds parsing for class, and for funcdef --- .../empty_class_declaration.rs | 93 +++++++++ src/integration_tests/mod.rs | 2 + .../multiple_inheritance_class.rs | 173 ++++++++++++++++ .../empty_class_declaration/lex_tokens.json | 53 +++++ .../empty_class_declaration/parsed_tree.json | 14 ++ .../empty_class_declaration/script.ses | 1 + .../lex_tokens.json | 129 ++++++++++++ .../parsed_tree.json | 18 ++ .../multiple_inheritance_class/script.ses | 1 + src/modifiers.rs | 1 + src/parsing/parser/mod.rs | 196 +++++++++++++++--- src/parsing/parser/parsed_statement.rs | 13 ++ 12 files changed, 662 insertions(+), 32 deletions(-) create mode 100644 src/integration_tests/empty_class_declaration.rs create mode 100644 src/integration_tests/multiple_inheritance_class.rs create mode 100644 src/integration_tests/test_cases/empty_class_declaration/lex_tokens.json create mode 100644 src/integration_tests/test_cases/empty_class_declaration/parsed_tree.json create mode 100644 src/integration_tests/test_cases/empty_class_declaration/script.ses create mode 100644 src/integration_tests/test_cases/multiple_inheritance_class/lex_tokens.json create mode 100644 src/integration_tests/test_cases/multiple_inheritance_class/parsed_tree.json create mode 100644 src/integration_tests/test_cases/multiple_inheritance_class/script.ses diff --git a/src/integration_tests/empty_class_declaration.rs b/src/integration_tests/empty_class_declaration.rs new file mode 100644 index 0000000..17f0f28 --- /dev/null +++ b/src/integration_tests/empty_class_declaration.rs @@ -0,0 +1,93 @@ +//////////////////////////// +// Automatically Generated// +//////////////////////////// +use crate::logger::messages::Message; +use crate::parsing::lexer::lex; +use crate::parsing::lexer::lex_tokens::LexToken; +use crate::parsing::parser::parse; +use crate::parsing::parser::parsed_statement::ParsedStatement; +use crate::span::Span; + +fn panic_on_error(msg: Message, _: Span) { + std::panic::panic_any(msg.stringify()); +} +#[test] +fn integration_empty_class_declaration() { + let script = "class Foo {}"; + let lexed_tokens = lex(script, &mut panic_on_error); + println!("{}", serde_json::to_string(&lexed_tokens).unwrap()); + let expected_tokens: Vec = + serde_json::from_str(r#"[ + { + "token_type": "ClassKeyword", + "span": { + "start": 0, + "end": 5 + } + }, + { + "token_type": "WhiteSpace", + "span": { + "start": 5, + "end": 6 + } + }, + { + "token_type": { + "Identifier": "Foo" + }, + "span": { + "start": 6, + "end": 9 + } + }, + { + "token_type": "WhiteSpace", + "span": { + "start": 9, + "end": 10 + } + }, + { + "token_type": "OpenCurlyBracket", + "span": { + "start": 10, + "end": 11 + } + }, + { + "token_type": "CloseCurlyBracket", + "span": { + "start": 11, + "end": 12 + } + }, + { + "token_type": "EndOfFile", + "span": { + "start": 12, + "end": 12 + } + } +]"#).unwrap(); + assert_eq!(lexed_tokens, expected_tokens); + + let parsed_tree = parse(lexed_tokens, &mut panic_on_error); + println!("{}", serde_json::to_string(&parsed_tree).unwrap()); + let expected_tree: Box = + serde_json::from_str(r#"{ + "Script": { + "statements": [ + { + "ClassDeclaration": { + "modifiers": 0, + "name": "Foo", + "inherits": [], + "statements": [] + } + } + ] + } +}"#).unwrap(); + assert_eq!(parsed_tree, expected_tree); +} \ No newline at end of file diff --git a/src/integration_tests/mod.rs b/src/integration_tests/mod.rs index 486bf46..c36cc4f 100644 --- a/src/integration_tests/mod.rs +++ b/src/integration_tests/mod.rs @@ -1,2 +1,4 @@ mod enum_definition; +mod multiple_inheritance_class; +mod empty_class_declaration; mod add_function; diff --git a/src/integration_tests/multiple_inheritance_class.rs b/src/integration_tests/multiple_inheritance_class.rs new file mode 100644 index 0000000..4a8bbc2 --- /dev/null +++ b/src/integration_tests/multiple_inheritance_class.rs @@ -0,0 +1,173 @@ +//////////////////////////// +// Automatically Generated// +//////////////////////////// +use crate::logger::messages::Message; +use crate::parsing::lexer::lex; +use crate::parsing::lexer::lex_tokens::LexToken; +use crate::parsing::parser::parse; +use crate::parsing::parser::parsed_statement::ParsedStatement; +use crate::span::Span; + +fn panic_on_error(msg: Message, _: Span) { + std::panic::panic_any(msg.stringify()); +} +#[test] +fn integration_multiple_inheritance_class() { + let script = "class Foo : Zom, Aar, Bar {}"; + let lexed_tokens = lex(script, &mut panic_on_error); + println!("{}", serde_json::to_string(&lexed_tokens).unwrap()); + let expected_tokens: Vec = + serde_json::from_str(r#"[ + { + "token_type": "ClassKeyword", + "span": { + "start": 0, + "end": 5 + } + }, + { + "token_type": "WhiteSpace", + "span": { + "start": 5, + "end": 6 + } + }, + { + "token_type": { + "Identifier": "Foo" + }, + "span": { + "start": 6, + "end": 9 + } + }, + { + "token_type": "WhiteSpace", + "span": { + "start": 9, + "end": 10 + } + }, + { + "token_type": "Colon", + "span": { + "start": 10, + "end": 11 + } + }, + { + "token_type": "WhiteSpace", + "span": { + "start": 11, + "end": 12 + } + }, + { + "token_type": { + "Identifier": "Zom" + }, + "span": { + "start": 12, + "end": 15 + } + }, + { + "token_type": "Comma", + "span": { + "start": 15, + "end": 16 + } + }, + { + "token_type": "WhiteSpace", + "span": { + "start": 16, + "end": 17 + } + }, + { + "token_type": { + "Identifier": "Aar" + }, + "span": { + "start": 17, + "end": 20 + } + }, + { + "token_type": "Comma", + "span": { + "start": 20, + "end": 21 + } + }, + { + "token_type": "WhiteSpace", + "span": { + "start": 21, + "end": 22 + } + }, + { + "token_type": { + "Identifier": "Bar" + }, + "span": { + "start": 22, + "end": 25 + } + }, + { + "token_type": "WhiteSpace", + "span": { + "start": 25, + "end": 26 + } + }, + { + "token_type": "OpenCurlyBracket", + "span": { + "start": 26, + "end": 27 + } + }, + { + "token_type": "CloseCurlyBracket", + "span": { + "start": 27, + "end": 28 + } + }, + { + "token_type": "EndOfFile", + "span": { + "start": 28, + "end": 28 + } + } +]"#).unwrap(); + assert_eq!(lexed_tokens, expected_tokens); + + let parsed_tree = parse(lexed_tokens, &mut panic_on_error); + println!("{}", serde_json::to_string(&parsed_tree).unwrap()); + let expected_tree: Box = + serde_json::from_str(r#"{ + "Script": { + "statements": [ + { + "ClassDeclaration": { + "modifiers": 0, + "name": "Foo", + "inherits": [ + "Zom", + "Aar", + "Bar" + ], + "statements": [] + } + } + ] + } +}"#).unwrap(); + assert_eq!(parsed_tree, expected_tree); +} \ No newline at end of file diff --git a/src/integration_tests/test_cases/empty_class_declaration/lex_tokens.json b/src/integration_tests/test_cases/empty_class_declaration/lex_tokens.json new file mode 100644 index 0000000..bcdff43 --- /dev/null +++ b/src/integration_tests/test_cases/empty_class_declaration/lex_tokens.json @@ -0,0 +1,53 @@ +[ + { + "token_type": "ClassKeyword", + "span": { + "start": 0, + "end": 5 + } + }, + { + "token_type": "WhiteSpace", + "span": { + "start": 5, + "end": 6 + } + }, + { + "token_type": { + "Identifier": "Foo" + }, + "span": { + "start": 6, + "end": 9 + } + }, + { + "token_type": "WhiteSpace", + "span": { + "start": 9, + "end": 10 + } + }, + { + "token_type": "OpenCurlyBracket", + "span": { + "start": 10, + "end": 11 + } + }, + { + "token_type": "CloseCurlyBracket", + "span": { + "start": 11, + "end": 12 + } + }, + { + "token_type": "EndOfFile", + "span": { + "start": 12, + "end": 12 + } + } +] \ No newline at end of file diff --git a/src/integration_tests/test_cases/empty_class_declaration/parsed_tree.json b/src/integration_tests/test_cases/empty_class_declaration/parsed_tree.json new file mode 100644 index 0000000..411e062 --- /dev/null +++ b/src/integration_tests/test_cases/empty_class_declaration/parsed_tree.json @@ -0,0 +1,14 @@ +{ + "Script": { + "statements": [ + { + "ClassDeclaration": { + "modifiers": 0, + "name": "Foo", + "inherits": [], + "statements": [] + } + } + ] + } +} \ No newline at end of file diff --git a/src/integration_tests/test_cases/empty_class_declaration/script.ses b/src/integration_tests/test_cases/empty_class_declaration/script.ses new file mode 100644 index 0000000..f72abf3 --- /dev/null +++ b/src/integration_tests/test_cases/empty_class_declaration/script.ses @@ -0,0 +1 @@ +class Foo {} \ No newline at end of file diff --git a/src/integration_tests/test_cases/multiple_inheritance_class/lex_tokens.json b/src/integration_tests/test_cases/multiple_inheritance_class/lex_tokens.json new file mode 100644 index 0000000..8d6b9e3 --- /dev/null +++ b/src/integration_tests/test_cases/multiple_inheritance_class/lex_tokens.json @@ -0,0 +1,129 @@ +[ + { + "token_type": "ClassKeyword", + "span": { + "start": 0, + "end": 5 + } + }, + { + "token_type": "WhiteSpace", + "span": { + "start": 5, + "end": 6 + } + }, + { + "token_type": { + "Identifier": "Foo" + }, + "span": { + "start": 6, + "end": 9 + } + }, + { + "token_type": "WhiteSpace", + "span": { + "start": 9, + "end": 10 + } + }, + { + "token_type": "Colon", + "span": { + "start": 10, + "end": 11 + } + }, + { + "token_type": "WhiteSpace", + "span": { + "start": 11, + "end": 12 + } + }, + { + "token_type": { + "Identifier": "Zom" + }, + "span": { + "start": 12, + "end": 15 + } + }, + { + "token_type": "Comma", + "span": { + "start": 15, + "end": 16 + } + }, + { + "token_type": "WhiteSpace", + "span": { + "start": 16, + "end": 17 + } + }, + { + "token_type": { + "Identifier": "Aar" + }, + "span": { + "start": 17, + "end": 20 + } + }, + { + "token_type": "Comma", + "span": { + "start": 20, + "end": 21 + } + }, + { + "token_type": "WhiteSpace", + "span": { + "start": 21, + "end": 22 + } + }, + { + "token_type": { + "Identifier": "Bar" + }, + "span": { + "start": 22, + "end": 25 + } + }, + { + "token_type": "WhiteSpace", + "span": { + "start": 25, + "end": 26 + } + }, + { + "token_type": "OpenCurlyBracket", + "span": { + "start": 26, + "end": 27 + } + }, + { + "token_type": "CloseCurlyBracket", + "span": { + "start": 27, + "end": 28 + } + }, + { + "token_type": "EndOfFile", + "span": { + "start": 28, + "end": 28 + } + } +] \ No newline at end of file diff --git a/src/integration_tests/test_cases/multiple_inheritance_class/parsed_tree.json b/src/integration_tests/test_cases/multiple_inheritance_class/parsed_tree.json new file mode 100644 index 0000000..bd83aec --- /dev/null +++ b/src/integration_tests/test_cases/multiple_inheritance_class/parsed_tree.json @@ -0,0 +1,18 @@ +{ + "Script": { + "statements": [ + { + "ClassDeclaration": { + "modifiers": 0, + "name": "Foo", + "inherits": [ + "Zom", + "Aar", + "Bar" + ], + "statements": [] + } + } + ] + } +} \ No newline at end of file diff --git a/src/integration_tests/test_cases/multiple_inheritance_class/script.ses b/src/integration_tests/test_cases/multiple_inheritance_class/script.ses new file mode 100644 index 0000000..c00e240 --- /dev/null +++ b/src/integration_tests/test_cases/multiple_inheritance_class/script.ses @@ -0,0 +1 @@ +class Foo : Zom, Aar, Bar {} \ No newline at end of file diff --git a/src/modifiers.rs b/src/modifiers.rs index 170aee5..37ee768 100644 --- a/src/modifiers.rs +++ b/src/modifiers.rs @@ -11,6 +11,7 @@ pub enum TypeModifier { Shared = 0x02, Abstract = 0x04, Final = 0x08, + Mixin = 0x10, } #[bitflags] diff --git a/src/parsing/parser/mod.rs b/src/parsing/parser/mod.rs index 7c3e8ee..14fd6f1 100644 --- a/src/parsing/parser/mod.rs +++ b/src/parsing/parser/mod.rs @@ -163,16 +163,15 @@ fn parse_script( vec.push(*parse_namespace(reader, log)); } TokenType::InterfaceKeyword => vec.push(*parse_interface(reader, log).unwrap()), + TokenType::EnumKeyword => vec.push(*parse_enum(reader, log).unwrap()), TokenType::EndOfFile => break, TokenType::CloseCurlyBracket => break, _ => { if let Some(s) = parse_enum(reader, log) { vec.push(*s); - } - // else if let Some(s) = parse_class(reader, log) { - // vec.push(*s); - // } - else if let Some(s) = parse_interface(reader, log) { + } else if let Some(s) = parse_class(reader, log) { + vec.push(*s); + } else if let Some(s) = parse_interface(reader, log) { vec.push(*s); } else if let Some(s) = parse_virtprop(reader, log) { vec.push(*s); @@ -219,6 +218,8 @@ fn parse_interface( outer_reader: &mut ParseReader, log: &mut dyn FnMut(Message, Span), ) -> Option> { + // interface ::= {'external' | 'shared'} 'interface' identifier (';' | ([':' identifier {',' identifier}] '{' {virtprop | interfacemethod} '}')); + let mut type_mod: BitFlags = BitFlags::empty(); let identifier: Option; let mut has_interface_keyword = false; @@ -304,14 +305,16 @@ fn parse_interface( } } reader.consume(TokenType::OpenCurlyBracket, log); - // TODO: parse interfacemethod loop { if reader.peek().token_type == TokenType::CloseCurlyBracket { break; } - let prop = parse_virtprop(&mut reader, log); + let mut prop = parse_virtprop(&mut reader, log); if prop.is_none() { - break; + prop = parse_interface_method(&mut reader, log); + if prop.is_none() { + break; + } } statements.push(*prop.unwrap()); } @@ -337,27 +340,22 @@ fn parse_interface_method( // interfacemethod ::= type ['&'] identifier paramlist ['const'] ';'; let mut reader = outer_reader.create_inner(); let return_type = parse_type(&mut reader, log); - if return_type.is_none() { - return None; - } + return_type.as_ref()?; let returns_reference = reader.peek().token_type == TokenType::Ampersand; if returns_reference { reader.next(); } let identifier = parse_identifier(&mut reader, log, true); - if identifier.is_none() { - return None; - } + identifier.as_ref()?; let param_list = parse_paramlist(&mut reader, log); - if param_list.is_none() { - return None; - } + param_list.as_ref()?; let is_const = reader.peek().token_type == TokenType::ConstKeyword; if is_const { reader.next(); } reader.consume(TokenType::Semicolon, log); + outer_reader.set_from_inner(&reader); Some(Box::new(ParsedStatement::InterfaceMethod { return_type: return_type.unwrap(), returns_reference, @@ -379,16 +377,14 @@ fn parse_typedef( let mut left_type = parse_primtype(reader, log); if left_type.is_none() { let left_type_identifier = parse_identifier(reader, log, false); - if left_type_identifier.is_some() { + if let Some(s) = left_type_identifier { left_type = Some(Box::new(ParsedStatement::DataTypeIdentifier { - identifier: left_type_identifier.unwrap(), + identifier: s, })); } } let right_type_identifier = parse_identifier(reader, log, false); - if right_type_identifier.is_none() { - return None; - } + right_type_identifier.as_ref()?; let right_type = Box::new(ParsedStatement::DataTypeIdentifier { identifier: right_type_identifier.unwrap(), }); @@ -531,20 +527,156 @@ fn parse_enum( } fn parse_class( - _outer_reader: &mut ParseReader, - _log: &mut dyn FnMut(Message, Span), + reader: &mut ParseReader, + log: &mut dyn FnMut(Message, Span), ) -> Option> { // class ::= {'shared' | 'abstract' | 'final' | 'external' | 'mixin'} 'class' identifier // (';' | ([':' identifier {',' identifier}] '{' {virtprop | func | var | funcdef | class} '}')); - unimplemented!(); + let mut inner_reader = reader.create_inner(); + let mut modifiers: BitFlags = BitFlags::empty(); + loop { + match inner_reader.peek().token_type { + TokenType::SharedKeyword => { + inner_reader.next(); + modifiers |= TypeModifier::Shared; + } + TokenType::AbstractKeyword => { + inner_reader.next(); + modifiers |= TypeModifier::Abstract; + } + TokenType::FinalKeyword => { + inner_reader.next(); + modifiers |= TypeModifier::Final; + } + TokenType::ExternalKeyword => { + inner_reader.next(); + modifiers |= TypeModifier::External; + } + TokenType::MixinKeyword => { + inner_reader.next(); + modifiers |= TypeModifier::Mixin; + } + _ => { + break; + } + } + } + if inner_reader.peek().token_type != TokenType::ClassKeyword { + return None; + } + inner_reader.next(); + reader.set_from_inner(&inner_reader); + + let name = parse_identifier(reader, log, false); + + // (';' | + // ( + // [':' identifier {',' identifier}] + // '{' {virtprop | func | var | funcdef | class} '}' + // ) + // ) + + // FIXME: Do we need predeclaring class (class foo;)? + + let mut inherits = Vec::new(); + + if reader.peek().token_type == TokenType::Colon { + reader.next(); + loop { + let inherit = parse_identifier(reader, log, false); + if let Some(s) = inherit { + inherits.push(s); + } + if reader.peek().token_type != TokenType::Comma { + break; + } + reader.next(); + } + } + + reader.consume(TokenType::OpenCurlyBracket, log); + let mut statements = Vec::new(); + + loop { + let mut statement = parse_virtprop(reader, log); + if statement.is_none() { + statement = parse_func(reader, log); + if statement.is_none() { + statement = parse_var(reader, log); + if statement.is_none() { + statement = parse_funcdef(reader, log); + if statement.is_none() { + statement = parse_class(reader, log); + } + } + } + } + if let Some(s) = statement { + statements.push(s); + } else { + break; + } + } + reader.consume(TokenType::CloseCurlyBracket, log); + + Some(Box::new(ParsedStatement::ClassDeclaration { + modifiers, + name: name.unwrap(), + inherits, + statements, + })) } fn parse_funcdef( - _outer_reader: &mut ParseReader, - _log: &mut dyn FnMut(Message, Span), + outer_reader: &mut ParseReader, + log: &mut dyn FnMut(Message, Span), ) -> Option> { // funcdef ::= {'external' | 'shared'} 'funcdef' type ['&'] identifier paramlist ';' - unimplemented!(); + let mut reader = outer_reader.create_inner(); + let mut modifiers: BitFlags = BitFlags::empty(); + loop { + match reader.peek().token_type { + TokenType::SharedKeyword => { + reader.next(); + modifiers |= TypeModifier::Shared; + } + TokenType::ExternalKeyword => { + reader.next(); + modifiers |= TypeModifier::External; + } + _ => { + break; + } + } + } + if reader.peek().token_type != TokenType::FuncDefKeyword { + return None; + } + reader.next(); + let return_type = parse_type(&mut reader, log); + return_type.as_ref()?; + let returns_reference = reader.peek().token_type == TokenType::Ampersand; + if returns_reference { + reader.next(); + } + let identifier = parse_identifier(&mut reader, log, true); + identifier.as_ref()?; + let param_list = parse_paramlist(&mut reader, log); + param_list.as_ref()?; + if reader.peek().token_type != TokenType::Semicolon { + return None; + } + reader.next(); + + outer_reader.set_from_inner(&reader); + + Some(Box::new(ParsedStatement::FuncDefDeclaration { + modifiers, + returns_reference, + return_type, + name: identifier.unwrap(), + param_list: param_list.unwrap(), + })) } fn parse_func( @@ -788,7 +920,7 @@ fn parse_paramlist( fn parse_funcattr( reader: &mut ParseReader, - log: &mut dyn FnMut(Message, Span), + _log: &mut dyn FnMut(Message, Span), ) -> BitFlags { // funcattr ::= {'override' | 'final' | 'explicit' | 'property'}; let mut func_attr: BitFlags = BitFlags::empty(); @@ -1067,8 +1199,8 @@ fn parse_for( break; } let assign = parse_assign(reader, log); - if assign.is_some() { - increment_expressions.push(assign.unwrap()); + if let Some(s) = assign { + increment_expressions.push(s); } if reader.peek().token_type != TokenType::Comma { break; @@ -1433,7 +1565,7 @@ fn parse_lambda( fn parse_typemod( reader: &mut ParseReader, - log: &mut dyn FnMut(Message, Span), + _log: &mut dyn FnMut(Message, Span), ) -> Option> { // typemod ::= ['&' ['in' | 'out' | 'inout']]; if reader.peek().token_type == TokenType::Ampersand { diff --git a/src/parsing/parser/parsed_statement.rs b/src/parsing/parser/parsed_statement.rs index 3ea9c29..6595114 100644 --- a/src/parsing/parser/parsed_statement.rs +++ b/src/parsing/parser/parsed_statement.rs @@ -224,4 +224,17 @@ pub enum ParsedStatement { func_attr: BitFlags, block: Option>, }, + FuncDefDeclaration { + modifiers: BitFlags, + returns_reference: bool, + return_type: Option>, + name: String, + param_list: Box, + }, + ClassDeclaration { + modifiers: BitFlags, + name: String, + inherits: Vec, + statements: Vec>, + }, }