1use std::collections::HashMap;
174use std::num::NonZeroUsize;
175
176use crate::errors::{ErrorKind, FormError};
177use crate::files::common::{
178 Creatable, Exposed, FileAttributes, FileFormatVersion, NameSpace, ObjectReference,
179 PreDeclaredID, Properties,
180};
181use crate::io::{SourceFile, SourceStream};
182use crate::language::{
183 CheckBoxProperties, ComboBoxProperties, CommandButtonProperties, Control, ControlKind,
184 DataProperties, DirListBoxProperties, DriveListBoxProperties, FileListBoxProperties, Font,
185 Form, FormProperties, FormRoot, FrameProperties, LabelProperties, ListBoxProperties, MDIForm,
186 MDIFormProperties, MenuControl, MenuProperties, OptionButtonProperties, PictureBoxProperties,
187 PropertyGroup, TextBoxProperties, Token,
188};
189use crate::lexer::{tokenize, TokenStream};
190use crate::parsers::SyntaxKind;
191use crate::ParseResult;
192
193use either::Either;
194use rowan::{GreenNode, GreenNodeBuilder, Language};
195use serde::Serialize;
196
197mod assignment;
199mod attribute_statements;
200mod declarations;
201mod deftype_statements;
202mod enum_statements;
203mod for_statements;
204mod function_statements;
205mod helpers;
206mod if_statements;
207mod loop_statements;
208mod navigation;
209mod option_statements;
210mod parameters;
211mod properties;
212mod property_statements;
213mod select_statements;
214mod sub_statements;
215mod type_statements;
216
217pub use navigation::CstNode;
219
220#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, Hash)]
225pub struct SerializableTree {
226 pub root: CstNode,
228}
229
230pub(crate) fn serialize_cst<S>(cst: &ConcreteSyntaxTree, serializer: S) -> Result<S::Ok, S::Error>
232where
233 S: serde::Serializer,
234{
235 cst.to_serializable().serialize(serializer)
236}
237
238#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
240pub enum VB6Language {}
241
242impl Language for VB6Language {
243 type Kind = SyntaxKind;
244
245 fn kind_from_raw(raw: rowan::SyntaxKind) -> Self::Kind {
246 SyntaxKind::from_raw(raw)
247 }
248
249 fn kind_to_raw(kind: Self::Kind) -> rowan::SyntaxKind {
250 kind.to_raw()
251 }
252}
253
254fn extract_property_groups(groups: &[PropertyGroup]) -> ExtractedGroups {
256 let mut font = None;
257
258 for group in groups {
259 if group.name.eq_ignore_ascii_case("Font") {
260 if let Ok(f) = Font::try_from(group) {
261 font = Some(f);
262 }
263 }
264 }
266
267 ExtractedGroups { font }
268}
269
270struct ExtractedGroups {
272 font: Option<Font>,
273 }
275
276#[derive(Debug, Clone, PartialEq, Eq, Hash)]
281pub struct ConcreteSyntaxTree {
282 root: GreenNode,
284}
285
286impl ConcreteSyntaxTree {
287 fn new(root: GreenNode) -> Self {
289 Self { root }
290 }
291
292 #[must_use]
302 pub fn from_source(source_file: &SourceFile) -> ParseResult<'_, Self> {
303 Self::from_text(
304 source_file.file_name().to_string(),
305 source_file.source_stream().contents,
306 )
307 }
308
309 pub fn from_text<S>(file_name: S, contents: &str) -> ParseResult<'_, Self>
320 where
321 S: Into<String>,
322 {
323 let mut source_stream = SourceStream::new(file_name.into(), contents);
324 let token_stream_result = tokenize(&mut source_stream);
325 let (token_stream_opt, failures) = token_stream_result.unpack();
326
327 let Some(token_stream) = token_stream_opt else {
328 return ParseResult::new(None, failures);
329 };
330
331 let cst = parse(token_stream);
332
333 ParseResult::new(Some(cst), failures)
334 }
335
336 #[must_use]
338 pub fn root_kind(&self) -> SyntaxKind {
339 SyntaxKind::from_raw(self.root.kind())
340 }
341
342 #[must_use]
372 pub fn to_serializable(&self) -> SerializableTree {
373 SerializableTree {
374 root: self.to_root_node(),
375 }
376 }
377
378 #[must_use]
384 pub fn to_root_node(&self) -> CstNode {
385 CstNode::new(SyntaxKind::Root, self.text(), false, self.children())
386 }
387
388 #[must_use]
419 pub fn without_kinds(&self, kinds_to_remove: &[SyntaxKind]) -> Self {
420 let syntax_node = rowan::SyntaxNode::<VB6Language>::new_root(self.root.clone());
421 let mut builder = GreenNodeBuilder::new();
422
423 builder.start_node(SyntaxKind::Root.to_raw());
424
425 for child in syntax_node.children_with_tokens() {
427 let child_kind = match &child {
428 rowan::NodeOrToken::Node(node) => node.kind(),
429 rowan::NodeOrToken::Token(token) => token.kind(),
430 };
431
432 if kinds_to_remove.contains(&child_kind) {
434 continue;
435 }
436
437 Self::clone_node_or_token(&mut builder, child);
439 }
440
441 builder.finish_node();
442 let new_root = builder.finish();
443
444 Self::new(new_root)
445 }
446
447 fn clone_node_or_token(
449 builder: &mut GreenNodeBuilder<'static>,
450 node_or_token: rowan::NodeOrToken<
451 rowan::SyntaxNode<VB6Language>,
452 rowan::SyntaxToken<VB6Language>,
453 >,
454 ) {
455 match node_or_token {
456 rowan::NodeOrToken::Node(node) => {
457 builder.start_node(node.kind().to_raw());
458 for child in node.children_with_tokens() {
459 Self::clone_node_or_token(builder, child);
460 }
461 builder.finish_node();
462 }
463 rowan::NodeOrToken::Token(token) => {
464 builder.token(token.kind().to_raw(), token.text());
465 }
466 }
467 }
468}
469
470#[must_use]
493pub fn parse(tokens: TokenStream) -> ConcreteSyntaxTree {
494 let parser = Parser::new(tokens);
495 parser.parse_root()
496}
497
498pub(crate) struct Parser<'a> {
500 pub(crate) tokens: Vec<(&'a str, Token)>,
501 pub(crate) pos: usize,
502 pub(crate) builder: GreenNodeBuilder<'static>,
503 pub(crate) parsing_header: bool,
504}
505
506impl<'a> Parser<'a> {
507 fn new(token_stream: TokenStream<'a>) -> Self {
508 Parser {
509 tokens: token_stream.into_tokens(),
510 pos: 0,
511 builder: GreenNodeBuilder::new(),
512 parsing_header: true,
513 }
514 }
515
516 pub(crate) fn new_direct_extraction(tokens: Vec<(&'a str, Token)>, pos: usize) -> Self {
518 Parser {
519 tokens,
520 pos,
521 builder: GreenNodeBuilder::new(),
522 parsing_header: true,
523 }
524 }
525
526 pub(crate) fn into_tokens(self) -> Vec<(&'a str, Token)> {
533 self.tokens[self.pos..].to_vec()
535 }
536
537 pub(crate) fn skip_whitespace(&mut self) {
539 while self.at_token(Token::Whitespace) {
540 self.pos += 1;
541 }
542 }
543
544 pub(crate) fn skip_whitespace_and_newlines(&mut self) {
546 while self.at_token(Token::Whitespace) || self.at_token(Token::Newline) {
547 self.pos += 1;
548 }
549 }
550
551 pub(crate) fn consume_advance(&mut self) -> Option<(&'a str, Token)> {
554 if self.pos < self.tokens.len() {
555 let token = self.tokens[self.pos];
556 self.pos += 1;
557 Some(token)
558 } else {
559 None
560 }
561 }
562
563 pub(crate) fn parse_version_direct(&mut self) -> ParseResult<'a, FileFormatVersion> {
576 self.skip_whitespace();
577
578 if !self.at_token(Token::VersionKeyword) {
580 return ParseResult::new(None, Vec::new());
581 }
582
583 self.consume_advance(); self.skip_whitespace();
585
586 let version_result = if let Some((text, token)) = self.tokens.get(self.pos) {
588 match token {
589 Token::SingleLiteral | Token::DoubleLiteral | Token::IntegerLiteral => {
590 let version_str = text.trim();
591 self.consume_advance();
592
593 let parts: Vec<&str> = version_str.split('.').collect();
595 if parts.len() == 2 {
596 if let (Ok(major), Ok(minor)) =
597 (parts[0].parse::<u8>(), parts[1].parse::<u8>())
598 {
599 Some(FileFormatVersion { major, minor })
600 } else {
601 None
602 }
603 } else {
604 None
605 }
606 }
607 _ => None,
608 }
609 } else {
610 None
611 };
612
613 self.skip_whitespace();
615 if self.at_token(Token::ClassKeyword) {
616 self.consume_advance();
617 }
618 self.skip_whitespace_and_newlines();
619
620 ParseResult::new(version_result, Vec::new())
621 }
622
623 fn is_begin_property(&self) -> bool {
627 if let Some((text, token)) = self.tokens.get(self.pos) {
628 *token == Token::Identifier && text.eq_ignore_ascii_case("BeginProperty")
629 } else {
630 false
631 }
632 }
633
634 fn is_identifier_text(&self, target: &str) -> bool {
636 if let Some((text, token)) = self.tokens.get(self.pos) {
637 *token == Token::Identifier && text.eq_ignore_ascii_case(target)
638 } else {
639 false
640 }
641 }
642
643 fn control_to_menu(control: Control) -> MenuControl {
645 let (name, tag, index, kind) = control.into_parts();
646
647 if let ControlKind::Menu {
648 properties,
649 sub_menus,
650 } = kind
651 {
652 MenuControl::new(name, tag, index, properties, sub_menus)
653 } else {
654 MenuControl::new(name, tag, index, MenuProperties::default(), Vec::new())
656 }
657 }
658
659 fn parse_control_type_direct(&mut self) -> String {
661 let mut parts = Vec::new();
662
663 if self.is_identifier() || self.at_keyword() {
665 if let Some((text, _)) = self.tokens.get(self.pos) {
666 parts.push(text.to_string());
667 self.consume_advance();
668 }
669 }
670
671 while self.at_token(Token::PeriodOperator) {
673 self.consume_advance(); if self.is_identifier() || self.at_keyword() {
675 if let Some((text, _)) = self.tokens.get(self.pos) {
676 parts.push(".".to_string());
677 parts.push(text.to_string());
678 self.consume_advance();
679 }
680 }
681 }
682
683 parts.join("")
684 }
685
686 fn parse_control_name_direct(&mut self) -> String {
688 if self.is_identifier() || self.at_keyword() {
689 if let Some((text, _)) = self.tokens.get(self.pos) {
690 let name = text.to_string();
691 self.consume_advance();
692 return name;
693 }
694 }
695 String::new()
696 }
697
698 fn parse_property_direct(&mut self) -> Option<(String, String)> {
701 let key = if self.is_identifier() || self.at_keyword() {
703 if let Some((text, _)) = self.tokens.get(self.pos) {
704 let k = text.to_string();
705 self.consume_advance();
706 k
707 } else {
708 return None;
709 }
710 } else {
711 return None;
712 };
713
714 self.skip_whitespace();
715
716 if !self.at_token(Token::EqualityOperator) {
718 return None;
719 }
720 self.consume_advance();
721 self.skip_whitespace();
722
723 let mut value_parts = Vec::new();
727 let mut in_resource_reference = false;
728
729 while !self.is_at_end() && !self.at_token(Token::Newline) {
730 if let Some((text, token)) = self.tokens.get(self.pos) {
731 let text_copy = *text;
732 let token_copy = *token;
733
734 if token_copy == Token::DollarSign {
736 value_parts.push(text_copy);
737 self.consume_advance();
738 }
739 else if token_copy == Token::StringLiteral {
741 value_parts.push(text_copy);
742 self.consume_advance();
743
744 if let Some((_, next_token)) = self.tokens.get(self.pos) {
746 if *next_token == Token::ColonOperator {
747 in_resource_reference = true;
748 }
749 }
750 }
751 else if in_resource_reference && token_copy == Token::ColonOperator {
753 value_parts.push(text_copy);
754 self.consume_advance();
755 }
756 else if in_resource_reference
758 && (token_copy == Token::IntegerLiteral || token_copy == Token::LongLiteral)
759 {
760 value_parts.push(text_copy);
761 self.consume_advance();
762 break; }
764 else if token_copy == Token::ColonOperator {
766 break;
767 }
768 else {
770 value_parts.push(text_copy);
771 self.consume_advance();
772 }
773 } else {
774 break;
775 }
776 }
777
778 self.skip_whitespace_and_newlines();
780
781 let value = value_parts.concat().trim().to_string();
783 Some((key, value))
784 }
785
786 fn parse_property_group_direct(&mut self) -> Option<PropertyGroup> {
788 if !self.is_identifier_text("BeginProperty") {
790 return None;
791 }
792
793 self.consume_advance(); self.skip_whitespace();
795
796 let (name, guid) = self.parse_property_group_name_direct();
798 self.skip_whitespace_and_newlines();
799
800 let mut properties = HashMap::new();
802
803 while !self.is_at_end() && !self.is_identifier_text("EndProperty") {
804 self.skip_whitespace();
805
806 if self.is_identifier_text("EndProperty") {
807 break;
808 }
809
810 if self.is_identifier_text("BeginProperty") {
811 if let Some(nested_group) = self.parse_property_group_direct() {
813 properties.insert(nested_group.name.clone(), Either::Right(nested_group));
814 }
815 } else if self.is_identifier() || self.at_keyword() {
816 if let Some((key, value)) = self.parse_property_direct() {
818 properties.insert(key, Either::Left(value));
819 }
820 } else {
821 self.consume_advance();
822 }
823 }
824
825 if self.is_identifier_text("EndProperty") {
827 self.consume_advance();
828 self.skip_whitespace_and_newlines();
829 }
830
831 Some(PropertyGroup {
832 name,
833 guid,
834 properties,
835 })
836 }
837
838 fn parse_property_group_name_direct(&mut self) -> (String, Option<uuid::Uuid>) {
841 let mut name_parts: Vec<&str> = Vec::new();
842 let mut guid_parts: Vec<&str> = Vec::new();
843 let mut in_guid = false;
844
845 while !self.is_at_end() && !self.at_token(Token::Newline) {
847 if let Some((text, token)) = self.tokens.get(self.pos) {
848 if *token == Token::LeftCurlyBrace {
849 in_guid = true;
851 } else if *token == Token::RightCurlyBrace {
852 in_guid = false;
854 } else if *token != Token::Whitespace && *token != Token::EndOfLineComment {
855 if in_guid {
857 guid_parts.push(*text);
858 } else {
859 name_parts.push(*text);
860 }
861 }
862 }
863 self.consume_advance();
864 }
865
866 let name = name_parts.concat();
867 let guid = if guid_parts.is_empty() {
868 None
869 } else {
870 let guid_str = guid_parts.concat();
871 uuid::Uuid::parse_str(&guid_str).ok()
872 };
873
874 (name, guid)
875 }
876
877 pub(crate) fn parse_objects_direct(&mut self) -> Vec<ObjectReference> {
880 let mut objects = Vec::new();
881
882 self.skip_whitespace_and_newlines();
883
884 while self.at_token(Token::ObjectKeyword) {
886 if let Some(obj_ref) = self.parse_single_object_direct() {
887 objects.push(obj_ref);
888 }
889 self.skip_whitespace_and_newlines();
890 }
891
892 objects
893 }
894
895 fn parse_single_object_direct(&mut self) -> Option<ObjectReference> {
899 if !self.at_token(Token::ObjectKeyword) {
901 return None;
902 }
903 self.consume_advance(); self.skip_whitespace();
905
906 if !self.at_token(Token::EqualityOperator) {
908 return None;
909 }
910 self.consume_advance(); self.skip_whitespace();
912
913 let mut _is_embedded = false;
915 if self.at_token(Token::MultiplicationOperator) {
916 self.consume_advance(); if let Some((_text, token)) = self.tokens.get(self.pos) {
919 if *token == Token::BackwardSlashOperator {
920 self.consume_advance(); if let Some((text2, token2)) = self.tokens.get(self.pos) {
922 if *token2 == Token::Identifier && text2.eq_ignore_ascii_case("G") {
923 self.consume_advance(); _is_embedded = true;
925 }
926 }
927 }
928 }
929 }
930 self.skip_whitespace();
931
932 let uuid_part = if let Some((text, token)) = self.tokens.get(self.pos) {
937 if *token == Token::StringLiteral {
938 let s = text.trim_matches('"').to_string();
940 self.consume_advance();
941 s
942 } else if *token == Token::LeftCurlyBrace {
943 let mut parts: Vec<&str> = Vec::new();
946 while !self.is_at_end() && !self.at_token(Token::Semicolon) {
947 if let Some((text, token)) = self.tokens.get(self.pos) {
948 if *token != Token::Whitespace {
950 parts.push(text);
951 }
952 self.consume_advance();
953 } else {
954 break;
955 }
956 }
957 parts.concat()
960 } else {
961 return None;
962 }
963 } else {
964 return None;
965 };
966
967 self.skip_whitespace();
968
969 if !self.at_token(Token::Semicolon) {
971 return None;
972 }
973 self.consume_advance(); self.skip_whitespace();
975
976 let file_name = if let Some((text, token)) = self.tokens.get(self.pos) {
978 if *token == Token::StringLiteral {
979 let s = text.trim_matches('"').to_string();
980 self.consume_advance();
981 s
982 } else {
983 return None;
984 }
985 } else {
986 return None;
987 };
988
989 let parts: Vec<&str> = uuid_part.split('#').collect();
991 if parts.len() >= 3 {
992 let uuid_str = parts[0].trim_matches(|c| c == '{' || c == '}');
994
995 if let Ok(uuid) = uuid::Uuid::parse_str(uuid_str) {
996 let version = parts[1].to_string();
997 let unknown1 = parts[2].to_string();
998
999 return Some(ObjectReference::Compiled {
1000 uuid,
1001 version,
1002 unknown1,
1003 file_name,
1004 });
1005 }
1006 }
1007
1008 None
1009 }
1010
1011 pub(crate) fn parse_attributes_direct(&mut self) -> FileAttributes {
1014 let mut name = String::new();
1015 let mut global_name_space = NameSpace::Local;
1016 let mut creatable = Creatable::True;
1017 let mut predeclared_id = PreDeclaredID::False;
1018 let mut exposed = Exposed::False;
1019 let mut description: Option<String> = None;
1020 let mut ext_key: HashMap<String, String> = HashMap::new();
1021
1022 self.skip_whitespace_and_newlines();
1023
1024 while self.at_token(Token::AttributeKeyword) {
1026 if let Some((key, value)) = self.parse_single_attribute_direct() {
1027 match key.as_str() {
1029 "VB_Name" => {
1030 name = value;
1031 }
1032 "VB_GlobalNameSpace" => {
1033 global_name_space = if value == "True" || value == "-1" {
1034 NameSpace::Global
1035 } else {
1036 NameSpace::Local
1037 };
1038 }
1039 "VB_Creatable" => {
1040 creatable = if value == "True" || value == "-1" {
1041 Creatable::True
1042 } else {
1043 Creatable::False
1044 };
1045 }
1046 "VB_PredeclaredId" => {
1047 predeclared_id = if value == "True" || value == "-1" {
1048 PreDeclaredID::True
1049 } else {
1050 PreDeclaredID::False
1051 };
1052 }
1053 "VB_Exposed" => {
1054 exposed = if value == "True" || value == "-1" {
1055 Exposed::True
1056 } else {
1057 Exposed::False
1058 };
1059 }
1060 "VB_Description" => {
1061 description = Some(value);
1062 }
1063 _ => {
1064 ext_key.insert(key, value);
1066 }
1067 }
1068 }
1069 self.skip_whitespace_and_newlines();
1070 }
1071
1072 FileAttributes {
1073 name,
1074 global_name_space,
1075 creatable,
1076 predeclared_id,
1077 exposed,
1078 description,
1079 ext_key,
1080 }
1081 }
1082
1083 fn parse_single_attribute_direct(&mut self) -> Option<(String, String)> {
1088 if !self.at_token(Token::AttributeKeyword) {
1090 return None;
1091 }
1092 self.consume_advance(); self.skip_whitespace();
1094
1095 let key = if let Some((text, token)) = self.tokens.get(self.pos) {
1097 if *token == Token::Identifier {
1098 let k = text.to_string();
1099 self.consume_advance();
1100 k
1101 } else {
1102 return None;
1103 }
1104 } else {
1105 return None;
1106 };
1107
1108 self.skip_whitespace();
1109
1110 if !self.at_token(Token::EqualityOperator) {
1112 return None;
1113 }
1114 self.consume_advance(); self.skip_whitespace();
1116
1117 let mut value = String::new();
1119 let mut found_value = false;
1120
1121 if self.at_token(Token::SubtractionOperator) {
1123 value.push('-');
1124 self.consume_advance();
1125 self.skip_whitespace();
1126 }
1127
1128 if let Some((text, token)) = self.tokens.get(self.pos) {
1129 match token {
1130 Token::StringLiteral => {
1131 value.push_str(text.trim().trim_matches('"'));
1133 self.consume_advance();
1134 found_value = true;
1135 }
1136 Token::TrueKeyword => {
1137 value.push_str("True");
1138 self.consume_advance();
1139 found_value = true;
1140 }
1141 Token::FalseKeyword => {
1142 value.push_str("False");
1143 self.consume_advance();
1144 found_value = true;
1145 }
1146 Token::IntegerLiteral | Token::LongLiteral => {
1147 value.push_str(text.trim());
1148 self.consume_advance();
1149 found_value = true;
1150 }
1151 _ => {}
1152 }
1153 }
1154
1155 while self.pos < self.tokens.len() {
1158 if let Some((_, token)) = self.tokens.get(self.pos) {
1159 if *token == Token::Newline {
1160 break;
1161 }
1162 self.consume_advance();
1163 } else {
1164 break;
1165 }
1166 }
1167
1168 if found_value {
1169 Some((key, value))
1170 } else {
1171 None
1172 }
1173 }
1174
1175 fn build_form_root(
1180 control_type: &str,
1181 control_name: String,
1182 tag: String,
1183 index: i32,
1184 properties: Properties,
1185 groups: &[PropertyGroup],
1186 child_controls: Vec<Control>,
1187 menus: Vec<MenuControl>,
1188 ) -> Result<FormRoot, ErrorKind> {
1189 match control_type {
1190 "VB.Form" => {
1191 let mut form_properties: FormProperties = properties.into();
1192 let extracted_groups = extract_property_groups(groups);
1194 if let Some(font) = extracted_groups.font {
1195 form_properties.font = Some(font);
1196 }
1197
1198 Ok(FormRoot::Form(Form {
1199 name: control_name,
1200 tag,
1201 index,
1202 properties: form_properties,
1203 controls: child_controls,
1204 menus,
1205 }))
1206 }
1207 "VB.MDIForm" => {
1208 let mut mdi_form_properties: MDIFormProperties = properties.into();
1209 let extracted_groups = extract_property_groups(groups);
1211 if let Some(font) = extracted_groups.font {
1212 mdi_form_properties.font = Some(font);
1213 }
1214
1215 Ok(FormRoot::MDIForm(MDIForm {
1216 name: control_name,
1217 tag,
1218 index,
1219 properties: mdi_form_properties,
1220 controls: child_controls,
1221 menus,
1222 }))
1223 }
1224 _ => Err(ErrorKind::Form(FormError::InvalidTopLevelControl {
1225 control_type: control_type.to_string(),
1226 })),
1227 }
1228 }
1229
1230 fn build_control_kind(
1235 control_type: &str,
1236 properties: Properties,
1237 child_controls: Vec<Control>,
1238 menus: Vec<MenuControl>,
1239 property_groups: Vec<PropertyGroup>,
1240 ) -> ControlKind {
1241 use ControlKind;
1242 let groups = extract_property_groups(&property_groups);
1244
1245 match control_type {
1246 "VB.CommandButton" => {
1247 let mut props: CommandButtonProperties = properties.into();
1248 if let Some(font) = groups.font {
1250 props.font = Some(font);
1251 }
1252 ControlKind::CommandButton { properties: props }
1253 }
1254 "VB.Data" => {
1255 let mut props: DataProperties = properties.into();
1256 if let Some(font) = groups.font {
1258 props.font = Some(font);
1259 }
1260 ControlKind::Data { properties: props }
1261 }
1262 "VB.TextBox" => {
1263 let mut props: TextBoxProperties = properties.into();
1264 if let Some(font) = groups.font {
1266 props.font = Some(font);
1267 }
1268 ControlKind::TextBox { properties: props }
1269 }
1270 "VB.Label" => {
1271 let mut props: LabelProperties = properties.into();
1272 if let Some(font) = groups.font {
1274 props.font = Some(font);
1275 }
1276 ControlKind::Label { properties: props }
1277 }
1278 "VB.CheckBox" => {
1279 let mut props: CheckBoxProperties = properties.into();
1280 if let Some(font) = groups.font {
1282 props.font = Some(font);
1283 }
1284 ControlKind::CheckBox { properties: props }
1285 }
1286 "VB.Line" => ControlKind::Line {
1287 properties: properties.into(),
1288 },
1289 "VB.Shape" => ControlKind::Shape {
1290 properties: properties.into(),
1291 },
1292 "VB.ListBox" => {
1293 let mut props: ListBoxProperties = properties.into();
1294 if let Some(font) = groups.font {
1296 props.font = Some(font);
1297 }
1298 ControlKind::ListBox { properties: props }
1299 }
1300 "VB.ComboBox" => {
1301 let mut props: ComboBoxProperties = properties.into();
1302 if let Some(font) = groups.font {
1304 props.font = Some(font);
1305 }
1306 ControlKind::ComboBox { properties: props }
1307 }
1308 "VB.Timer" => ControlKind::Timer {
1309 properties: properties.into(),
1310 },
1311 "VB.HScrollBar" => ControlKind::HScrollBar {
1312 properties: properties.into(),
1313 },
1314 "VB.VScrollBar" => ControlKind::VScrollBar {
1315 properties: properties.into(),
1316 },
1317 "VB.Frame" => {
1318 let mut props: FrameProperties = properties.into();
1319 if let Some(font) = groups.font {
1321 props.font = Some(font);
1322 }
1323 ControlKind::Frame {
1324 properties: props,
1325 controls: child_controls,
1326 }
1327 }
1328 "VB.PictureBox" => {
1329 let mut props: PictureBoxProperties = properties.into();
1330 if let Some(font) = groups.font {
1332 props.font = Some(font);
1333 }
1334 ControlKind::PictureBox {
1335 properties: props,
1336 controls: child_controls,
1337 }
1338 }
1339 "VB.FileListBox" => {
1340 let mut props: FileListBoxProperties = properties.into();
1341 if let Some(font) = groups.font {
1343 props.font = Some(font);
1344 }
1345 ControlKind::FileListBox { properties: props }
1346 }
1347 "VB.DirListBox" => {
1348 let mut props: DirListBoxProperties = properties.into();
1349 if let Some(font) = groups.font {
1351 props.font = Some(font);
1352 }
1353 ControlKind::DirListBox { properties: props }
1354 }
1355 "VB.DriveListBox" => {
1356 let mut props: DriveListBoxProperties = properties.into();
1357 if let Some(font) = groups.font {
1359 props.font = Some(font);
1360 }
1361 ControlKind::DriveListBox { properties: props }
1362 }
1363 "VB.Image" => ControlKind::Image {
1364 properties: properties.into(),
1365 },
1366 "VB.OptionButton" => {
1367 let mut props: OptionButtonProperties = properties.into();
1368 if let Some(font) = groups.font {
1370 props.font = Some(font);
1371 }
1372 ControlKind::OptionButton { properties: props }
1373 }
1374 "VB.OLE" => ControlKind::Ole {
1375 properties: properties.into(),
1376 },
1377 "VB.Menu" => ControlKind::Menu {
1378 properties: properties.into(),
1379 sub_menus: menus,
1380 },
1381 _ => ControlKind::Custom {
1382 properties: properties.into(),
1383 property_groups,
1384 },
1385 }
1386 }
1387
1388 pub(crate) fn parse_properties_block_to_control(&mut self) -> ParseResult<'a, Control> {
1391 self.skip_whitespace();
1392
1393 if !self.at_token(Token::BeginKeyword) {
1395 return ParseResult::new(None, Vec::new());
1396 }
1397
1398 self.consume_advance(); self.skip_whitespace();
1400
1401 let control_type = self.parse_control_type_direct();
1403 self.skip_whitespace();
1404
1405 let control_name = self.parse_control_name_direct();
1407 self.skip_whitespace_and_newlines();
1408
1409 let mut properties = Properties::new();
1411 let mut child_controls = Vec::new();
1412 let mut menus = Vec::new();
1413 let mut property_groups = Vec::new();
1414 let mut failures = Vec::new();
1415
1416 while !self.is_at_end() && !self.at_token(Token::EndKeyword) {
1417 self.skip_whitespace();
1418
1419 if self.at_token(Token::EndKeyword) {
1420 break;
1421 }
1422
1423 if self.at_token(Token::BeginKeyword) {
1424 let child_result = self.parse_properties_block_to_control();
1426 let (child_opt, child_failures) = child_result.unpack();
1427 failures.extend(child_failures);
1428
1429 if let Some(child) = child_opt {
1430 if matches!(child.kind(), ControlKind::Menu { .. }) {
1432 menus.push(Self::control_to_menu(child));
1433 } else {
1434 child_controls.push(child);
1435 }
1436 }
1437 } else if self.is_begin_property() {
1438 if let Some(group) = self.parse_property_group_direct() {
1440 property_groups.push(group);
1441 }
1442 } else if self.is_identifier() || self.at_keyword() {
1443 if let Some((key, value)) = self.parse_property_direct() {
1445 let is_resource_reference = value.contains(':')
1448 && value
1449 .split(':')
1450 .next_back()
1451 .is_some_and(|part| part.chars().all(|c| c.is_ascii_digit()));
1452
1453 let cleaned_value = if !is_resource_reference
1454 && value.starts_with('"')
1455 && value.ends_with('"')
1456 && value.len() >= 2
1457 {
1458 &value[1..value.len() - 1]
1459 } else {
1460 &value
1461 };
1462 properties.insert(&key, cleaned_value);
1463 }
1464 } else {
1465 self.consume_advance();
1467 }
1468 }
1469
1470 if self.at_token(Token::EndKeyword) {
1472 self.consume_advance();
1473 self.skip_whitespace_and_newlines();
1474 }
1475
1476 let tag = properties.get("Tag").cloned().unwrap_or_default();
1478 let index = properties
1479 .get("Index")
1480 .and_then(|s| s.parse().ok())
1481 .unwrap_or(0);
1482
1483 let kind = Self::build_control_kind(
1485 &control_type,
1486 properties,
1487 child_controls,
1488 menus,
1489 property_groups,
1490 );
1491
1492 let control = Control::new(control_name, tag, index, kind);
1493
1494 ParseResult::new(Some(control), failures)
1495 }
1496
1497 pub(crate) fn parse_properties_block_to_form_root(&mut self) -> ParseResult<'a, FormRoot> {
1508 use Properties;
1509
1510 let mut groups = Vec::new();
1511
1512 self.skip_whitespace();
1513
1514 if !self.at_token(Token::BeginKeyword) {
1516 return ParseResult::new(None, Vec::new());
1517 }
1518
1519 self.consume_advance(); self.skip_whitespace();
1521
1522 let control_type = self.parse_control_type_direct();
1524 self.skip_whitespace();
1525
1526 let control_name = self.parse_control_name_direct();
1528 self.skip_whitespace_and_newlines();
1529
1530 let mut properties = Properties::new();
1532 let mut child_controls = Vec::new();
1533 let mut menus = Vec::new();
1534 let mut failures = Vec::new();
1535
1536 while !self.is_at_end() && !self.at_token(Token::EndKeyword) {
1537 self.skip_whitespace();
1538
1539 if self.at_token(Token::EndKeyword) {
1540 break;
1541 }
1542
1543 if self.at_token(Token::BeginKeyword) {
1544 let child_result = self.parse_properties_block_to_control();
1546 let (child_opt, child_failures) = child_result.unpack();
1547 failures.extend(child_failures);
1548
1549 if let Some(child) = child_opt {
1550 if matches!(child.kind(), ControlKind::Menu { .. }) {
1552 menus.push(Self::control_to_menu(child));
1553 } else {
1554 child_controls.push(child);
1555 }
1556 }
1557 } else if self.is_begin_property() {
1558 if let Some(group) = self.parse_property_group_direct() {
1560 groups.push(group);
1563 }
1564 } else if self.is_identifier() || self.at_keyword() {
1565 if let Some((key, value)) = self.parse_property_direct() {
1567 let is_resource_reference = value.contains(':')
1570 && value
1571 .split(':')
1572 .next_back()
1573 .is_some_and(|part| part.chars().all(|c| c.is_ascii_digit()));
1574
1575 let cleaned_value = if !is_resource_reference
1576 && value.starts_with('"')
1577 && value.ends_with('"')
1578 && value.len() >= 2
1579 {
1580 &value[1..value.len() - 1]
1581 } else {
1582 &value
1583 };
1584 properties.insert(&key, cleaned_value);
1585 }
1586 } else {
1587 self.consume_advance();
1589 }
1590 }
1591
1592 if self.at_token(Token::EndKeyword) {
1594 self.consume_advance();
1595 self.skip_whitespace_and_newlines();
1596 }
1597
1598 let tag = properties.get("Tag").cloned().unwrap_or_default();
1600 let index = properties
1601 .get("Index")
1602 .and_then(|s| s.parse().ok())
1603 .unwrap_or(0);
1604
1605 if let Ok(form_root) = Self::build_form_root(
1607 &control_type,
1608 control_name,
1609 tag,
1610 index,
1611 properties,
1612 &groups,
1613 child_controls,
1614 menus,
1615 ) {
1616 ParseResult::new(Some(form_root), failures)
1617 } else {
1618 let default_form = FormRoot::Form(Form {
1620 name: String::new(),
1621 tag: String::new(),
1622 index: 0,
1623 properties: FormProperties::default(),
1624 controls: Vec::new(),
1625 menus: Vec::new(),
1626 });
1627 ParseResult::new(Some(default_form), failures)
1628 }
1629 }
1630
1631 fn parse_root(mut self) -> ConcreteSyntaxTree {
1637 self.builder.start_node(SyntaxKind::Root.to_raw());
1638
1639 if self.at_token(Token::VersionKeyword) {
1641 self.parse_version_statement();
1642 }
1643
1644 if self.at_token(Token::BeginKeyword) {
1646 self.parse_properties_block();
1647 }
1648
1649 while self.at_token(Token::AttributeKeyword) {
1652 self.parse_attribute_statement();
1653 }
1654
1655 self.parse_module_body();
1656 self.builder.finish_node(); let root = self.builder.finish();
1659 ConcreteSyntaxTree::new(root)
1660 }
1661
1662 fn parse_module_body(&mut self) {
1663 while !self.is_at_end() {
1664 match self.current_token() {
1669 Some(Token::BeginKeyword) => {
1672 self.parse_properties_block();
1673 }
1674 Some(Token::ObjectKeyword) if self.is_object_statement() => {
1677 self.parse_object_statement();
1678 }
1679 Some(Token::AttributeKeyword) => {
1681 self.parse_attribute_statement();
1682 }
1683 Some(Token::OptionKeyword) => {
1684 if let Some(Token::BaseKeyword) = self.peek_next_keyword() {
1686 self.parse_option_base_statement();
1687 } else if let Some(Token::CompareKeyword) = self.peek_next_keyword() {
1688 self.parse_option_compare_statement();
1689 } else if let Some(Token::PrivateKeyword) = self.peek_next_keyword() {
1690 self.parse_option_private_statement();
1691 } else {
1692 self.parse_option_statement();
1693 }
1694 }
1695 Some(
1697 Token::DefBoolKeyword
1698 | Token::DefByteKeyword
1699 | Token::DefIntKeyword
1700 | Token::DefLngKeyword
1701 | Token::DefCurKeyword
1702 | Token::DefSngKeyword
1703 | Token::DefDblKeyword
1704 | Token::DefDecKeyword
1705 | Token::DefDateKeyword
1706 | Token::DefStrKeyword
1707 | Token::DefObjKeyword
1708 | Token::DefVarKeyword,
1709 ) => {
1710 self.parse_deftype_statement();
1711 }
1712 Some(Token::DeclareKeyword) => {
1714 self.parse_declare_statement();
1715 }
1716 Some(Token::EventKeyword) => {
1718 self.parse_event_statement();
1719 }
1720 Some(Token::ImplementsKeyword) => {
1722 self.parse_implements_statement();
1723 }
1724 Some(Token::EnumKeyword) => {
1726 self.parse_enum_statement();
1727 }
1728 Some(Token::TypeKeyword) => {
1730 self.parse_type_statement();
1731 }
1732 Some(Token::SubKeyword) => {
1734 self.parse_sub_statement();
1735 }
1736 Some(Token::FunctionKeyword) => {
1741 self.parse_function_statement();
1742 }
1743 Some(Token::PropertyKeyword) => {
1748 self.parse_property_statement();
1749 }
1750 Some(Token::DimKeyword | Token::ConstKeyword) => {
1754 self.parse_dim();
1755 }
1756 Some(
1758 Token::PrivateKeyword
1759 | Token::PublicKeyword
1760 | Token::FriendKeyword
1761 | Token::StaticKeyword,
1762 ) => {
1763 let next_keywords: Vec<_> = self
1766 .peek_next_count_keywords(NonZeroUsize::new(2).unwrap())
1767 .collect();
1768
1769 match next_keywords.as_slice() {
1770 [Token::FunctionKeyword, ..] => self.parse_function_statement(), [Token::SubKeyword, ..] => self.parse_sub_statement(), [Token::PropertyKeyword, ..] => self.parse_property_statement(), [Token::DeclareKeyword, ..] => self.parse_declare_statement(), [Token::EnumKeyword, ..] => self.parse_enum_statement(), [Token::TypeKeyword, ..] => self.parse_type_statement(), [Token::EventKeyword, ..] => self.parse_event_statement(), [Token::ImplementsKeyword, ..] => self.parse_implements_statement(), [Token::StaticKeyword, Token::FunctionKeyword] => {
1781 self.parse_function_statement();
1782 }
1783 [Token::StaticKeyword, Token::SubKeyword] => {
1784 self.parse_sub_statement();
1785 }
1786 [Token::StaticKeyword, Token::PropertyKeyword] => {
1787 self.parse_property_statement();
1788 }
1789 _ => self.parse_dim(),
1791 }
1792 }
1793 _ => {
1795 if matches!(
1799 self.current_token(),
1800 Some(
1801 Token::Whitespace
1802 | Token::Newline
1803 | Token::EndOfLineComment
1804 | Token::RemComment
1805 )
1806 ) {
1807 self.consume_token();
1808 } else if self.is_control_flow_keyword() {
1810 self.parse_control_flow_statement();
1811 } else if self.is_library_statement_keyword() {
1813 self.parse_library_statement();
1814 } else if self.is_variable_declaration_keyword() {
1816 self.parse_array_statement();
1817 } else if self.is_statement_keyword() {
1819 self.parse_statement();
1820 } else if self.is_at_label() {
1822 self.parse_label_statement();
1823 } else if self.at_token(Token::LetKeyword) {
1825 self.parse_let_statement();
1826 } else if self.is_at_assignment() {
1829 self.parse_assignment_statement();
1830 } else if self.is_at_procedure_call() {
1832 self.parse_procedure_call();
1833 } else if self.is_identifier() || self.at_keyword() {
1834 self.consume_token();
1835 } else {
1836 self.consume_token_as_unknown();
1840 }
1841 }
1842 }
1843 }
1844 }
1845
1846 fn is_control_flow_keyword(&self) -> bool {
1849 let token = if self.at_token(Token::Whitespace) {
1850 self.peek_next_keyword()
1851 } else {
1852 self.current_token().copied()
1853 };
1854
1855 matches!(
1856 token,
1857 Some(
1858 Token::IfKeyword
1859 | Token::SelectKeyword
1860 | Token::ForKeyword
1861 | Token::DoKeyword
1862 | Token::WhileKeyword
1863 | Token::GotoKeyword
1864 | Token::GoSubKeyword
1865 | Token::ReturnKeyword
1866 | Token::ResumeKeyword
1867 | Token::ExitKeyword
1868 | Token::OnKeyword
1869 )
1870 )
1871 }
1872
1873 fn parse_control_flow_statement(&mut self) {
1875 let token = if self.at_token(Token::Whitespace) {
1876 self.peek_next_keyword()
1877 } else {
1878 self.current_token().copied()
1879 };
1880
1881 match token {
1882 Some(Token::IfKeyword) => {
1883 self.parse_if_statement();
1884 }
1885 Some(Token::SelectKeyword) => {
1886 self.parse_select_case_statement();
1887 }
1888 Some(Token::ForKeyword) => {
1889 let next_kw = if self.at_token(Token::Whitespace) {
1892 self.peek_next_count_keywords(NonZeroUsize::new(2).unwrap())
1894 .nth(1)
1895 } else {
1896 self.peek_next_keyword()
1898 };
1899
1900 if let Some(Token::EachKeyword) = next_kw {
1901 self.parse_for_each_statement();
1902 } else {
1903 self.parse_for_statement();
1904 }
1905 }
1906 Some(Token::DoKeyword) => {
1907 self.parse_do_statement();
1908 }
1909 Some(Token::WhileKeyword) => {
1910 self.parse_while_statement();
1911 }
1912 Some(Token::GotoKeyword) => {
1913 self.parse_goto_statement();
1914 }
1915 Some(Token::GoSubKeyword) => {
1916 self.parse_gosub_statement();
1917 }
1918 Some(Token::ReturnKeyword) => {
1919 self.parse_return_statement();
1920 }
1921 Some(Token::ResumeKeyword) => {
1922 self.parse_resume_statement();
1923 }
1924 Some(Token::ExitKeyword) => {
1925 self.parse_exit_statement();
1926 }
1927 Some(Token::OnKeyword) => {
1928 let next_kw = if self.at_token(Token::Whitespace) {
1931 self.peek_next_count_keywords(NonZeroUsize::new(2).unwrap())
1933 .nth(1)
1934 } else {
1935 self.peek_next_keyword()
1937 };
1938
1939 if let Some(Token::ErrorKeyword) = next_kw {
1940 self.parse_on_error_statement();
1941 } else {
1942 let peek_start = if self.at_token(Token::Whitespace) {
1945 2
1946 } else {
1947 1
1948 };
1949 let keywords: Vec<Token> = self
1950 .peek_next_count_keywords(NonZeroUsize::new(20).unwrap())
1951 .skip(peek_start)
1952 .collect();
1953
1954 let has_goto = keywords.contains(&Token::GotoKeyword);
1955 let has_gosub = keywords.contains(&Token::GoSubKeyword);
1956
1957 if has_goto {
1958 self.parse_on_goto_statement();
1959 } else if has_gosub {
1960 self.parse_on_gosub_statement();
1961 } else {
1962 self.parse_on_error_statement();
1964 }
1965 }
1966 }
1967 _ => {}
1968 }
1969 }
1970
1971 fn is_variable_declaration_keyword(&self) -> bool {
1974 let token = if self.at_token(Token::Whitespace) {
1975 self.peek_next_keyword()
1976 } else {
1977 self.current_token().copied()
1978 };
1979
1980 matches!(token, Some(Token::ReDimKeyword | Token::EraseKeyword))
1981 }
1982
1983 #[allow(clippy::needless_continue)] fn is_object_statement(&self) -> bool {
1993 if !self.at_token(Token::ObjectKeyword) {
1995 return false;
1996 }
1997
1998 let mut found_equals = false;
2001 for (_text, token) in self.tokens.iter().skip(self.pos + 1) {
2002 match token {
2003 Token::Whitespace => continue,
2005 Token::EqualityOperator if !found_equals => {
2006 found_equals = true;
2007 }
2008 Token::StringLiteral | Token::MultiplicationOperator if found_equals => {
2010 return true;
2014 }
2015 _ if found_equals => return false,
2017 Token::Newline | Token::EndOfLineComment | Token::RemComment => {
2019 return false;
2020 }
2021 _ => return false,
2022 }
2023 }
2024 false
2025 }
2026
2027 fn parse_array_statement(&mut self) {
2029 let token = if self.at_token(Token::Whitespace) {
2030 self.peek_next_keyword()
2031 } else {
2032 self.current_token().copied()
2033 };
2034
2035 match token {
2036 Some(Token::ReDimKeyword) => {
2037 self.parse_redim_statement();
2038 }
2039 Some(Token::EraseKeyword) => {
2040 self.parse_erase_statement();
2041 }
2042 _ => {}
2043 }
2044 }
2045
2046 pub(crate) fn parse_statement_list<F>(&mut self, stop_conditions: F)
2055 where
2056 F: Fn(&Parser) -> bool,
2057 {
2058 self.builder.start_node(SyntaxKind::StatementList.to_raw());
2062
2063 while !self.is_at_end() {
2064 if stop_conditions(self) {
2065 break;
2066 }
2067
2068 if self.is_control_flow_keyword() {
2070 self.parse_control_flow_statement();
2071 continue;
2072 }
2073
2074 if self.is_library_statement_keyword() {
2076 self.parse_library_statement();
2077 continue;
2078 }
2079
2080 if self.is_variable_declaration_keyword() {
2082 self.parse_array_statement();
2083 continue;
2084 }
2085
2086 if self.is_statement_keyword() {
2088 self.parse_statement();
2089 continue;
2090 }
2091
2092 match self.current_token() {
2094 Some(
2098 Token::Whitespace
2099 | Token::Newline
2100 | Token::EndOfLineComment
2101 | Token::RemComment,
2102 ) => {
2103 self.consume_token();
2104 }
2105 Some(
2107 Token::DimKeyword
2108 | Token::PrivateKeyword
2109 | Token::PublicKeyword
2110 | Token::ConstKeyword
2111 | Token::StaticKeyword,
2112 ) => {
2113 self.parse_dim();
2114 }
2115 _ => {
2117 if self.is_at_label() {
2119 self.parse_label_statement();
2120 } else if self.at_token(Token::LetKeyword)
2122 || (self.at_token(Token::Whitespace)
2123 && self.peek_next_keyword() == Some(Token::LetKeyword))
2124 {
2125 self.parse_let_statement();
2126 } else if self.is_at_assignment() {
2128 self.parse_assignment_statement();
2129 } else if self.is_at_procedure_call() {
2131 self.parse_procedure_call();
2132 } else {
2133 self.consume_token_as_unknown();
2134 }
2135 }
2136 }
2137 }
2138 self.builder.finish_node(); }
2140}
2141
2142#[cfg(test)]
2143mod tests {
2144 use super::Parser;
2145 use crate::parsers::cst::{
2146 ControlKind, Creatable, Exposed, FormRoot, NameSpace, ObjectReference, PreDeclaredID,
2147 };
2148 use crate::*;
2149
2150 use assert_matches::assert_matches;
2151
2152 #[test]
2153 fn parse_single_quote_comment() {
2154 let code = "' This is a comment\nSub Main()\n";
2155
2156 let mut source_stream = SourceStream::new("test.bas", code);
2157 let result = tokenize(&mut source_stream);
2158 let (token_stream_opt, _failures) = result.unpack();
2159
2160 let token_stream = token_stream_opt.expect("Tokenization failed");
2161 let cst = parse(token_stream);
2162
2163 assert_eq!(cst.root_kind(), SyntaxKind::Root);
2164 assert_eq!(cst.child_count(), 3); assert!(cst.text().contains("' This is a comment"));
2167 assert!(cst.text().contains("Sub Main()"));
2168
2169 assert!(cst.contains_kind(SyntaxKind::EndOfLineComment));
2171 assert!(cst.contains_kind(SyntaxKind::SubStatement));
2172
2173 let first = cst.first_child().expect("Expected first child");
2174 assert_eq!(first.kind(), SyntaxKind::EndOfLineComment);
2175 assert!(first.is_token());
2176 }
2177
2178 #[test]
2179 fn syntax_kind_conversions() {
2180 assert_eq!(
2182 SyntaxKind::from(Token::FunctionKeyword),
2183 SyntaxKind::FunctionKeyword
2184 );
2185 assert_eq!(SyntaxKind::from(Token::IfKeyword), SyntaxKind::IfKeyword);
2186 assert_eq!(SyntaxKind::from(Token::ForKeyword), SyntaxKind::ForKeyword);
2187
2188 assert_eq!(
2190 SyntaxKind::from(Token::AdditionOperator),
2191 SyntaxKind::AdditionOperator
2192 );
2193 assert_eq!(
2194 SyntaxKind::from(Token::EqualityOperator),
2195 SyntaxKind::EqualityOperator
2196 );
2197
2198 assert_eq!(
2200 SyntaxKind::from(Token::StringLiteral),
2201 SyntaxKind::StringLiteral
2202 );
2203 assert_eq!(
2204 SyntaxKind::from(Token::IntegerLiteral),
2205 SyntaxKind::IntegerLiteral
2206 );
2207 assert_eq!(
2208 SyntaxKind::from(Token::LongLiteral),
2209 SyntaxKind::LongLiteral
2210 );
2211 assert_eq!(
2212 SyntaxKind::from(Token::SingleLiteral),
2213 SyntaxKind::SingleLiteral
2214 );
2215 assert_eq!(
2216 SyntaxKind::from(Token::DoubleLiteral),
2217 SyntaxKind::DoubleLiteral
2218 );
2219 assert_eq!(
2220 SyntaxKind::from(Token::DateTimeLiteral),
2221 SyntaxKind::DateLiteral
2222 );
2223 }
2224
2225 #[test]
2226 fn parse_empty_stream() {
2227 let source = "";
2228 let (cst_opt, _failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
2229 let cst = cst_opt.expect("Failed to parse source");
2230
2231 assert_eq!(cst.root_kind(), SyntaxKind::Root);
2232 assert_eq!(cst.child_count(), 0);
2233 }
2234
2235 #[test]
2236 fn parse_rem_comment() {
2237 let source = "REM This is a REM comment\nSub Test()\nEnd Sub\n";
2238 let (cst_opt, _failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
2239 let cst = cst_opt.expect("Failed to parse source");
2240
2241 assert_eq!(cst.root_kind(), SyntaxKind::Root);
2242 assert_eq!(cst.child_count(), 3); assert!(cst.text().contains("REM This is a REM comment"));
2245 assert!(cst.text().contains("Sub Test()"));
2246
2247 let debug = cst.debug_tree();
2249 assert!(debug.contains("RemComment"));
2250 }
2251
2252 #[test]
2253 fn parse_mixed_comments() {
2254 let source = "' Single quote comment\nREM REM comment\nSub Test()\nEnd Sub\n";
2255 let (cst_opt, _failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
2256 let cst = cst_opt.expect("Failed to parse source");
2257
2258 assert_eq!(cst.root_kind(), SyntaxKind::Root);
2259 assert_eq!(cst.child_count(), 5);
2261 assert!(cst.text().contains("' Single quote comment"));
2262 assert!(cst.text().contains("REM REM comment"));
2263
2264 let children = cst.children();
2266 assert_eq!(children[0].kind(), SyntaxKind::EndOfLineComment);
2267 assert_eq!(children[1].kind(), SyntaxKind::Newline);
2268 assert_eq!(children[2].kind(), SyntaxKind::RemComment);
2269 assert_eq!(children[3].kind(), SyntaxKind::Newline);
2270 assert_eq!(children[4].kind(), SyntaxKind::SubStatement);
2271
2272 assert!(cst.contains_kind(SyntaxKind::EndOfLineComment));
2273 assert!(cst.contains_kind(SyntaxKind::RemComment));
2274 }
2275
2276 #[test]
2277 fn cst_with_comments() {
2278 let source = "' This is a comment\nSub Main()\n";
2279 let (cst_opt, _failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
2280 let cst = cst_opt.expect("Failed to parse source");
2281
2282 assert_eq!(cst.child_count(), 3);
2284 assert!(cst.text().contains("' This is a comment"));
2285 assert!(cst.text().contains("Sub Main()"));
2286 }
2287
2288 #[test]
2289 fn cst_serializable_tree() {
2290 let source = "Sub Test()\nEnd Sub\n";
2291 let (cst_opt, _failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
2292 let cst = cst_opt.expect("Failed to parse source");
2293
2294 let serializable = cst.to_serializable();
2296
2297 assert_eq!(serializable.root.kind(), SyntaxKind::Root);
2299 assert!(!serializable.root.is_token());
2300 assert_eq!(serializable.root.children().len(), 1);
2301 assert_eq!(
2302 serializable.root.children()[0].kind(),
2303 SyntaxKind::SubStatement
2304 );
2305
2306 }
2309
2310 #[test]
2311 fn cst_serializable_with_insta() {
2312 let source = "Dim x As Integer\n";
2313 let (cst_opt, _failures) = ConcreteSyntaxTree::from_text("test.bas", source).unpack();
2314 let cst = cst_opt.expect("Failed to parse source");
2315 let serializable = cst.to_serializable();
2316
2317 assert!(!serializable.root.children().is_empty());
2322 }
2323
2324 #[test]
2325 fn parser_mode_full_cst_default() {
2326 let source = "Sub Test()\nEnd Sub\n";
2327 let mut stream = SourceStream::new("test.bas".to_string(), source);
2328 let (token_stream_opt, _) = tokenize(&mut stream).unpack();
2329 let token_stream = token_stream_opt.expect("Tokenization failed");
2330
2331 let parser = Parser::new(token_stream);
2332 assert_eq!(parser.pos, 0);
2334 }
2335
2336 #[test]
2337 fn parser_mode_direct_extraction() {
2338 let source = "Sub Test()\nEnd Sub\n";
2339 let mut stream = SourceStream::new("test.bas".to_string(), source);
2340 let (token_stream_opt, _) = tokenize(&mut stream).unpack();
2341 let token_stream = token_stream_opt.expect("Tokenization failed");
2342 let tokens = token_stream.into_tokens();
2343
2344 let parser = Parser::new_direct_extraction(tokens, 0);
2345 assert_eq!(parser.pos, 0);
2346 }
2347
2348 #[test]
2349 fn parser_constructors_preserve_tokens() {
2350 let source = "VERSION 5.00\n";
2351 let mut stream = SourceStream::new("test.frm".to_string(), source);
2352 let (token_stream_opt, _) = tokenize(&mut stream).unpack();
2353 let token_stream = token_stream_opt.expect("Tokenization failed");
2354
2355 let tokens_vec = token_stream.into_tokens();
2356 let token_count = tokens_vec.len();
2357
2358 let parser = Parser::new_direct_extraction(tokens_vec, 0);
2359 assert_eq!(parser.tokens.len(), token_count);
2360 assert!(parser.tokens[0].1 == Token::VersionKeyword);
2361 }
2362
2363 #[test]
2364 fn parser_new_with_position() {
2365 let source = "VERSION 5.00\nSub Test()\nEnd Sub\n";
2366 let mut stream = SourceStream::new("test.bas".to_string(), source);
2367 let (token_stream_opt, _) = tokenize(&mut stream).unpack();
2368 let token_stream = token_stream_opt.expect("Tokenization failed");
2369 let tokens = token_stream.into_tokens();
2370
2371 let parser = Parser::new_direct_extraction(tokens, 3);
2373 assert_eq!(parser.pos, 3);
2374 }
2375
2376 #[test]
2377 fn parse_version_direct_with_version() {
2378 let source = "VERSION 5.00\nSub Test()\nEnd Sub\n";
2379 let mut stream = SourceStream::new("test.frm".to_string(), source);
2380 let (token_stream_opt, _) = tokenize(&mut stream).unpack();
2381 let token_stream = token_stream_opt.expect("Tokenization failed");
2382 let tokens = token_stream.into_tokens();
2383
2384 let mut parser = Parser::new_direct_extraction(tokens, 0);
2385 let (version_opt, failures) = parser.parse_version_direct().unpack();
2386
2387 assert!(version_opt.is_some());
2388 let version = version_opt.expect("Expected version to be parsed");
2389 assert_eq!(version.major, 5);
2390 assert_eq!(version.minor, 0);
2391 assert!(failures.is_empty());
2392 }
2393
2394 #[test]
2395 fn parse_version_direct_without_version() {
2396 let source = "Sub Test()\nEnd Sub\n";
2397 let mut stream = SourceStream::new("test.bas".to_string(), source);
2398 let (token_stream_opt, _) = tokenize(&mut stream).unpack();
2399 let token_stream = token_stream_opt.expect("Tokenization failed");
2400 let tokens = token_stream.into_tokens();
2401
2402 let mut parser = Parser::new_direct_extraction(tokens, 0);
2403 let (version_opt, failures) = parser.parse_version_direct().unpack();
2404
2405 assert!(version_opt.is_none());
2406 assert!(failures.is_empty());
2407 }
2408
2409 #[test]
2410 fn parse_version_direct_with_class_keyword() {
2411 let source = "VERSION 1.0 CLASS\nSub Test()\nEnd Sub\n";
2412 let mut stream = SourceStream::new("test.cls".to_string(), source);
2413 let (token_stream_opt, _) = tokenize(&mut stream).unpack();
2414 let token_stream = token_stream_opt.expect("Tokenization failed");
2415 let tokens = token_stream.into_tokens();
2416
2417 let mut parser = Parser::new_direct_extraction(tokens, 0);
2418 let (version_opt, failures) = parser.parse_version_direct().unpack();
2419
2420 assert!(version_opt.is_some());
2421 let version = version_opt.expect("Expected version to be parsed");
2422 assert_eq!(version.major, 1);
2423 assert_eq!(version.minor, 0);
2424 assert!(failures.is_empty());
2425 }
2426
2427 #[test]
2428 fn parse_version_direct_version_100() {
2429 let source = "VERSION 1.00\n";
2430 let mut stream = SourceStream::new("test.cls".to_string(), source);
2431 let (token_stream_opt, _) = tokenize(&mut stream).unpack();
2432 let token_stream = token_stream_opt.expect("Tokenization failed");
2433 let tokens = token_stream.into_tokens();
2434
2435 let mut parser = Parser::new_direct_extraction(tokens, 0);
2436 let (version_opt, _failures) = parser.parse_version_direct().unpack();
2437
2438 assert!(version_opt.is_some());
2439 let version = version_opt.expect("Expected version to be parsed");
2440 assert_eq!(version.major, 1);
2441 assert_eq!(version.minor, 0);
2442 }
2443
2444 #[test]
2445 fn parse_version_direct_with_whitespace() {
2446 let source = " VERSION 5.00 \nSub Test()\n";
2447 let mut stream = SourceStream::new("test.frm".to_string(), source);
2448 let (token_stream_opt, _) = tokenize(&mut stream).unpack();
2449 let token_stream = token_stream_opt.expect("Tokenization failed");
2450 let tokens = token_stream.into_tokens();
2451
2452 let mut parser = Parser::new_direct_extraction(tokens, 0);
2453 let (version_opt, _failures) = parser.parse_version_direct().unpack();
2454
2455 assert!(version_opt.is_some());
2456 let version = version_opt.expect("Expected version to be parsed");
2457 assert_eq!(version.major, 5);
2458 assert_eq!(version.minor, 0);
2459 }
2460
2461 #[test]
2462 fn parse_version_direct_position_advances() {
2463 let source = "VERSION 5.00\nBegin VB.Form Form1\nEnd\n";
2464 let mut stream = SourceStream::new("test.frm".to_string(), source);
2465 let (token_stream_opt, _) = tokenize(&mut stream).unpack();
2466 let token_stream = token_stream_opt.expect("Tokenization failed");
2467 let tokens = token_stream.into_tokens();
2468
2469 let mut parser = Parser::new_direct_extraction(tokens, 0);
2470 let initial_pos = parser.pos;
2471 let _result = parser.parse_version_direct();
2472
2473 assert!(parser.pos > initial_pos);
2475
2476 assert_eq!(parser.current_token(), Some(&Token::BeginKeyword));
2478 }
2479
2480 #[test]
2481 fn parse_version_direct_accuracy() {
2482 let test_cases = vec![
2483 ("VERSION 5.00\n", Some((5, 0))),
2484 ("VERSION 1.0\n", Some((1, 0))),
2485 ("VERSION 6.00 CLASS\n", Some((6, 0))),
2486 ("VERSION 4.00\n", Some((4, 0))),
2487 ("Sub Test()\n", None), ];
2489
2490 for (source, expected) in test_cases {
2491 let mut stream = SourceStream::new("test.vb".to_string(), source);
2492 let (token_stream_opt, _) = tokenize(&mut stream).unpack();
2493 let token_stream = token_stream_opt.expect("Tokenization failed");
2494 let tokens = token_stream.into_tokens();
2495
2496 let mut parser = Parser::new_direct_extraction(tokens, 0);
2497 let (version_opt, _failures) = parser.parse_version_direct().unpack();
2498
2499 match expected {
2500 Some((major, minor)) => {
2501 assert!(version_opt.is_some(), "Expected version for: {source}");
2502 let version = version_opt.expect("Expected version to be parsed");
2503 assert_eq!(version.major, major, "Major mismatch for: {source}");
2504 assert_eq!(version.minor, minor, "Minor mismatch for: {source}");
2505 }
2506 None => {
2507 assert!(version_opt.is_none(), "Expected no version for: {source}");
2508 }
2509 }
2510 }
2511 }
2512
2513 #[test]
2514 fn parse_control_type_direct_simple() {
2515 let source = "VB.Form Form1\n";
2516 let mut stream = SourceStream::new("test.frm".to_string(), source);
2517 let (token_stream_opt, _) = tokenize(&mut stream).unpack();
2518 let token_stream = token_stream_opt.expect("Tokenization failed");
2519 let tokens = token_stream.into_tokens();
2520
2521 let mut parser = Parser::new_direct_extraction(tokens, 0);
2522 let control_type = parser.parse_control_type_direct();
2523
2524 assert_eq!(control_type, "VB.Form");
2525 }
2526
2527 #[test]
2528 fn parse_control_name_direct_simple() {
2529 let source = "Form1 \n";
2530 let mut stream = SourceStream::new("test.frm".to_string(), source);
2531 let (token_stream_opt, _) = tokenize(&mut stream).unpack();
2532 let token_stream = token_stream_opt.expect("Tokenization failed");
2533 let tokens = token_stream.into_tokens();
2534
2535 let mut parser = Parser::new_direct_extraction(tokens, 0);
2536 let control_name = parser.parse_control_name_direct();
2537
2538 assert_eq!(control_name, "Form1");
2539 }
2540
2541 #[test]
2542 fn parse_property_direct_simple() {
2543 let source = "Caption = \"Hello World\"\n";
2544 let mut stream = SourceStream::new("test.frm".to_string(), source);
2545 let (token_stream_opt, _) = tokenize(&mut stream).unpack();
2546 let token_stream = token_stream_opt.expect("Tokenization failed");
2547 let tokens = token_stream.into_tokens();
2548
2549 let mut parser = Parser::new_direct_extraction(tokens, 0);
2550 let property = parser.parse_property_direct();
2551
2552 assert!(property.is_some());
2553 let (key, value) = property.expect("Expected property to be parsed");
2554 assert_eq!(key, "Caption");
2555 assert_eq!(value, "\"Hello World\"");
2556 }
2557
2558 #[test]
2559 fn parse_properties_block_to_control_simple_form() {
2560 let source = r#"Begin VB.Form Form1
2561 Caption = "Test Form"
2562 ClientHeight = 3000
2563 ClientWidth = 4000
2564End
2565"#;
2566 let mut stream = SourceStream::new("test.frm".to_string(), source);
2567 let (token_stream_opt, _) = tokenize(&mut stream).unpack();
2568 let token_stream = token_stream_opt.expect("Tokenization failed");
2569 let tokens = token_stream.into_tokens();
2570
2571 let mut parser = Parser::new_direct_extraction(tokens, 0);
2572 let (control_opt, failures) = parser.parse_properties_block_to_form_root().unpack();
2573
2574 assert!(failures.is_empty(), "Expected no failures");
2575 assert!(control_opt.is_some(), "Expected form root to be parsed");
2576
2577 let form_root = control_opt.expect("Expected form root to be parsed");
2578 assert_eq!(form_root.name(), "Form1");
2579
2580 assert!(form_root.is_form());
2582 }
2583
2584 #[test]
2585 fn parse_properties_block_to_control_command_button() {
2586 let source = r#"Begin VB.CommandButton Command1
2587 Caption = "Click Me"
2588 Height = 495
2589 Width = 1215
2590End
2591"#;
2592 let mut stream = SourceStream::new("test.frm".to_string(), source);
2593 let (token_stream_opt, _) = tokenize(&mut stream).unpack();
2594 let token_stream = token_stream_opt.expect("Tokenization failed");
2595 let tokens = token_stream.into_tokens();
2596
2597 let mut parser = Parser::new_direct_extraction(tokens, 0);
2598 let (control_opt, failures) = parser.parse_properties_block_to_control().unpack();
2599
2600 assert!(failures.is_empty());
2601 assert!(control_opt.is_some());
2602
2603 let control = control_opt.expect("Expected control to be parsed");
2604 assert_eq!(control.name(), "Command1");
2605 assert_matches!(control.kind(), ControlKind::CommandButton { .. });
2606 }
2607
2608 #[test]
2609 fn parse_properties_block_to_control_textbox() {
2610 let source = r#"Begin VB.TextBox Text1
2611 Text = "Initial Text"
2612 Height = 300
2613 Width = 2000
2614End
2615"#;
2616 let mut stream = SourceStream::new("test.frm".to_string(), source);
2617 let (token_stream_opt, _) = tokenize(&mut stream).unpack();
2618 let token_stream = token_stream_opt.expect("Tokenization failed");
2619 let tokens = token_stream.into_tokens();
2620
2621 let mut parser = Parser::new_direct_extraction(tokens, 0);
2622 let (control_opt, _failures) = parser.parse_properties_block_to_control().unpack();
2623
2624 assert!(control_opt.is_some());
2625 let control = control_opt.expect("Expected control to be parsed");
2626 assert_eq!(control.name(), "Text1");
2627 assert_matches!(control.kind(), ControlKind::TextBox { .. });
2628 }
2629
2630 #[test]
2631 fn parse_properties_block_without_begin() {
2632 let source = "Caption = \"Test\"\nEnd\n";
2633 let mut stream = SourceStream::new("test.frm".to_string(), source);
2634 let (token_stream_opt, _) = tokenize(&mut stream).unpack();
2635 let token_stream = token_stream_opt.expect("Tokenization failed");
2636 let tokens = token_stream.into_tokens();
2637
2638 let mut parser = Parser::new_direct_extraction(tokens, 0);
2639 let (control_opt, _failures) = parser.parse_properties_block_to_control().unpack();
2640
2641 assert!(control_opt.is_none());
2643 }
2644
2645 #[test]
2646 fn parse_form_with_nested_control() {
2647 let source = r#"Begin VB.Form Form1
2648 Caption = "Main Form"
2649 Begin VB.CommandButton Command1
2650 Caption = "Click Me"
2651 Height = 400
2652 End
2653End
2654"#;
2655 let mut stream = SourceStream::new("test.frm".to_string(), source);
2656 let (token_stream_opt, _) = tokenize(&mut stream).unpack();
2657 let token_stream = token_stream_opt.expect("Tokenization failed");
2658 let tokens = token_stream.into_tokens();
2659
2660 let mut parser = Parser::new_direct_extraction(tokens, 0);
2661 let (form_root_opt, failures) = parser.parse_properties_block_to_form_root().unpack();
2662
2663 assert!(failures.is_empty(), "Should have no failures");
2664 assert!(form_root_opt.is_some());
2665 let form_root = form_root_opt.expect("Expected form root to be parsed");
2666 assert_eq!(form_root.name(), "Form1");
2667
2668 if let FormRoot::Form(form) = &form_root {
2670 assert_eq!(form.controls.len(), 1);
2671 assert_eq!(form.controls[0].name(), "Command1");
2672 assert_matches!(form.controls[0].kind(), ControlKind::CommandButton { .. });
2673 } else {
2674 panic!("Expected Form");
2675 }
2676 }
2677
2678 #[test]
2679 fn parse_frame_with_multiple_nested_controls() {
2680 let source = r#"Begin VB.Frame Frame1
2681 Caption = "Options"
2682 Begin VB.CheckBox Check1
2683 Caption = "Option 1"
2684 End
2685 Begin VB.CheckBox Check2
2686 Caption = "Option 2"
2687 End
2688End
2689"#;
2690 let mut stream = SourceStream::new("test.frm".to_string(), source);
2691 let (token_stream_opt, _) = tokenize(&mut stream).unpack();
2692 let token_stream = token_stream_opt.expect("Tokenization failed");
2693 let tokens = token_stream.into_tokens();
2694
2695 let mut parser = Parser::new_direct_extraction(tokens, 0);
2696 let (control_opt, failures) = parser.parse_properties_block_to_control().unpack();
2697
2698 assert!(failures.is_empty());
2699 assert!(control_opt.is_some());
2700 let control = control_opt.expect("Expected control to be parsed");
2701 assert_eq!(control.name(), "Frame1");
2702
2703 if let ControlKind::Frame { controls, .. } = control.kind() {
2705 assert_eq!(controls.len(), 2);
2706 assert_eq!(controls[0].name(), "Check1");
2707 assert_eq!(controls[1].name(), "Check2");
2708 } else {
2709 panic!("Expected Frame control kind");
2710 }
2711 }
2712
2713 #[test]
2714 fn parse_control_with_property_group() {
2715 let source = r#"Begin VB.CommandButton Command1
2716 Caption = "Button"
2717 BeginProperty Font
2718 Name = "Arial"
2719 Size = 12
2720 EndProperty
2721End
2722"#;
2723 let mut stream = SourceStream::new("test.frm".to_string(), source);
2724 let (token_stream_opt, _) = tokenize(&mut stream).unpack();
2725 let token_stream = token_stream_opt.expect("Tokenization failed");
2726 let tokens = token_stream.into_tokens();
2727
2728 let mut parser = Parser::new_direct_extraction(tokens, 0);
2729 let (control_opt, failures) = parser.parse_properties_block_to_control().unpack();
2730
2731 assert!(failures.is_empty());
2732 assert!(control_opt.is_some());
2733 let control = control_opt.expect("Expected control to be parsed");
2734 assert_eq!(control.name(), "Command1");
2735
2736 assert_matches!(control.kind(), ControlKind::CommandButton { .. });
2739 }
2740
2741 #[test]
2742 fn parse_custom_control_with_property_group() {
2743 let source = r#"Begin MSComctlLib.TreeView TreeView1
2744 BeginProperty Font {0BE35203-8F91-11CE-9DE3-00AA004BB851}
2745 Name = "MS Sans Serif"
2746 Size = 8.25
2747 Charset = 0
2748 EndProperty
2749 Caption = "Tree"
2750End
2751"#;
2752 let mut stream = SourceStream::new("test.frm".to_string(), source);
2753 let (token_stream_opt, _) = tokenize(&mut stream).unpack();
2754 let token_stream = token_stream_opt.expect("Tokenization failed");
2755 let tokens = token_stream.into_tokens();
2756
2757 let mut parser = Parser::new_direct_extraction(tokens, 0);
2758 let (control_opt, failures) = parser.parse_properties_block_to_control().unpack();
2759
2760 assert!(failures.is_empty());
2761 assert!(control_opt.is_some());
2762 let control = control_opt.expect("Expected control to be parsed");
2763 assert_eq!(control.name(), "TreeView1");
2764
2765 if let ControlKind::Custom {
2767 property_groups, ..
2768 } = control.kind()
2769 {
2770 assert_eq!(property_groups.len(), 1);
2771 assert_eq!(property_groups[0].name, "Font");
2772 assert!(property_groups[0].guid.is_some());
2773 } else {
2774 panic!("Expected Custom control kind");
2775 }
2776 }
2777
2778 #[test]
2779 fn parse_simple_object_statement() {
2780 let source = r#"Object = "{12345678-1234-1234-1234-123456789ABC}#1.0#0"; "MyLib.dll""#;
2781 let mut stream = SourceStream::new("test.frm".to_string(), source);
2782 let (token_stream_opt, _) = tokenize(&mut stream).unpack();
2783 let token_stream = token_stream_opt.expect("Tokenization failed");
2784 let tokens = token_stream.into_tokens();
2785
2786 let mut parser = Parser::new_direct_extraction(tokens, 0);
2787 let objects = parser.parse_objects_direct();
2788
2789 assert_eq!(objects.len(), 1);
2790 match &objects[0] {
2791 ObjectReference::Compiled {
2792 uuid,
2793 version,
2794 unknown1,
2795 file_name,
2796 } => {
2797 assert_eq!(
2798 uuid.to_string().to_uppercase(),
2799 "12345678-1234-1234-1234-123456789ABC"
2800 );
2801 assert_eq!(version, "1.0");
2802 assert_eq!(unknown1, "0");
2803 assert_eq!(file_name, "MyLib.dll");
2804 }
2805 ObjectReference::Project { .. } => {
2806 panic!("Expected Compiled object reference")
2807 }
2808 }
2809 }
2810
2811 #[test]
2812 fn parse_multiple_object_statements() {
2813 let source = r#"Object = "{AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAAA}#1.0#0"; "Lib1.dll"
2814Object = "{BBBBBBBB-BBBB-BBBB-BBBB-BBBBBBBBBBBB}#2.0#1"; "Lib2.ocx"
2815"#;
2816 let mut stream = SourceStream::new("test.frm".to_string(), source);
2817 let (token_stream_opt, _) = tokenize(&mut stream).unpack();
2818 let token_stream = token_stream_opt.expect("Tokenization failed");
2819 let tokens = token_stream.into_tokens();
2820
2821 let mut parser = Parser::new_direct_extraction(tokens, 0);
2822 let objects = parser.parse_objects_direct();
2823
2824 assert_eq!(objects.len(), 2);
2825
2826 match &objects[0] {
2827 ObjectReference::Compiled { file_name, .. } => {
2828 assert_eq!(file_name, "Lib1.dll");
2829 }
2830 ObjectReference::Project { .. } => {
2831 panic!("Expected Compiled object reference")
2832 }
2833 }
2834
2835 match &objects[1] {
2836 ObjectReference::Compiled { file_name, .. } => {
2837 assert_eq!(file_name, "Lib2.ocx");
2838 }
2839 ObjectReference::Project { .. } => {
2840 panic!("Expected Compiled object reference")
2841 }
2842 }
2843 }
2844
2845 #[test]
2846 fn parse_embedded_object_statement() {
2847 let source = r#"Object = *\G{87654321-4321-4321-4321-CBA987654321}#3.0#5; "Embedded.ocx""#;
2848 let mut stream = SourceStream::new("test.frm".to_string(), source);
2849 let (token_stream_opt, _) = tokenize(&mut stream).unpack();
2850 let token_stream = token_stream_opt.expect("Tokenization failed");
2851 let tokens = token_stream.into_tokens();
2852
2853 let mut parser = Parser::new_direct_extraction(tokens, 0);
2854 let objects = parser.parse_objects_direct();
2855
2856 assert_eq!(objects.len(), 1);
2857 match &objects[0] {
2858 ObjectReference::Compiled {
2859 uuid,
2860 version,
2861 file_name,
2862 ..
2863 } => {
2864 assert_eq!(
2865 uuid.to_string().to_uppercase(),
2866 "87654321-4321-4321-4321-CBA987654321"
2867 );
2868 assert_eq!(version, "3.0");
2869 assert_eq!(file_name, "Embedded.ocx");
2870 }
2871 ObjectReference::Project { .. } => {
2872 panic!("Expected Compiled object reference")
2873 }
2874 }
2875 }
2876
2877 #[test]
2878 fn parse_nested_property_groups() {
2879 use either::Either;
2880
2881 let source = r#"Begin Custom.Control Ctrl1
2882 BeginProperty Outer
2883 Value1 = "Test"
2884 BeginProperty Inner
2885 Value2 = "Nested"
2886 EndProperty
2887 EndProperty
2888End
2889"#;
2890 let mut stream = SourceStream::new("test.frm".to_string(), source);
2891 let (token_stream_opt, _) = tokenize(&mut stream).unpack();
2892 let token_stream = token_stream_opt.expect("Tokenization failed");
2893 let tokens = token_stream.into_tokens();
2894
2895 let mut parser = Parser::new_direct_extraction(tokens, 0);
2896 let (control_opt, failures) = parser.parse_properties_block_to_control().unpack();
2897
2898 assert!(failures.is_empty());
2899 assert!(control_opt.is_some());
2900 let control = control_opt.expect("Expected control to be parsed");
2901
2902 if let ControlKind::Custom {
2904 property_groups, ..
2905 } = control.kind()
2906 {
2907 assert_eq!(property_groups.len(), 1);
2908 assert_eq!(property_groups[0].name, "Outer");
2909
2910 if let Some(Either::Right(inner)) = property_groups[0].properties.get("Inner") {
2913 assert_eq!(inner.name, "Inner");
2914 } else {
2915 panic!("Expected nested Inner property group");
2916 }
2917 } else {
2918 panic!("Expected Custom control kind");
2919 }
2920 }
2921
2922 #[test]
2923 fn parse_deeply_nested_controls() {
2924 let source = r#"Begin VB.Form Form1
2925 Caption = "Outer"
2926 Begin VB.PictureBox Picture1
2927 Begin VB.Frame Frame1
2928 Begin VB.Label Label1
2929 Caption = "Deep"
2930 End
2931 End
2932 End
2933End
2934"#;
2935 let mut stream = SourceStream::new("test.frm".to_string(), source);
2936 let (token_stream_opt, _) = tokenize(&mut stream).unpack();
2937 let token_stream = token_stream_opt.expect("Tokenization failed");
2938 let tokens = token_stream.into_tokens();
2939
2940 let mut parser = Parser::new_direct_extraction(tokens, 0);
2941 let (form_root_opt, failures) = parser.parse_properties_block_to_form_root().unpack();
2942
2943 assert!(failures.is_empty());
2944 assert!(form_root_opt.is_some());
2945 let form_root = form_root_opt.expect("Expected form root to be parsed");
2946
2947 if let FormRoot::Form(form) = &form_root {
2949 assert_eq!(form.controls.len(), 1);
2950 if let ControlKind::PictureBox { controls, .. } = form.controls[0].kind() {
2951 assert_eq!(controls.len(), 1);
2952 if let ControlKind::Frame { controls, .. } = controls[0].kind() {
2953 assert_eq!(controls.len(), 1);
2954 assert_eq!(controls[0].name(), "Label1");
2955 } else {
2956 panic!("Expected Frame");
2957 }
2958 } else {
2959 panic!("Expected PictureBox");
2960 }
2961 } else {
2962 panic!("Expected Form");
2963 }
2964 }
2965
2966 #[test]
2967 fn parse_simple_string_attribute() {
2968 let source = r#"Attribute VB_Name = "Form1"
2969"#;
2970 let mut stream = SourceStream::new("test.frm".to_string(), source);
2971 let (token_stream_opt, _) = tokenize(&mut stream).unpack();
2972 let token_stream = token_stream_opt.expect("Tokenization failed");
2973 let tokens = token_stream.into_tokens();
2974
2975 let mut parser = Parser::new_direct_extraction(tokens, 0);
2976 let attrs = parser.parse_attributes_direct();
2977
2978 assert_eq!(attrs.name, "Form1");
2979 assert_eq!(attrs.global_name_space, NameSpace::Local);
2980 assert_eq!(attrs.creatable, Creatable::True);
2981 assert_eq!(attrs.predeclared_id, PreDeclaredID::False);
2982 assert_eq!(attrs.exposed, Exposed::False);
2983 assert_eq!(attrs.description, None);
2984 }
2985
2986 #[test]
2987 fn parse_boolean_attributes() {
2988 let source = r"Attribute VB_GlobalNameSpace = False
2989Attribute VB_Creatable = True
2990Attribute VB_PredeclaredId = True
2991Attribute VB_Exposed = False
2992";
2993 let mut stream = SourceStream::new("test.frm".to_string(), source);
2994 let (token_stream_opt, _) = tokenize(&mut stream).unpack();
2995 let token_stream = token_stream_opt.expect("Tokenization failed");
2996 let tokens = token_stream.into_tokens();
2997
2998 let mut parser = Parser::new_direct_extraction(tokens, 0);
2999 let attrs = parser.parse_attributes_direct();
3000
3001 assert_eq!(attrs.global_name_space, NameSpace::Local);
3002 assert_eq!(attrs.creatable, Creatable::True);
3003 assert_eq!(attrs.predeclared_id, PreDeclaredID::True);
3004 assert_eq!(attrs.exposed, Exposed::False);
3005 }
3006
3007 #[test]
3008 fn parse_numeric_attribute() {
3009 let source = r"Attribute VB_PredeclaredId = -1
3010";
3011 let mut stream = SourceStream::new("test.frm".to_string(), source);
3012 let (token_stream_opt, _) = tokenize(&mut stream).unpack();
3013 let token_stream = token_stream_opt.expect("Tokenization failed");
3014 let tokens = token_stream.into_tokens();
3015
3016 let mut parser = Parser::new_direct_extraction(tokens, 0);
3017 let attrs = parser.parse_attributes_direct();
3018
3019 assert_eq!(attrs.predeclared_id, PreDeclaredID::True);
3021 }
3022
3023 #[test]
3024 fn parse_multiple_attributes() {
3025 let source = r#"Attribute VB_Name = "MyForm"
3026Attribute VB_GlobalNameSpace = False
3027Attribute VB_Creatable = False
3028Attribute VB_PredeclaredId = True
3029Attribute VB_Exposed = False
3030Attribute VB_Description = "This is a test form"
3031"#;
3032 let mut stream = SourceStream::new("test.frm".to_string(), source);
3033 let (token_stream_opt, _) = tokenize(&mut stream).unpack();
3034 let token_stream = token_stream_opt.expect("Tokenization failed");
3035 let tokens = token_stream.into_tokens();
3036
3037 let mut parser = Parser::new_direct_extraction(tokens, 0);
3038 let attrs = parser.parse_attributes_direct();
3039
3040 assert_eq!(attrs.name, "MyForm");
3041 assert_eq!(attrs.global_name_space, NameSpace::Local);
3042 assert_eq!(attrs.creatable, Creatable::False);
3043 assert_eq!(attrs.predeclared_id, PreDeclaredID::True);
3044 assert_eq!(attrs.exposed, Exposed::False);
3045 assert_eq!(attrs.description, Some("This is a test form".to_string()));
3046 }
3047
3048 #[test]
3049 fn parse_ext_key_attributes() {
3050 let source = r#"Attribute VB_Name = "Form1"
3051Attribute VB_Ext_KEY = "CustomKey" ,"CustomValue"
3052Attribute VB_Description = "Test"
3053"#;
3054 let mut stream = SourceStream::new("test.frm".to_string(), source);
3055 let (token_stream_opt, _) = tokenize(&mut stream).unpack();
3056 let token_stream = token_stream_opt.expect("Tokenization failed");
3057 let tokens = token_stream.into_tokens();
3058
3059 let mut parser = Parser::new_direct_extraction(tokens, 0);
3060 let attrs = parser.parse_attributes_direct();
3061
3062 assert_eq!(attrs.name, "Form1");
3063 assert_eq!(attrs.description, Some("Test".to_string()));
3064 assert_eq!(attrs.ext_key.len(), 1);
3065 assert!(attrs.ext_key.contains_key("VB_Ext_KEY"));
3066 }
3067
3068 #[test]
3069 fn parse_empty_attributes() {
3070 let source = r"";
3071 let mut stream = SourceStream::new("test.frm".to_string(), source);
3072 let (token_stream_opt, _) = tokenize(&mut stream).unpack();
3073 let token_stream = token_stream_opt.expect("Tokenization failed");
3074 let tokens = token_stream.into_tokens();
3075
3076 let mut parser = Parser::new_direct_extraction(tokens, 0);
3077 let attrs = parser.parse_attributes_direct();
3078
3079 assert_eq!(attrs.name, "");
3080 assert_eq!(attrs.global_name_space, NameSpace::Local);
3081 assert_eq!(attrs.creatable, Creatable::True);
3082 assert_eq!(attrs.predeclared_id, PreDeclaredID::False);
3083 assert_eq!(attrs.exposed, Exposed::False);
3084 assert_eq!(attrs.description, None);
3085 assert!(attrs.ext_key.is_empty());
3086 }
3087
3088 #[test]
3089 fn parse_resource_reference_property() {
3090 let source = r#"Caption = $"Gradient.frx":0000
3091"#;
3092 let mut stream = SourceStream::new("test.frm".to_string(), source);
3093 let (token_stream_opt, _) = tokenize(&mut stream).unpack();
3094 let token_stream = token_stream_opt.expect("Tokenization failed");
3095 let tokens = token_stream.into_tokens();
3096
3097 let mut parser = Parser::new_direct_extraction(tokens, 0);
3098 let property = parser.parse_property_direct();
3099
3100 assert!(property.is_some());
3101 let (key, value) = property.expect("Expected property to be parsed");
3102 assert_eq!(key, "Caption");
3103 assert_eq!(value, r#"$"Gradient.frx":0000"#);
3104 }
3105}