diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 00000000..2f90d749 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,19 @@ +# Windows (MSVC toolchain) - 8MB stack +[target.'cfg(all(target_os = "windows", target_env = "msvc"))'] +rustflags = ["-C", "link-arg=/STACK:8388608"] + +# Windows (GNU toolchain) - 8MB stack +[target.'cfg(all(target_os = "windows", target_env = "gnu"))'] +rustflags = ["-C", "link-arg=-Wl,--stack,8388608"] + +# Linux - 8MB stack +[target.'cfg(target_os = "linux")'] +rustflags = ["-C", "link-arg=-Wl,-z,stack-size=8388608"] + +# macOS - 8MB stack +[target.'cfg(target_os = "macos")'] +rustflags = ["-C", "link-arg=-Wl,-stack_size,0x800000"] + +# FreeBSD and other Unix-like systems - 8MB stack +[target.'cfg(all(unix, not(any(target_os = "macos", target_os = "linux"))))'] +rustflags = ["-C", "link-arg=-Wl,-z,stack-size=8388608"] \ No newline at end of file diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 0ae00109..e5afb86b 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -38,7 +38,11 @@ "Bash(../target/release/wfl --parse basic_syntax_comprehensive.wfl)", "Bash(target\\release\\wfl.exe:*)", "Bash(targetreleasewfl.exe TestProgramstest_length.wfl)", - "Bash(../target/release/wfl.exe test_redefinition_error.wfl:*)" + "Bash(../target/release/wfl.exe test_redefinition_error.wfl:*)", + "Bash(../target/debug/wfl.exe --parse containers_comprehensive.wfl)", + "Bash(.targetdebugwfl.exe --parse TestProgramscontainers_comprehensive.wfl)", + "Bash(.targetreleasewfl.exe TestProgramstest_redefinition_error.wfl)", + "Bash(git fetch:*)" ], "deny": [] } diff --git a/TestPrograms/args_comprehensive.wfl.lex.txt b/TestPrograms/args_comprehensive.wfl.lex.txt deleted file mode 100644 index db814d61..00000000 --- a/TestPrograms/args_comprehensive.wfl.lex.txt +++ /dev/null @@ -1,723 +0,0 @@ -Lexer output for: TestPrograms/args_comprehensive.wfl -============================================== - - 0: KeywordDisplay at line 4, column 1 (length: 7) - 1: StringLiteral("=== WFL Command Line Arguments Comprehensive Test ===") at line 4, column 9 (length: 55) - 2: KeywordDisplay at line 5, column 1 (length: 7) - 3: StringLiteral("") at line 5, column 9 (length: 2) - 4: KeywordDisplay at line 8, column 1 (length: 7) - 5: StringLiteral("1. Basic Argument Information") at line 8, column 9 (length: 31) - 6: KeywordDisplay at line 9, column 1 (length: 7) - 7: StringLiteral("Arguments count: ") at line 9, column 9 (length: 19) - 8: KeywordWith at line 9, column 29 (length: 4) - 9: Identifier("arg_count") at line 9, column 34 (length: 9) - 10: KeywordDisplay at line 10, column 1 (length: 7) - 11: StringLiteral("Program name: ") at line 10, column 9 (length: 16) - 12: KeywordWith at line 10, column 26 (length: 4) - 13: Identifier("program_name") at line 10, column 31 (length: 12) - 14: KeywordDisplay at line 11, column 1 (length: 7) - 15: StringLiteral("") at line 11, column 9 (length: 2) - 16: KeywordDisplay at line 14, column 1 (length: 7) - 17: StringLiteral("2. All Arguments List") at line 14, column 9 (length: 23) - 18: KeywordCheck at line 15, column 1 (length: 5) - 19: KeywordIf at line 15, column 7 (length: 2) - 20: Identifier("arg_count") at line 15, column 10 (length: 9) - 21: KeywordIs at line 15, column 20 (length: 2) - 22: KeywordGreater at line 15, column 23 (length: 7) - 23: KeywordThan at line 15, column 31 (length: 4) - 24: IntLiteral(0) at line 15, column 36 (length: 1) - 25: Colon at line 15, column 37 (length: 1) - 26: KeywordDisplay at line 16, column 5 (length: 7) - 27: StringLiteral("Arguments passed to program:") at line 16, column 13 (length: 30) - 28: KeywordFor at line 17, column 5 (length: 3) - 29: KeywordEach at line 17, column 9 (length: 4) - 30: Identifier("arg") at line 17, column 14 (length: 3) - 31: KeywordIn at line 17, column 18 (length: 2) - 32: Identifier("args") at line 17, column 21 (length: 4) - 33: Colon at line 17, column 25 (length: 1) - 34: KeywordDisplay at line 18, column 9 (length: 7) - 35: StringLiteral(" - ") at line 18, column 17 (length: 6) - 36: KeywordWith at line 18, column 24 (length: 4) - 37: Identifier("arg") at line 18, column 29 (length: 3) - 38: KeywordEnd at line 19, column 5 (length: 3) - 39: KeywordFor at line 19, column 9 (length: 3) - 40: Identifier("else") at line 20, column 1 (length: 4) - 41: Colon at line 20, column 5 (length: 1) - 42: KeywordDisplay at line 21, column 5 (length: 7) - 43: StringLiteral("No arguments passed to program") at line 21, column 13 (length: 32) - 44: KeywordEnd at line 22, column 1 (length: 3) - 45: KeywordCheck at line 22, column 5 (length: 5) - 46: KeywordDisplay at line 23, column 1 (length: 7) - 47: StringLiteral("") at line 23, column 9 (length: 2) - 48: KeywordDisplay at line 26, column 1 (length: 7) - 49: StringLiteral("3. Indexed Argument Access") at line 26, column 9 (length: 28) - 50: KeywordCheck at line 27, column 1 (length: 5) - 51: KeywordIf at line 27, column 7 (length: 2) - 52: Identifier("arg_count") at line 27, column 10 (length: 9) - 53: KeywordIs at line 27, column 20 (length: 2) - 54: KeywordGreater at line 27, column 23 (length: 7) - 55: KeywordThan at line 27, column 31 (length: 4) - 56: IntLiteral(0) at line 27, column 36 (length: 1) - 57: Colon at line 27, column 37 (length: 1) - 58: KeywordDisplay at line 28, column 5 (length: 7) - 59: StringLiteral("First argument: ") at line 28, column 13 (length: 18) - 60: KeywordWith at line 28, column 32 (length: 4) - 61: Identifier("args") at line 28, column 37 (length: 4) - 62: LeftBracket at line 28, column 41 (length: 1) - 63: IntLiteral(0) at line 28, column 42 (length: 1) - 64: RightBracket at line 28, column 43 (length: 1) - 65: KeywordCheck at line 30, column 5 (length: 5) - 66: KeywordIf at line 30, column 11 (length: 2) - 67: Identifier("arg_count") at line 30, column 14 (length: 9) - 68: KeywordIs at line 30, column 24 (length: 2) - 69: KeywordGreater at line 30, column 27 (length: 7) - 70: KeywordThan at line 30, column 35 (length: 4) - 71: IntLiteral(1) at line 30, column 40 (length: 1) - 72: Colon at line 30, column 41 (length: 1) - 73: KeywordDisplay at line 31, column 9 (length: 7) - 74: StringLiteral("Second argument: ") at line 31, column 17 (length: 19) - 75: KeywordWith at line 31, column 37 (length: 4) - 76: Identifier("args") at line 31, column 42 (length: 4) - 77: LeftBracket at line 31, column 46 (length: 1) - 78: IntLiteral(1) at line 31, column 47 (length: 1) - 79: RightBracket at line 31, column 48 (length: 1) - 80: Identifier("else") at line 32, column 5 (length: 4) - 81: Colon at line 32, column 9 (length: 1) - 82: KeywordDisplay at line 33, column 9 (length: 7) - 83: StringLiteral("No second argument provided") at line 33, column 17 (length: 29) - 84: KeywordEnd at line 34, column 5 (length: 3) - 85: KeywordCheck at line 34, column 9 (length: 5) - 86: KeywordCheck at line 36, column 5 (length: 5) - 87: KeywordIf at line 36, column 11 (length: 2) - 88: Identifier("arg_count") at line 36, column 14 (length: 9) - 89: KeywordIs at line 36, column 24 (length: 2) - 90: KeywordGreater at line 36, column 27 (length: 7) - 91: KeywordThan at line 36, column 35 (length: 4) - 92: IntLiteral(2) at line 36, column 40 (length: 1) - 93: Colon at line 36, column 41 (length: 1) - 94: KeywordDisplay at line 37, column 9 (length: 7) - 95: StringLiteral("Third argument: ") at line 37, column 17 (length: 18) - 96: KeywordWith at line 37, column 36 (length: 4) - 97: Identifier("args") at line 37, column 41 (length: 4) - 98: LeftBracket at line 37, column 45 (length: 1) - 99: IntLiteral(2) at line 37, column 46 (length: 1) - 100: RightBracket at line 37, column 47 (length: 1) - 101: Identifier("else") at line 38, column 5 (length: 4) - 102: Colon at line 38, column 9 (length: 1) - 103: KeywordDisplay at line 39, column 9 (length: 7) - 104: StringLiteral("No third argument provided") at line 39, column 17 (length: 28) - 105: KeywordEnd at line 40, column 5 (length: 3) - 106: KeywordCheck at line 40, column 9 (length: 5) - 107: Identifier("else") at line 41, column 1 (length: 4) - 108: Colon at line 41, column 5 (length: 1) - 109: KeywordDisplay at line 42, column 5 (length: 7) - 110: StringLiteral("No arguments to access by index") at line 42, column 13 (length: 33) - 111: KeywordEnd at line 43, column 1 (length: 3) - 112: KeywordCheck at line 43, column 5 (length: 5) - 113: KeywordDisplay at line 44, column 1 (length: 7) - 114: StringLiteral("") at line 44, column 9 (length: 2) - 115: KeywordDisplay at line 47, column 1 (length: 7) - 116: StringLiteral("4. Argument Processing") at line 47, column 9 (length: 24) - 117: KeywordCheck at line 48, column 1 (length: 5) - 118: KeywordIf at line 48, column 7 (length: 2) - 119: Identifier("arg_count") at line 48, column 10 (length: 9) - 120: KeywordIs at line 48, column 20 (length: 2) - 121: KeywordGreater at line 48, column 23 (length: 7) - 122: KeywordThan at line 48, column 31 (length: 4) - 123: IntLiteral(0) at line 48, column 36 (length: 1) - 124: Colon at line 48, column 37 (length: 1) - 125: KeywordDisplay at line 49, column 5 (length: 7) - 126: StringLiteral("Processing each argument:") at line 49, column 13 (length: 27) - 127: KeywordStore at line 50, column 5 (length: 5) - 128: Identifier("arg_index") at line 50, column 11 (length: 9) - 129: KeywordAs at line 50, column 21 (length: 2) - 130: IntLiteral(0) at line 50, column 24 (length: 1) - 131: KeywordFor at line 51, column 5 (length: 3) - 132: KeywordEach at line 51, column 9 (length: 4) - 133: Identifier("arg") at line 51, column 14 (length: 3) - 134: KeywordIn at line 51, column 18 (length: 2) - 135: Identifier("args") at line 51, column 21 (length: 4) - 136: Colon at line 51, column 25 (length: 1) - 137: KeywordStore at line 52, column 9 (length: 5) - 138: Identifier("arg_length") at line 52, column 15 (length: 10) - 139: KeywordAs at line 52, column 26 (length: 2) - 140: Identifier("length") at line 52, column 29 (length: 6) - 141: KeywordOf at line 52, column 36 (length: 2) - 142: Identifier("arg") at line 52, column 39 (length: 3) - 143: KeywordDisplay at line 53, column 9 (length: 7) - 144: StringLiteral(" Arg ") at line 53, column 17 (length: 8) - 145: KeywordWith at line 53, column 26 (length: 4) - 146: Identifier("arg_index") at line 53, column 31 (length: 9) - 147: KeywordWith at line 53, column 41 (length: 4) - 148: StringLiteral(": '") at line 53, column 46 (length: 5) - 149: KeywordWith at line 53, column 52 (length: 4) - 150: Identifier("arg") at line 53, column 57 (length: 3) - 151: KeywordWith at line 53, column 61 (length: 4) - 152: StringLiteral("' (length: ") at line 53, column 66 (length: 13) - 153: KeywordWith at line 53, column 80 (length: 4) - 154: Identifier("arg_length") at line 53, column 85 (length: 10) - 155: KeywordWith at line 53, column 96 (length: 4) - 156: StringLiteral(")") at line 53, column 101 (length: 3) - 157: KeywordStore at line 54, column 9 (length: 5) - 158: Identifier("arg_index") at line 54, column 15 (length: 9) - 159: KeywordAs at line 54, column 25 (length: 2) - 160: Identifier("arg_index") at line 54, column 28 (length: 9) - 161: Plus at line 54, column 38 (length: 1) - 162: IntLiteral(1) at line 54, column 40 (length: 1) - 163: KeywordEnd at line 55, column 5 (length: 3) - 164: KeywordFor at line 55, column 9 (length: 3) - 165: Identifier("else") at line 56, column 1 (length: 4) - 166: Colon at line 56, column 5 (length: 1) - 167: KeywordDisplay at line 57, column 5 (length: 7) - 168: StringLiteral("No arguments to process") at line 57, column 13 (length: 25) - 169: KeywordEnd at line 58, column 1 (length: 3) - 170: KeywordCheck at line 58, column 5 (length: 5) - 171: KeywordDisplay at line 59, column 1 (length: 7) - 172: StringLiteral("") at line 59, column 9 (length: 2) - 173: KeywordDisplay at line 62, column 1 (length: 7) - 174: StringLiteral("5. Argument Validation") at line 62, column 9 (length: 24) - 175: KeywordCheck at line 63, column 1 (length: 5) - 176: KeywordIf at line 63, column 7 (length: 2) - 177: Identifier("arg_count") at line 63, column 10 (length: 9) - 178: KeywordIs at line 63, column 20 (length: 2) - 179: IntLiteral(0) at line 63, column 23 (length: 1) - 180: Colon at line 63, column 24 (length: 1) - 181: KeywordDisplay at line 64, column 5 (length: 7) - 182: StringLiteral("Usage: program.wfl [arg3] ...") at line 64, column 13 (length: 45) - 183: KeywordDisplay at line 65, column 5 (length: 7) - 184: StringLiteral("Example: program.wfl hello world 123") at line 65, column 13 (length: 38) - 185: Identifier("elif arg_count") at line 66, column 1 (length: 14) - 186: KeywordIs at line 66, column 16 (length: 2) - 187: IntLiteral(1) at line 66, column 19 (length: 1) - 188: Colon at line 66, column 20 (length: 1) - 189: KeywordDisplay at line 67, column 5 (length: 7) - 190: StringLiteral("Single argument mode:") at line 67, column 13 (length: 23) - 191: KeywordDisplay at line 68, column 5 (length: 7) - 192: StringLiteral(" Argument: ") at line 68, column 13 (length: 14) - 193: KeywordWith at line 68, column 28 (length: 4) - 194: Identifier("args") at line 68, column 33 (length: 4) - 195: LeftBracket at line 68, column 37 (length: 1) - 196: IntLiteral(0) at line 68, column 38 (length: 1) - 197: RightBracket at line 68, column 39 (length: 1) - 198: KeywordDisplay at line 69, column 5 (length: 7) - 199: StringLiteral(" Length: ") at line 69, column 13 (length: 12) - 200: KeywordWith at line 69, column 26 (length: 4) - 201: Identifier("length") at line 69, column 31 (length: 6) - 202: KeywordOf at line 69, column 38 (length: 2) - 203: Identifier("args") at line 69, column 41 (length: 4) - 204: LeftBracket at line 69, column 45 (length: 1) - 205: IntLiteral(0) at line 69, column 46 (length: 1) - 206: RightBracket at line 69, column 47 (length: 1) - 207: KeywordDisplay at line 70, column 5 (length: 7) - 208: StringLiteral(" Uppercase: ") at line 70, column 13 (length: 15) - 209: KeywordWith at line 70, column 29 (length: 4) - 210: Identifier("touppercase") at line 70, column 34 (length: 11) - 211: KeywordOf at line 70, column 46 (length: 2) - 212: Identifier("args") at line 70, column 49 (length: 4) - 213: LeftBracket at line 70, column 53 (length: 1) - 214: IntLiteral(0) at line 70, column 54 (length: 1) - 215: RightBracket at line 70, column 55 (length: 1) - 216: Identifier("elif arg_count") at line 71, column 1 (length: 14) - 217: KeywordIs at line 71, column 16 (length: 2) - 218: IntLiteral(2) at line 71, column 19 (length: 1) - 219: Colon at line 71, column 20 (length: 1) - 220: KeywordDisplay at line 72, column 5 (length: 7) - 221: StringLiteral("Two argument mode:") at line 72, column 13 (length: 20) - 222: KeywordDisplay at line 73, column 5 (length: 7) - 223: StringLiteral(" First: ") at line 73, column 13 (length: 11) - 224: KeywordWith at line 73, column 25 (length: 4) - 225: Identifier("args") at line 73, column 30 (length: 4) - 226: LeftBracket at line 73, column 34 (length: 1) - 227: IntLiteral(0) at line 73, column 35 (length: 1) - 228: RightBracket at line 73, column 36 (length: 1) - 229: KeywordDisplay at line 74, column 5 (length: 7) - 230: StringLiteral(" Second: ") at line 74, column 13 (length: 12) - 231: KeywordWith at line 74, column 26 (length: 4) - 232: Identifier("args") at line 74, column 31 (length: 4) - 233: LeftBracket at line 74, column 35 (length: 1) - 234: IntLiteral(1) at line 74, column 36 (length: 1) - 235: RightBracket at line 74, column 37 (length: 1) - 236: KeywordDisplay at line 75, column 5 (length: 7) - 237: StringLiteral(" Combined: ") at line 75, column 13 (length: 14) - 238: KeywordWith at line 75, column 28 (length: 4) - 239: Identifier("args") at line 75, column 33 (length: 4) - 240: LeftBracket at line 75, column 37 (length: 1) - 241: IntLiteral(0) at line 75, column 38 (length: 1) - 242: RightBracket at line 75, column 39 (length: 1) - 243: KeywordWith at line 75, column 41 (length: 4) - 244: StringLiteral(" ") at line 75, column 46 (length: 3) - 245: KeywordWith at line 75, column 50 (length: 4) - 246: Identifier("args") at line 75, column 55 (length: 4) - 247: LeftBracket at line 75, column 59 (length: 1) - 248: IntLiteral(1) at line 75, column 60 (length: 1) - 249: RightBracket at line 75, column 61 (length: 1) - 250: Identifier("elif arg_count") at line 76, column 1 (length: 14) - 251: KeywordIs at line 76, column 16 (length: 2) - 252: KeywordGreater at line 76, column 19 (length: 7) - 253: KeywordThan at line 76, column 27 (length: 4) - 254: IntLiteral(2) at line 76, column 32 (length: 1) - 255: Colon at line 76, column 33 (length: 1) - 256: KeywordDisplay at line 77, column 5 (length: 7) - 257: StringLiteral("Multiple argument mode:") at line 77, column 13 (length: 25) - 258: KeywordDisplay at line 78, column 5 (length: 7) - 259: StringLiteral(" Count: ") at line 78, column 13 (length: 11) - 260: KeywordWith at line 78, column 25 (length: 4) - 261: Identifier("arg_count") at line 78, column 30 (length: 9) - 262: KeywordDisplay at line 79, column 5 (length: 7) - 263: StringLiteral(" First: ") at line 79, column 13 (length: 11) - 264: KeywordWith at line 79, column 25 (length: 4) - 265: Identifier("args") at line 79, column 30 (length: 4) - 266: LeftBracket at line 79, column 34 (length: 1) - 267: IntLiteral(0) at line 79, column 35 (length: 1) - 268: RightBracket at line 79, column 36 (length: 1) - 269: KeywordStore at line 80, column 5 (length: 5) - 270: Identifier("last_index") at line 80, column 11 (length: 10) - 271: KeywordAs at line 80, column 22 (length: 2) - 272: Identifier("arg_count") at line 80, column 25 (length: 9) - 273: KeywordMinus at line 80, column 35 (length: 5) - 274: IntLiteral(1) at line 80, column 41 (length: 1) - 275: KeywordDisplay at line 81, column 5 (length: 7) - 276: StringLiteral(" Last: ") at line 81, column 13 (length: 10) - 277: KeywordWith at line 81, column 24 (length: 4) - 278: Identifier("args") at line 81, column 29 (length: 4) - 279: LeftBracket at line 81, column 33 (length: 1) - 280: Identifier("last_index") at line 81, column 34 (length: 10) - 281: RightBracket at line 81, column 44 (length: 1) - 282: KeywordEnd at line 82, column 1 (length: 3) - 283: KeywordCheck at line 82, column 5 (length: 5) - 284: KeywordDisplay at line 83, column 1 (length: 7) - 285: StringLiteral("") at line 83, column 9 (length: 2) - 286: KeywordDisplay at line 86, column 1 (length: 7) - 287: StringLiteral("6. Argument Type Detection") at line 86, column 9 (length: 28) - 288: KeywordCheck at line 87, column 1 (length: 5) - 289: KeywordIf at line 87, column 7 (length: 2) - 290: Identifier("arg_count") at line 87, column 10 (length: 9) - 291: KeywordIs at line 87, column 20 (length: 2) - 292: KeywordGreater at line 87, column 23 (length: 7) - 293: KeywordThan at line 87, column 31 (length: 4) - 294: IntLiteral(0) at line 87, column 36 (length: 1) - 295: Colon at line 87, column 37 (length: 1) - 296: KeywordFor at line 88, column 5 (length: 3) - 297: KeywordEach at line 88, column 9 (length: 4) - 298: Identifier("arg") at line 88, column 14 (length: 3) - 299: KeywordIn at line 88, column 18 (length: 2) - 300: Identifier("args") at line 88, column 21 (length: 4) - 301: Colon at line 88, column 25 (length: 1) - 302: KeywordCreate at line 90, column 9 (length: 6) - 303: KeywordPattern at line 90, column 16 (length: 7) - 304: Identifier("numeric") at line 90, column 24 (length: 7) - 305: Colon at line 90, column 31 (length: 1) - 306: KeywordOne at line 91, column 13 (length: 3) - 307: KeywordOr at line 91, column 17 (length: 2) - 308: KeywordMore at line 91, column 20 (length: 4) - 309: KeywordDigit at line 91, column 25 (length: 5) - 310: KeywordEnd at line 92, column 9 (length: 3) - 311: KeywordPattern at line 92, column 13 (length: 7) - 312: KeywordCreate at line 94, column 9 (length: 6) - 313: KeywordPattern at line 94, column 16 (length: 7) - 314: Identifier("decimal") at line 94, column 24 (length: 7) - 315: Colon at line 94, column 31 (length: 1) - 316: KeywordOne at line 95, column 13 (length: 3) - 317: KeywordOr at line 95, column 17 (length: 2) - 318: KeywordMore at line 95, column 20 (length: 4) - 319: KeywordDigit at line 95, column 25 (length: 5) - 320: KeywordThen at line 95, column 31 (length: 4) - 321: StringLiteral(".") at line 95, column 36 (length: 3) - 322: KeywordThen at line 95, column 40 (length: 4) - 323: KeywordOne at line 95, column 45 (length: 3) - 324: KeywordOr at line 95, column 49 (length: 2) - 325: KeywordMore at line 95, column 52 (length: 4) - 326: KeywordDigit at line 95, column 57 (length: 5) - 327: KeywordEnd at line 96, column 9 (length: 3) - 328: KeywordPattern at line 96, column 13 (length: 7) - 329: KeywordCheck at line 98, column 9 (length: 5) - 330: KeywordIf at line 98, column 15 (length: 2) - 331: Identifier("arg") at line 98, column 18 (length: 3) - 332: KeywordMatches at line 98, column 22 (length: 7) - 333: Identifier("numeric") at line 98, column 30 (length: 7) - 334: Colon at line 98, column 37 (length: 1) - 335: KeywordDisplay at line 99, column 13 (length: 7) - 336: StringLiteral(" '") at line 99, column 21 (length: 5) - 337: KeywordWith at line 99, column 27 (length: 4) - 338: Identifier("arg") at line 99, column 32 (length: 3) - 339: KeywordWith at line 99, column 36 (length: 4) - 340: StringLiteral("' is an integer") at line 99, column 41 (length: 17) - 341: Identifier("elif arg") at line 100, column 9 (length: 8) - 342: KeywordMatches at line 100, column 18 (length: 7) - 343: Identifier("decimal") at line 100, column 26 (length: 7) - 344: Colon at line 100, column 33 (length: 1) - 345: KeywordDisplay at line 101, column 13 (length: 7) - 346: StringLiteral(" '") at line 101, column 21 (length: 5) - 347: KeywordWith at line 101, column 27 (length: 4) - 348: Identifier("arg") at line 101, column 32 (length: 3) - 349: KeywordWith at line 101, column 36 (length: 4) - 350: StringLiteral("' is a decimal number") at line 101, column 41 (length: 23) - 351: Identifier("else") at line 102, column 9 (length: 4) - 352: Colon at line 102, column 13 (length: 1) - 353: KeywordDisplay at line 103, column 13 (length: 7) - 354: StringLiteral(" '") at line 103, column 21 (length: 5) - 355: KeywordWith at line 103, column 27 (length: 4) - 356: Identifier("arg") at line 103, column 32 (length: 3) - 357: KeywordWith at line 103, column 36 (length: 4) - 358: StringLiteral("' is text") at line 103, column 41 (length: 11) - 359: KeywordEnd at line 104, column 9 (length: 3) - 360: KeywordCheck at line 104, column 13 (length: 5) - 361: KeywordEnd at line 105, column 5 (length: 3) - 362: KeywordFor at line 105, column 9 (length: 3) - 363: Identifier("else") at line 106, column 1 (length: 4) - 364: Colon at line 106, column 5 (length: 1) - 365: KeywordDisplay at line 107, column 5 (length: 7) - 366: StringLiteral("No arguments for type detection") at line 107, column 13 (length: 33) - 367: KeywordEnd at line 108, column 1 (length: 3) - 368: KeywordCheck at line 108, column 5 (length: 5) - 369: KeywordDisplay at line 109, column 1 (length: 7) - 370: StringLiteral("") at line 109, column 9 (length: 2) - 371: KeywordDisplay at line 112, column 1 (length: 7) - 372: StringLiteral("7. Flag/Option Parsing") at line 112, column 9 (length: 24) - 373: KeywordCheck at line 113, column 1 (length: 5) - 374: KeywordIf at line 113, column 7 (length: 2) - 375: Identifier("arg_count") at line 113, column 10 (length: 9) - 376: KeywordIs at line 113, column 20 (length: 2) - 377: KeywordGreater at line 113, column 23 (length: 7) - 378: KeywordThan at line 113, column 31 (length: 4) - 379: IntLiteral(0) at line 113, column 36 (length: 1) - 380: Colon at line 113, column 37 (length: 1) - 381: KeywordStore at line 114, column 5 (length: 5) - 382: Identifier("has_help") at line 114, column 11 (length: 8) - 383: KeywordAs at line 114, column 20 (length: 2) - 384: BooleanLiteral(false) at line 114, column 23 (length: 2) - 385: KeywordStore at line 115, column 5 (length: 5) - 386: Identifier("has_version") at line 115, column 11 (length: 11) - 387: KeywordAs at line 115, column 23 (length: 2) - 388: BooleanLiteral(false) at line 115, column 26 (length: 2) - 389: KeywordStore at line 116, column 5 (length: 5) - 390: Identifier("has_verbose") at line 116, column 11 (length: 11) - 391: KeywordAs at line 116, column 23 (length: 2) - 392: BooleanLiteral(false) at line 116, column 26 (length: 2) - 393: KeywordStore at line 117, column 5 (length: 5) - 394: Identifier("non_flag_args") at line 117, column 11 (length: 13) - 395: KeywordAs at line 117, column 25 (length: 2) - 396: LeftBracket at line 117, column 28 (length: 1) - 397: RightBracket at line 117, column 29 (length: 1) - 398: KeywordFor at line 119, column 5 (length: 3) - 399: KeywordEach at line 119, column 9 (length: 4) - 400: Identifier("arg") at line 119, column 14 (length: 3) - 401: KeywordIn at line 119, column 18 (length: 2) - 402: Identifier("args") at line 119, column 21 (length: 4) - 403: Colon at line 119, column 25 (length: 1) - 404: KeywordCheck at line 120, column 9 (length: 5) - 405: KeywordIf at line 120, column 15 (length: 2) - 406: Identifier("arg") at line 120, column 18 (length: 3) - 407: KeywordIs at line 120, column 22 (length: 2) - 408: StringLiteral("--help") at line 120, column 25 (length: 8) - 409: KeywordOr at line 120, column 34 (length: 2) - 410: Identifier("arg") at line 120, column 37 (length: 3) - 411: KeywordIs at line 120, column 41 (length: 2) - 412: StringLiteral("-h") at line 120, column 44 (length: 4) - 413: Colon at line 120, column 48 (length: 1) - 414: KeywordStore at line 121, column 13 (length: 5) - 415: Identifier("has_help") at line 121, column 19 (length: 8) - 416: KeywordAs at line 121, column 28 (length: 2) - 417: BooleanLiteral(true) at line 121, column 31 (length: 3) - 418: Identifier("elif arg") at line 122, column 9 (length: 8) - 419: KeywordIs at line 122, column 18 (length: 2) - 420: StringLiteral("--version") at line 122, column 21 (length: 11) - 421: KeywordOr at line 122, column 33 (length: 2) - 422: Identifier("arg") at line 122, column 36 (length: 3) - 423: KeywordIs at line 122, column 40 (length: 2) - 424: StringLiteral("-v") at line 122, column 43 (length: 4) - 425: Colon at line 122, column 47 (length: 1) - 426: KeywordStore at line 123, column 13 (length: 5) - 427: Identifier("has_version") at line 123, column 19 (length: 11) - 428: KeywordAs at line 123, column 31 (length: 2) - 429: BooleanLiteral(true) at line 123, column 34 (length: 3) - 430: Identifier("elif arg") at line 124, column 9 (length: 8) - 431: KeywordIs at line 124, column 18 (length: 2) - 432: StringLiteral("--verbose") at line 124, column 21 (length: 11) - 433: Colon at line 124, column 32 (length: 1) - 434: KeywordStore at line 125, column 13 (length: 5) - 435: Identifier("has_verbose") at line 125, column 19 (length: 11) - 436: KeywordAs at line 125, column 31 (length: 2) - 437: BooleanLiteral(true) at line 125, column 34 (length: 3) - 438: Identifier("elif substring") at line 126, column 9 (length: 14) - 439: KeywordOf at line 126, column 24 (length: 2) - 440: Identifier("arg") at line 126, column 27 (length: 3) - 441: KeywordAnd at line 126, column 31 (length: 3) - 442: IntLiteral(0) at line 126, column 35 (length: 1) - 443: KeywordAnd at line 126, column 37 (length: 3) - 444: IntLiteral(1) at line 126, column 41 (length: 1) - 445: KeywordIs at line 126, column 43 (length: 2) - 446: StringLiteral("-") at line 126, column 46 (length: 3) - 447: Colon at line 126, column 49 (length: 1) - 448: KeywordDisplay at line 127, column 13 (length: 7) - 449: StringLiteral(" Unknown flag: ") at line 127, column 21 (length: 18) - 450: KeywordWith at line 127, column 40 (length: 4) - 451: Identifier("arg") at line 127, column 45 (length: 3) - 452: Identifier("else") at line 128, column 9 (length: 4) - 453: Colon at line 128, column 13 (length: 1) - 454: KeywordPush at line 129, column 13 (length: 4) - 455: KeywordWith at line 129, column 18 (length: 4) - 456: Identifier("non_flag_args") at line 129, column 23 (length: 13) - 457: KeywordAnd at line 129, column 37 (length: 3) - 458: Identifier("arg") at line 129, column 41 (length: 3) - 459: KeywordEnd at line 130, column 9 (length: 3) - 460: KeywordCheck at line 130, column 13 (length: 5) - 461: KeywordEnd at line 131, column 5 (length: 3) - 462: KeywordFor at line 131, column 9 (length: 3) - 463: KeywordDisplay at line 133, column 5 (length: 7) - 464: StringLiteral("Flags detected:") at line 133, column 13 (length: 17) - 465: KeywordDisplay at line 134, column 5 (length: 7) - 466: StringLiteral(" Help flag: ") at line 134, column 13 (length: 15) - 467: KeywordWith at line 134, column 29 (length: 4) - 468: Identifier("has_help") at line 134, column 34 (length: 8) - 469: KeywordDisplay at line 135, column 5 (length: 7) - 470: StringLiteral(" Version flag: ") at line 135, column 13 (length: 18) - 471: KeywordWith at line 135, column 32 (length: 4) - 472: Identifier("has_version") at line 135, column 37 (length: 11) - 473: KeywordDisplay at line 136, column 5 (length: 7) - 474: StringLiteral(" Verbose flag: ") at line 136, column 13 (length: 18) - 475: KeywordWith at line 136, column 32 (length: 4) - 476: Identifier("has_verbose") at line 136, column 37 (length: 11) - 477: KeywordDisplay at line 138, column 5 (length: 7) - 478: StringLiteral("Non-flag arguments:") at line 138, column 13 (length: 21) - 479: KeywordFor at line 139, column 5 (length: 3) - 480: KeywordEach at line 139, column 9 (length: 4) - 481: Identifier("non_flag") at line 139, column 14 (length: 8) - 482: KeywordIn at line 139, column 23 (length: 2) - 483: Identifier("non_flag_args") at line 139, column 26 (length: 13) - 484: Colon at line 139, column 39 (length: 1) - 485: KeywordDisplay at line 140, column 9 (length: 7) - 486: StringLiteral(" - ") at line 140, column 17 (length: 6) - 487: KeywordWith at line 140, column 24 (length: 4) - 488: Identifier("non_flag") at line 140, column 29 (length: 8) - 489: KeywordEnd at line 141, column 5 (length: 3) - 490: KeywordFor at line 141, column 9 (length: 3) - 491: Identifier("else") at line 142, column 1 (length: 4) - 492: Colon at line 142, column 5 (length: 1) - 493: KeywordDisplay at line 143, column 5 (length: 7) - 494: StringLiteral("No arguments for flag parsing") at line 143, column 13 (length: 31) - 495: KeywordEnd at line 144, column 1 (length: 3) - 496: KeywordCheck at line 144, column 5 (length: 5) - 497: KeywordDisplay at line 145, column 1 (length: 7) - 498: StringLiteral("") at line 145, column 9 (length: 2) - 499: KeywordDisplay at line 148, column 1 (length: 7) - 500: StringLiteral("8. Environment Integration Test") at line 148, column 9 (length: 33) - 501: KeywordDisplay at line 150, column 1 (length: 7) - 502: StringLiteral("Program execution context:") at line 150, column 9 (length: 28) - 503: KeywordDisplay at line 151, column 1 (length: 7) - 504: StringLiteral(" Program: ") at line 151, column 9 (length: 13) - 505: KeywordWith at line 151, column 23 (length: 4) - 506: Identifier("program_name") at line 151, column 28 (length: 12) - 507: KeywordDisplay at line 152, column 1 (length: 7) - 508: StringLiteral(" Arguments: ") at line 152, column 9 (length: 15) - 509: KeywordWith at line 152, column 25 (length: 4) - 510: Identifier("arg_count") at line 152, column 30 (length: 9) - 511: KeywordDisplay at line 153, column 1 (length: 7) - 512: StringLiteral(" Current directory: ") at line 153, column 9 (length: 23) - 513: KeywordWith at line 153, column 33 (length: 4) - 514: Identifier("current_directory") at line 153, column 38 (length: 17) - 515: KeywordCheck at line 155, column 1 (length: 5) - 516: KeywordIf at line 155, column 7 (length: 2) - 517: Identifier("arg_count") at line 155, column 10 (length: 9) - 518: KeywordIs at line 155, column 20 (length: 2) - 519: KeywordGreater at line 155, column 23 (length: 7) - 520: KeywordThan at line 155, column 31 (length: 4) - 521: IntLiteral(0) at line 155, column 36 (length: 1) - 522: Colon at line 155, column 37 (length: 1) - 523: KeywordDisplay at line 156, column 5 (length: 7) - 524: StringLiteral(" Working with arguments in current environment") at line 156, column 13 (length: 49) - 525: KeywordStore at line 157, column 5 (length: 5) - 526: Identifier("combined_args") at line 157, column 11 (length: 13) - 527: KeywordAs at line 157, column 25 (length: 2) - 528: StringLiteral("") at line 157, column 28 (length: 2) - 529: KeywordFor at line 158, column 5 (length: 3) - 530: KeywordEach at line 158, column 9 (length: 4) - 531: Identifier("arg") at line 158, column 14 (length: 3) - 532: KeywordIn at line 158, column 18 (length: 2) - 533: Identifier("args") at line 158, column 21 (length: 4) - 534: Colon at line 158, column 25 (length: 1) - 535: KeywordStore at line 159, column 9 (length: 5) - 536: Identifier("combined_args") at line 159, column 15 (length: 13) - 537: KeywordAs at line 159, column 29 (length: 2) - 538: Identifier("combined_args") at line 159, column 32 (length: 13) - 539: KeywordWith at line 159, column 46 (length: 4) - 540: Identifier("arg") at line 159, column 51 (length: 3) - 541: KeywordWith at line 159, column 55 (length: 4) - 542: StringLiteral(" ") at line 159, column 60 (length: 3) - 543: KeywordEnd at line 160, column 5 (length: 3) - 544: KeywordFor at line 160, column 9 (length: 3) - 545: KeywordDisplay at line 161, column 5 (length: 7) - 546: StringLiteral(" Combined arguments: '") at line 161, column 13 (length: 25) - 547: KeywordWith at line 161, column 39 (length: 4) - 548: Identifier("combined_args") at line 161, column 44 (length: 13) - 549: KeywordWith at line 161, column 58 (length: 4) - 550: StringLiteral("'") at line 161, column 63 (length: 3) - 551: KeywordEnd at line 162, column 1 (length: 3) - 552: KeywordCheck at line 162, column 5 (length: 5) - 553: KeywordDisplay at line 163, column 1 (length: 7) - 554: StringLiteral("") at line 163, column 9 (length: 2) - 555: KeywordDisplay at line 166, column 1 (length: 7) - 556: StringLiteral("9. Argument Filtering") at line 166, column 9 (length: 23) - 557: KeywordCheck at line 167, column 1 (length: 5) - 558: KeywordIf at line 167, column 7 (length: 2) - 559: Identifier("arg_count") at line 167, column 10 (length: 9) - 560: KeywordIs at line 167, column 20 (length: 2) - 561: KeywordGreater at line 167, column 23 (length: 7) - 562: KeywordThan at line 167, column 31 (length: 4) - 563: IntLiteral(0) at line 167, column 36 (length: 1) - 564: Colon at line 167, column 37 (length: 1) - 565: KeywordStore at line 168, column 5 (length: 5) - 566: Identifier("long_args") at line 168, column 11 (length: 9) - 567: KeywordAs at line 168, column 21 (length: 2) - 568: LeftBracket at line 168, column 24 (length: 1) - 569: RightBracket at line 168, column 25 (length: 1) - 570: KeywordStore at line 169, column 5 (length: 5) - 571: Identifier("short_args") at line 169, column 11 (length: 10) - 572: KeywordAs at line 169, column 22 (length: 2) - 573: LeftBracket at line 169, column 25 (length: 1) - 574: RightBracket at line 169, column 26 (length: 1) - 575: KeywordFor at line 171, column 5 (length: 3) - 576: KeywordEach at line 171, column 9 (length: 4) - 577: Identifier("arg") at line 171, column 14 (length: 3) - 578: KeywordIn at line 171, column 18 (length: 2) - 579: Identifier("args") at line 171, column 21 (length: 4) - 580: Colon at line 171, column 25 (length: 1) - 581: KeywordCheck at line 172, column 9 (length: 5) - 582: KeywordIf at line 172, column 15 (length: 2) - 583: Identifier("length") at line 172, column 18 (length: 6) - 584: KeywordOf at line 172, column 25 (length: 2) - 585: Identifier("arg") at line 172, column 28 (length: 3) - 586: KeywordIs at line 172, column 32 (length: 2) - 587: KeywordGreater at line 172, column 35 (length: 7) - 588: KeywordThan at line 172, column 43 (length: 4) - 589: IntLiteral(5) at line 172, column 48 (length: 1) - 590: Colon at line 172, column 49 (length: 1) - 591: KeywordPush at line 173, column 13 (length: 4) - 592: KeywordWith at line 173, column 18 (length: 4) - 593: Identifier("long_args") at line 173, column 23 (length: 9) - 594: KeywordAnd at line 173, column 33 (length: 3) - 595: Identifier("arg") at line 173, column 37 (length: 3) - 596: Identifier("else") at line 174, column 9 (length: 4) - 597: Colon at line 174, column 13 (length: 1) - 598: KeywordPush at line 175, column 13 (length: 4) - 599: KeywordWith at line 175, column 18 (length: 4) - 600: Identifier("short_args") at line 175, column 23 (length: 10) - 601: KeywordAnd at line 175, column 34 (length: 3) - 602: Identifier("arg") at line 175, column 38 (length: 3) - 603: KeywordEnd at line 176, column 9 (length: 3) - 604: KeywordCheck at line 176, column 13 (length: 5) - 605: KeywordEnd at line 177, column 5 (length: 3) - 606: KeywordFor at line 177, column 9 (length: 3) - 607: KeywordDisplay at line 179, column 5 (length: 7) - 608: StringLiteral("Long arguments (>5 chars):") at line 179, column 13 (length: 28) - 609: KeywordFor at line 180, column 5 (length: 3) - 610: KeywordEach at line 180, column 9 (length: 4) - 611: Identifier("long_arg") at line 180, column 14 (length: 8) - 612: KeywordIn at line 180, column 23 (length: 2) - 613: Identifier("long_args") at line 180, column 26 (length: 9) - 614: Colon at line 180, column 35 (length: 1) - 615: KeywordDisplay at line 181, column 9 (length: 7) - 616: StringLiteral(" - ") at line 181, column 17 (length: 6) - 617: KeywordWith at line 181, column 24 (length: 4) - 618: Identifier("long_arg") at line 181, column 29 (length: 8) - 619: KeywordEnd at line 182, column 5 (length: 3) - 620: KeywordFor at line 182, column 9 (length: 3) - 621: KeywordDisplay at line 184, column 5 (length: 7) - 622: StringLiteral("Short arguments (≤5 chars):") at line 184, column 13 (length: 31) - 623: KeywordFor at line 185, column 5 (length: 3) - 624: KeywordEach at line 185, column 9 (length: 4) - 625: Identifier("short_arg") at line 185, column 14 (length: 9) - 626: KeywordIn at line 185, column 24 (length: 2) - 627: Identifier("short_args") at line 185, column 27 (length: 10) - 628: Colon at line 185, column 37 (length: 1) - 629: KeywordDisplay at line 186, column 9 (length: 7) - 630: StringLiteral(" - ") at line 186, column 17 (length: 6) - 631: KeywordWith at line 186, column 24 (length: 4) - 632: Identifier("short_arg") at line 186, column 29 (length: 9) - 633: KeywordEnd at line 187, column 5 (length: 3) - 634: KeywordFor at line 187, column 9 (length: 3) - 635: Identifier("else") at line 188, column 1 (length: 4) - 636: Colon at line 188, column 5 (length: 1) - 637: KeywordDisplay at line 189, column 5 (length: 7) - 638: StringLiteral("No arguments for filtering") at line 189, column 13 (length: 28) - 639: KeywordEnd at line 190, column 1 (length: 3) - 640: KeywordCheck at line 190, column 5 (length: 5) - 641: KeywordDisplay at line 191, column 1 (length: 7) - 642: StringLiteral("") at line 191, column 9 (length: 2) - 643: KeywordDisplay at line 194, column 1 (length: 7) - 644: StringLiteral("10. Execution Summary") at line 194, column 9 (length: 23) - 645: KeywordDisplay at line 195, column 1 (length: 7) - 646: StringLiteral("Program: ") at line 195, column 9 (length: 11) - 647: KeywordWith at line 195, column 21 (length: 4) - 648: Identifier("program_name") at line 195, column 26 (length: 12) - 649: KeywordDisplay at line 196, column 1 (length: 7) - 650: StringLiteral("Total arguments: ") at line 196, column 9 (length: 19) - 651: KeywordWith at line 196, column 29 (length: 4) - 652: Identifier("arg_count") at line 196, column 34 (length: 9) - 653: KeywordCheck at line 198, column 1 (length: 5) - 654: KeywordIf at line 198, column 7 (length: 2) - 655: Identifier("arg_count") at line 198, column 10 (length: 9) - 656: KeywordIs at line 198, column 20 (length: 2) - 657: KeywordGreater at line 198, column 23 (length: 7) - 658: KeywordThan at line 198, column 31 (length: 4) - 659: IntLiteral(0) at line 198, column 36 (length: 1) - 660: Colon at line 198, column 37 (length: 1) - 661: KeywordStore at line 199, column 5 (length: 5) - 662: Identifier("total_length") at line 199, column 11 (length: 12) - 663: KeywordAs at line 199, column 24 (length: 2) - 664: IntLiteral(0) at line 199, column 27 (length: 1) - 665: KeywordFor at line 200, column 5 (length: 3) - 666: KeywordEach at line 200, column 9 (length: 4) - 667: Identifier("arg") at line 200, column 14 (length: 3) - 668: KeywordIn at line 200, column 18 (length: 2) - 669: Identifier("args") at line 200, column 21 (length: 4) - 670: Colon at line 200, column 25 (length: 1) - 671: KeywordStore at line 201, column 9 (length: 5) - 672: Identifier("total_length") at line 201, column 15 (length: 12) - 673: KeywordAs at line 201, column 28 (length: 2) - 674: Identifier("total_length") at line 201, column 31 (length: 12) - 675: Plus at line 201, column 44 (length: 1) - 676: Identifier("length") at line 201, column 46 (length: 6) - 677: KeywordOf at line 201, column 53 (length: 2) - 678: Identifier("arg") at line 201, column 56 (length: 3) - 679: KeywordEnd at line 202, column 5 (length: 3) - 680: KeywordFor at line 202, column 9 (length: 3) - 681: KeywordDisplay at line 203, column 5 (length: 7) - 682: StringLiteral("Total character count: ") at line 203, column 13 (length: 25) - 683: KeywordWith at line 203, column 39 (length: 4) - 684: Identifier("total_length") at line 203, column 44 (length: 12) - 685: KeywordDisplay at line 204, column 5 (length: 7) - 686: StringLiteral("Average argument length: ") at line 204, column 13 (length: 27) - 687: KeywordWith at line 204, column 41 (length: 4) - 688: Identifier("total_length arg_count") at line 204, column 46 (length: 22) - 689: KeywordDisplay at line 206, column 5 (length: 7) - 690: StringLiteral("Shortest argument: ") at line 206, column 13 (length: 21) - 691: KeywordWith at line 206, column 35 (length: 4) - 692: Identifier("args") at line 206, column 40 (length: 4) - 693: LeftBracket at line 206, column 44 (length: 1) - 694: IntLiteral(0) at line 206, column 45 (length: 1) - 695: RightBracket at line 206, column 46 (length: 1) - 696: KeywordDisplay at line 207, column 5 (length: 7) - 697: StringLiteral("Arguments summary completed") at line 207, column 13 (length: 29) - 698: Identifier("else") at line 208, column 1 (length: 4) - 699: Colon at line 208, column 5 (length: 1) - 700: KeywordDisplay at line 209, column 5 (length: 7) - 701: StringLiteral("No arguments provided") at line 209, column 13 (length: 23) - 702: KeywordDisplay at line 210, column 5 (length: 7) - 703: StringLiteral("Try running with: program.wfl arg1 arg2 --flag value") at line 210, column 13 (length: 54) - 704: KeywordEnd at line 211, column 1 (length: 3) - 705: KeywordCheck at line 211, column 5 (length: 5) - 706: KeywordDisplay at line 212, column 1 (length: 7) - 707: StringLiteral("") at line 212, column 9 (length: 2) - 708: KeywordDisplay at line 214, column 1 (length: 7) - 709: StringLiteral("=== Command Line Arguments Tests Completed ===") at line 214, column 9 (length: 48) - 710: KeywordDisplay at line 215, column 1 (length: 7) - 711: StringLiteral("") at line 215, column 9 (length: 2) - 712: KeywordDisplay at line 216, column 1 (length: 7) - 713: StringLiteral("To test this program, run it with various arguments:") at line 216, column 9 (length: 54) - 714: KeywordDisplay at line 217, column 1 (length: 7) - 715: StringLiteral(" cargo run -- TestPrograms/args_comprehensive.wfl hello world 123") at line 217, column 9 (length: 68) - 716: KeywordDisplay at line 218, column 1 (length: 7) - 717: StringLiteral(" cargo run -- TestPrograms/args_comprehensive.wfl --help --verbose file.txt") at line 218, column 9 (length: 78) - 718: KeywordDisplay at line 219, column 1 (length: 7) - 719: StringLiteral(" cargo run -- TestPrograms/args_comprehensive.wfl") at line 219, column 9 (length: 52) diff --git a/TestPrograms/basic_syntax_comprehensive_debug.txt b/TestPrograms/basic_syntax_comprehensive_debug.txt deleted file mode 100644 index b6e5bd3c..00000000 --- a/TestPrograms/basic_syntax_comprehensive_debug.txt +++ /dev/null @@ -1,19 +0,0 @@ -=== WFL Debug Report === -Script: basic_syntax_comprehensive.wfl -Time: 2025-08-11 03:00:26 - -=== Error Summary === -Runtime error at line 103, column 37: Error in native function: Runtime error at line 0, column 0: Expected text, got List - -=== Stack Trace === -In main script at line 103, column 37 - -=== Source Code === - 101: store my numbers as [1 and 2 and 3 and 4 and 5] - 102: display "Number list: " with my numbers ->> 103: display "List length: " with length of my numbers - 104: display "" - 105: - -=== Local Variables === -(No local variables in global scope) diff --git a/TestPrograms/container_parsing_test.wfl b/TestPrograms/container_parsing_test.wfl new file mode 100644 index 00000000..71b2c6de --- /dev/null +++ b/TestPrograms/container_parsing_test.wfl @@ -0,0 +1,21 @@ +// Minimal test case to reproduce container parsing issues +// This file should fail to parse initially, then pass after fixes + +create container SimpleTest: + property name: Text + + action greet: + display "Hello" + end + + action set_name needs new_name: Text: + store name as new_name + end +end + +create new SimpleTest as test: + name is "Test" +end + +test.greet() +test.set_name("Fixed") \ No newline at end of file diff --git a/TestPrograms/container_parsing_test.wfl.ast.txt b/TestPrograms/container_parsing_test.wfl.ast.txt new file mode 100644 index 00000000..245f1db5 --- /dev/null +++ b/TestPrograms/container_parsing_test.wfl.ast.txt @@ -0,0 +1,147 @@ +AST output for: TestPrograms/container_parsing_test.wfl +============================================== + +Program with 4 statements: + +Statement #1: ContainerDefinition { + name: "SimpleTest", + extends: None, + implements: [], + properties: [ + PropertyDefinition { + name: "name", + property_type: Some( + Text, + ), + default_value: None, + validation_rules: [], + visibility: Public, + is_static: false, + line: 5, + column: 5, + }, + ], + methods: [ + ActionDefinition { + name: "greet", + parameters: [], + body: [ + DisplayStatement { + value: Literal( + String( + "Hello", + ), + 8, + 17, + ), + line: 9, + column: 5, + }, + ], + return_type: None, + line: 0, + column: 0, + }, + ActionDefinition { + name: "set_name", + parameters: [ + Parameter { + name: "new_name", + param_type: Some( + Text, + ), + default_value: None, + line: 11, + column: 27, + }, + ], + body: [ + VariableDeclaration { + name: "name", + value: Variable( + "new_name", + 12, + 23, + ), + is_constant: false, + line: 12, + column: 9, + }, + ], + return_type: None, + line: 0, + column: 0, + }, + ], + events: [], + static_properties: [], + static_methods: [], + line: 4, + column: 1, +} + +Statement #2: ContainerInstantiation { + container_type: "SimpleTest", + instance_name: "test", + arguments: [], + property_initializers: [ + PropertyInitializer { + name: "name", + value: Literal( + String( + "Test", + ), + 17, + 13, + ), + line: 17, + column: 5, + }, + ], + line: 16, + column: 1, +} + +Statement #3: ExpressionStatement { + expression: MethodCall { + object: Variable( + "test", + 20, + 1, + ), + method: "greet", + arguments: [], + line: 20, + column: 1, + }, + line: 21, + column: 1, +} + +Statement #4: ExpressionStatement { + expression: MethodCall { + object: Variable( + "test", + 21, + 1, + ), + method: "set_name", + arguments: [ + Argument { + name: None, + value: Literal( + String( + "Fixed", + ), + 21, + 15, + ), + }, + ], + line: 21, + column: 1, + }, + line: 0, + column: 0, +} + diff --git a/TestPrograms/container_property_access_test.wfl b/TestPrograms/container_property_access_test.wfl new file mode 100644 index 00000000..de0236c9 --- /dev/null +++ b/TestPrograms/container_property_access_test.wfl @@ -0,0 +1,15 @@ +// Test for container property access in methods +create container Person: + property name: Text + + action get_name: + return name + end +end + +create new Person as alice: + name is "Alice" +end + +store result as alice.get_name() +display result \ No newline at end of file diff --git a/TestPrograms/container_property_access_test.wfl.ast.txt b/TestPrograms/container_property_access_test.wfl.ast.txt new file mode 100644 index 00000000..1ab9d9f2 --- /dev/null +++ b/TestPrograms/container_property_access_test.wfl.ast.txt @@ -0,0 +1,111 @@ +AST output for: TestPrograms/container_property_access_test.wfl +============================================== + +Program with 4 statements: + +Statement #1: ContainerDefinition { + name: "Person", + extends: None, + implements: [], + properties: [ + PropertyDefinition { + name: "name", + property_type: Some( + Text, + ), + default_value: None, + validation_rules: [], + visibility: Public, + is_static: false, + line: 3, + column: 5, + }, + ], + methods: [ + ActionDefinition { + name: "get_name", + parameters: [], + body: [ + ExpressionStatement { + expression: Variable( + "Text", + 5, + 22, + ), + line: 6, + column: 9, + }, + ReturnStatement { + value: Some( + Variable( + "name", + 6, + 16, + ), + ), + line: 6, + column: 9, + }, + ], + return_type: None, + line: 0, + column: 0, + }, + ], + events: [], + static_properties: [], + static_methods: [], + line: 2, + column: 1, +} + +Statement #2: ContainerInstantiation { + container_type: "Person", + instance_name: "alice", + arguments: [], + property_initializers: [ + PropertyInitializer { + name: "name", + value: Literal( + String( + "Alice", + ), + 11, + 13, + ), + line: 11, + column: 5, + }, + ], + line: 10, + column: 1, +} + +Statement #3: VariableDeclaration { + name: "result", + value: MethodCall { + object: Variable( + "alice", + 14, + 17, + ), + method: "get_name", + arguments: [], + line: 14, + column: 17, + }, + is_constant: false, + line: 14, + column: 1, +} + +Statement #4: DisplayStatement { + value: Variable( + "result", + 15, + 9, + ), + line: 15, + column: 9, +} + diff --git a/TestPrograms/containers_comprehensive.wfl b/TestPrograms/containers_comprehensive.wfl index 087a114e..70d7c6c8 100644 --- a/TestPrograms/containers_comprehensive.wfl +++ b/TestPrograms/containers_comprehensive.wfl @@ -15,7 +15,7 @@ create container Person: display "Hello, I am " with name with " and I am " with age with " years old." end - action set_email with new_email: Text: + action set_email needs new_email: Text: store email as new_email display "Email set to: " with email end @@ -51,7 +51,7 @@ create container Employee extends Person: return job_title with " earns $" with salary end - action give_raise with amount: Number: + action give_raise needs amount: Number: store salary as salary + amount display name with " received a raise of $" with amount end @@ -72,10 +72,7 @@ display "" // === Interface Implementation === display "3. Interface Implementation Test" -create interface Drawable: - action draw - action get_area: Number -end +create interface Drawable create container Rectangle implements Drawable: property width: Number @@ -86,10 +83,10 @@ create container Rectangle implements Drawable: end action get_area: Number - return width * height + return width times height end - action set_dimensions with w: Number and h: Number: + action set_dimensions needs w: Number, h: Number: store width as w store height as h end @@ -146,7 +143,7 @@ create container TypedContainer: property num_prop: Number property bool_prop: Boolean - action set_props with t: Text and n: Number and b: Boolean: + action set_props needs t: Text, n: Number, b: Boolean: store text_prop as t store num_prop as n store bool_prop as b diff --git a/TestPrograms/containers_comprehensive.wfl.ast.txt b/TestPrograms/containers_comprehensive.wfl.ast.txt new file mode 100644 index 00000000..40b51c75 --- /dev/null +++ b/TestPrograms/containers_comprehensive.wfl.ast.txt @@ -0,0 +1,2295 @@ +AST output for: TestPrograms/containers_comprehensive.wfl +============================================== + +Program with 53 statements: + +Statement #1: DisplayStatement { + value: Literal( + String( + "=== WFL Container System Comprehensive Test ===", + ), + 4, + 9, + ), + line: 5, + column: 1, +} + +Statement #2: DisplayStatement { + value: Literal( + String( + "", + ), + 5, + 9, + ), + line: 8, + column: 1, +} + +Statement #3: DisplayStatement { + value: Literal( + String( + "1. Basic Container Test", + ), + 8, + 9, + ), + line: 9, + column: 1, +} + +Statement #4: ContainerDefinition { + name: "Person", + extends: None, + implements: [], + properties: [ + PropertyDefinition { + name: "name", + property_type: Some( + Text, + ), + default_value: None, + validation_rules: [], + visibility: Public, + is_static: false, + line: 10, + column: 5, + }, + PropertyDefinition { + name: "age", + property_type: Some( + Number, + ), + default_value: None, + validation_rules: [], + visibility: Public, + is_static: false, + line: 11, + column: 5, + }, + PropertyDefinition { + name: "email", + property_type: Some( + Text, + ), + default_value: None, + validation_rules: [], + visibility: Public, + is_static: false, + line: 12, + column: 5, + }, + ], + methods: [ + ActionDefinition { + name: "greet", + parameters: [], + body: [ + DisplayStatement { + value: Concatenation { + left: Literal( + String( + "Hello, I am ", + ), + 15, + 17, + ), + right: Concatenation { + left: Variable( + "name", + 15, + 37, + ), + right: Concatenation { + left: Literal( + String( + " and I am ", + ), + 15, + 47, + ), + right: Concatenation { + left: Variable( + "age", + 15, + 65, + ), + right: Literal( + String( + " years old.", + ), + 15, + 74, + ), + line: 15, + column: 69, + }, + line: 15, + column: 60, + }, + line: 15, + column: 42, + }, + line: 15, + column: 32, + }, + line: 16, + column: 5, + }, + ], + return_type: None, + line: 0, + column: 0, + }, + ActionDefinition { + name: "set_email", + parameters: [ + Parameter { + name: "new_email", + param_type: Some( + Text, + ), + default_value: None, + line: 18, + column: 28, + }, + ], + body: [ + VariableDeclaration { + name: "email", + value: Variable( + "new_email", + 19, + 24, + ), + is_constant: false, + line: 19, + column: 9, + }, + DisplayStatement { + value: Concatenation { + left: Literal( + String( + "Email set to: ", + ), + 20, + 17, + ), + right: Variable( + "email", + 20, + 39, + ), + line: 20, + column: 34, + }, + line: 21, + column: 5, + }, + ], + return_type: None, + line: 0, + column: 0, + }, + ActionDefinition { + name: "get_info", + parameters: [], + body: [ + ReturnStatement { + value: Some( + Concatenation { + left: Variable( + "name", + 24, + 16, + ), + right: Concatenation { + left: Literal( + String( + " (", + ), + 24, + 26, + ), + right: Concatenation { + left: Variable( + "age", + 24, + 36, + ), + right: Literal( + String( + " years old)", + ), + 24, + 45, + ), + line: 24, + column: 40, + }, + line: 24, + column: 31, + }, + line: 24, + column: 21, + }, + ), + line: 24, + column: 9, + }, + ], + return_type: Some( + Text, + ), + line: 0, + column: 0, + }, + ], + events: [], + static_properties: [], + static_methods: [], + line: 9, + column: 1, +} + +Statement #5: ContainerInstantiation { + container_type: "Person", + instance_name: "alice", + arguments: [], + property_initializers: [ + PropertyInitializer { + name: "name", + value: Literal( + String( + "Alice", + ), + 29, + 13, + ), + line: 29, + column: 5, + }, + PropertyInitializer { + name: "age", + value: Literal( + Integer( + 28, + ), + 30, + 12, + ), + line: 30, + column: 5, + }, + PropertyInitializer { + name: "email", + value: Literal( + String( + "alice@example.com", + ), + 31, + 14, + ), + line: 31, + column: 5, + }, + ], + line: 28, + column: 1, +} + +Statement #6: ExpressionStatement { + expression: MethodCall { + object: Variable( + "alice", + 34, + 1, + ), + method: "greet", + arguments: [], + line: 34, + column: 1, + }, + line: 35, + column: 1, +} + +Statement #7: ExpressionStatement { + expression: MethodCall { + object: Variable( + "alice", + 35, + 1, + ), + method: "set_email", + arguments: [ + Argument { + name: None, + value: Literal( + String( + "alice.smith@example.com", + ), + 35, + 17, + ), + }, + ], + line: 35, + column: 1, + }, + line: 36, + column: 1, +} + +Statement #8: VariableDeclaration { + name: "info", + value: MethodCall { + object: Variable( + "alice", + 36, + 15, + ), + method: "get_info", + arguments: [], + line: 36, + column: 15, + }, + is_constant: false, + line: 36, + column: 1, +} + +Statement #9: DisplayStatement { + value: Concatenation { + left: Literal( + String( + "Person info: ", + ), + 37, + 9, + ), + right: Variable( + "info", + 37, + 30, + ), + line: 37, + column: 25, + }, + line: 38, + column: 1, +} + +Statement #10: DisplayStatement { + value: Literal( + String( + "", + ), + 38, + 9, + ), + line: 41, + column: 1, +} + +Statement #11: DisplayStatement { + value: Literal( + String( + "2. Container Inheritance Test", + ), + 41, + 9, + ), + line: 42, + column: 1, +} + +Statement #12: ContainerDefinition { + name: "Employee", + extends: Some( + "Person", + ), + implements: [], + properties: [ + PropertyDefinition { + name: "job_title", + property_type: Some( + Text, + ), + default_value: None, + validation_rules: [], + visibility: Public, + is_static: false, + line: 43, + column: 5, + }, + PropertyDefinition { + name: "salary", + property_type: Some( + Number, + ), + default_value: None, + validation_rules: [], + visibility: Public, + is_static: false, + line: 44, + column: 5, + }, + ], + methods: [ + ActionDefinition { + name: "greet", + parameters: [], + body: [ + DisplayStatement { + value: Concatenation { + left: Literal( + String( + "Hello, I am ", + ), + 47, + 17, + ), + right: Concatenation { + left: Variable( + "name", + 47, + 37, + ), + right: Concatenation { + left: Literal( + String( + ", ", + ), + 47, + 47, + ), + right: Concatenation { + left: Variable( + "job_title", + 47, + 57, + ), + right: Literal( + String( + " at your service.", + ), + 47, + 72, + ), + line: 47, + column: 67, + }, + line: 47, + column: 52, + }, + line: 47, + column: 42, + }, + line: 47, + column: 32, + }, + line: 48, + column: 5, + }, + ], + return_type: None, + line: 0, + column: 0, + }, + ActionDefinition { + name: "get_salary_info", + parameters: [], + body: [ + ReturnStatement { + value: Some( + Concatenation { + left: Variable( + "job_title", + 51, + 16, + ), + right: Concatenation { + left: Literal( + String( + " earns $", + ), + 51, + 31, + ), + right: Variable( + "salary", + 51, + 47, + ), + line: 51, + column: 42, + }, + line: 51, + column: 26, + }, + ), + line: 51, + column: 9, + }, + ], + return_type: Some( + Text, + ), + line: 0, + column: 0, + }, + ActionDefinition { + name: "give_raise", + parameters: [ + Parameter { + name: "amount", + param_type: Some( + Number, + ), + default_value: None, + line: 54, + column: 29, + }, + ], + body: [ + VariableDeclaration { + name: "salary", + value: BinaryOperation { + left: Variable( + "salary", + 55, + 25, + ), + operator: Plus, + right: Variable( + "amount", + 55, + 34, + ), + line: 55, + column: 32, + }, + is_constant: false, + line: 55, + column: 9, + }, + DisplayStatement { + value: Concatenation { + left: Variable( + "name", + 56, + 17, + ), + right: Concatenation { + left: Literal( + String( + " received a raise of $", + ), + 56, + 27, + ), + right: Variable( + "amount", + 56, + 57, + ), + line: 56, + column: 52, + }, + line: 56, + column: 22, + }, + line: 57, + column: 5, + }, + ], + return_type: None, + line: 0, + column: 0, + }, + ], + events: [], + static_properties: [], + static_methods: [], + line: 42, + column: 1, +} + +Statement #13: ContainerInstantiation { + container_type: "Employee", + instance_name: "bob", + arguments: [], + property_initializers: [ + PropertyInitializer { + name: "name", + value: Literal( + String( + "Bob", + ), + 61, + 13, + ), + line: 61, + column: 5, + }, + PropertyInitializer { + name: "age", + value: Literal( + Integer( + 35, + ), + 62, + 12, + ), + line: 62, + column: 5, + }, + PropertyInitializer { + name: "job_title", + value: Literal( + String( + "Developer", + ), + 63, + 18, + ), + line: 63, + column: 5, + }, + PropertyInitializer { + name: "salary", + value: Literal( + Integer( + 75000, + ), + 64, + 15, + ), + line: 64, + column: 5, + }, + ], + line: 60, + column: 1, +} + +Statement #14: ExpressionStatement { + expression: MethodCall { + object: Variable( + "bob", + 67, + 1, + ), + method: "greet", + arguments: [], + line: 67, + column: 1, + }, + line: 68, + column: 1, +} + +Statement #15: VariableDeclaration { + name: "salary_info", + value: MethodCall { + object: Variable( + "bob", + 68, + 22, + ), + method: "get_salary_info", + arguments: [], + line: 68, + column: 22, + }, + is_constant: false, + line: 68, + column: 1, +} + +Statement #16: DisplayStatement { + value: Variable( + "salary_info", + 69, + 9, + ), + line: 70, + column: 1, +} + +Statement #17: ExpressionStatement { + expression: MethodCall { + object: Variable( + "bob", + 70, + 1, + ), + method: "give_raise", + arguments: [ + Argument { + name: None, + value: Literal( + Integer( + 5000, + ), + 70, + 16, + ), + }, + ], + line: 70, + column: 1, + }, + line: 71, + column: 1, +} + +Statement #18: DisplayStatement { + value: Literal( + String( + "", + ), + 71, + 9, + ), + line: 74, + column: 1, +} + +Statement #19: DisplayStatement { + value: Literal( + String( + "3. Interface Implementation Test", + ), + 74, + 9, + ), + line: 75, + column: 1, +} + +Statement #20: InterfaceDefinition { + name: "Drawable", + extends: [], + required_actions: [], + line: 75, + column: 1, +} + +Statement #21: ContainerDefinition { + name: "Rectangle", + extends: None, + implements: [ + "Drawable", + ], + properties: [ + PropertyDefinition { + name: "width", + property_type: Some( + Number, + ), + default_value: None, + validation_rules: [], + visibility: Public, + is_static: false, + line: 78, + column: 5, + }, + PropertyDefinition { + name: "height", + property_type: Some( + Number, + ), + default_value: None, + validation_rules: [], + visibility: Public, + is_static: false, + line: 79, + column: 5, + }, + ], + methods: [ + ActionDefinition { + name: "draw", + parameters: [], + body: [ + DisplayStatement { + value: Concatenation { + left: Literal( + String( + "Drawing rectangle: ", + ), + 82, + 17, + ), + right: Concatenation { + left: Variable( + "width", + 82, + 44, + ), + right: Concatenation { + left: Literal( + String( + " x ", + ), + 82, + 55, + ), + right: Variable( + "height", + 82, + 66, + ), + line: 82, + column: 61, + }, + line: 82, + column: 50, + }, + line: 82, + column: 39, + }, + line: 83, + column: 5, + }, + ], + return_type: None, + line: 0, + column: 0, + }, + ActionDefinition { + name: "get_area", + parameters: [], + body: [ + ReturnStatement { + value: Some( + BinaryOperation { + left: Variable( + "width", + 86, + 16, + ), + operator: Multiply, + right: Variable( + "height", + 86, + 28, + ), + line: 86, + column: 22, + }, + ), + line: 86, + column: 9, + }, + ], + return_type: Some( + Number, + ), + line: 0, + column: 0, + }, + ActionDefinition { + name: "set_dimensions", + parameters: [ + Parameter { + name: "w", + param_type: Some( + Number, + ), + default_value: None, + line: 89, + column: 33, + }, + Parameter { + name: "h", + param_type: Some( + Number, + ), + default_value: None, + line: 89, + column: 44, + }, + ], + body: [ + VariableDeclaration { + name: "width", + value: Variable( + "w", + 90, + 24, + ), + is_constant: false, + line: 90, + column: 9, + }, + VariableDeclaration { + name: "height", + value: Variable( + "h", + 91, + 25, + ), + is_constant: false, + line: 91, + column: 9, + }, + ], + return_type: None, + line: 0, + column: 0, + }, + ], + events: [], + static_properties: [], + static_methods: [], + line: 77, + column: 1, +} + +Statement #22: ContainerInstantiation { + container_type: "Rectangle", + instance_name: "rect", + arguments: [], + property_initializers: [ + PropertyInitializer { + name: "width", + value: Literal( + Integer( + 10, + ), + 96, + 14, + ), + line: 96, + column: 5, + }, + PropertyInitializer { + name: "height", + value: Literal( + Integer( + 5, + ), + 97, + 15, + ), + line: 97, + column: 5, + }, + ], + line: 95, + column: 1, +} + +Statement #23: ExpressionStatement { + expression: MethodCall { + object: Variable( + "rect", + 100, + 1, + ), + method: "draw", + arguments: [], + line: 100, + column: 1, + }, + line: 101, + column: 1, +} + +Statement #24: VariableDeclaration { + name: "area", + value: MethodCall { + object: Variable( + "rect", + 101, + 15, + ), + method: "get_area", + arguments: [], + line: 101, + column: 15, + }, + is_constant: false, + line: 101, + column: 1, +} + +Statement #25: DisplayStatement { + value: Concatenation { + left: Literal( + String( + "Rectangle area: ", + ), + 102, + 9, + ), + right: Variable( + "area", + 102, + 33, + ), + line: 102, + column: 28, + }, + line: 103, + column: 1, +} + +Statement #26: ExpressionStatement { + expression: MethodCall { + object: Variable( + "rect", + 103, + 1, + ), + method: "set_dimensions", + arguments: [ + Argument { + name: None, + value: Literal( + Integer( + 15, + ), + 103, + 21, + ), + }, + Argument { + name: None, + value: Literal( + Integer( + 8, + ), + 103, + 25, + ), + }, + ], + line: 103, + column: 1, + }, + line: 104, + column: 1, +} + +Statement #27: ExpressionStatement { + expression: MethodCall { + object: Variable( + "rect", + 104, + 1, + ), + method: "draw", + arguments: [], + line: 104, + column: 1, + }, + line: 105, + column: 1, +} + +Statement #28: DisplayStatement { + value: Concatenation { + left: Literal( + String( + "New area: ", + ), + 105, + 9, + ), + right: MethodCall { + object: Variable( + "rect", + 105, + 27, + ), + method: "get_area", + arguments: [], + line: 105, + column: 27, + }, + line: 105, + column: 22, + }, + line: 106, + column: 1, +} + +Statement #29: DisplayStatement { + value: Literal( + String( + "", + ), + 106, + 9, + ), + line: 109, + column: 1, +} + +Statement #30: DisplayStatement { + value: Literal( + String( + "4. Container Events Test", + ), + 109, + 9, + ), + line: 110, + column: 1, +} + +Statement #31: ContainerDefinition { + name: "Button", + extends: None, + implements: [], + properties: [ + PropertyDefinition { + name: "label", + property_type: Some( + Text, + ), + default_value: None, + validation_rules: [], + visibility: Public, + is_static: false, + line: 111, + column: 5, + }, + PropertyDefinition { + name: "clicked", + property_type: Some( + Number, + ), + default_value: None, + validation_rules: [], + visibility: Public, + is_static: false, + line: 112, + column: 5, + }, + ], + methods: [ + ActionDefinition { + name: "click", + parameters: [], + body: [ + VariableDeclaration { + name: "clicked", + value: BinaryOperation { + left: Variable( + "clicked", + 118, + 26, + ), + operator: Plus, + right: Literal( + Integer( + 1, + ), + 118, + 36, + ), + line: 118, + column: 34, + }, + is_constant: false, + line: 118, + column: 9, + }, + DisplayStatement { + value: Concatenation { + left: Literal( + String( + "Button '", + ), + 119, + 17, + ), + right: Concatenation { + left: Variable( + "label", + 119, + 33, + ), + right: Concatenation { + left: Literal( + String( + "' clicked ", + ), + 119, + 44, + ), + right: Concatenation { + left: Variable( + "clicked", + 119, + 62, + ), + right: Literal( + String( + " times", + ), + 119, + 75, + ), + line: 119, + column: 70, + }, + line: 119, + column: 57, + }, + line: 119, + column: 39, + }, + line: 119, + column: 28, + }, + line: 120, + column: 9, + }, + EventTrigger { + name: "on_click", + arguments: [], + line: 120, + column: 9, + }, + ], + return_type: None, + line: 0, + column: 0, + }, + ActionDefinition { + name: "hover", + parameters: [], + body: [ + DisplayStatement { + value: Concatenation { + left: Literal( + String( + "Hovering over '", + ), + 124, + 17, + ), + right: Concatenation { + left: Variable( + "label", + 124, + 40, + ), + right: Literal( + String( + "'", + ), + 124, + 51, + ), + line: 124, + column: 46, + }, + line: 124, + column: 35, + }, + line: 125, + column: 9, + }, + EventTrigger { + name: "on_hover", + arguments: [], + line: 125, + column: 9, + }, + ], + return_type: None, + line: 0, + column: 0, + }, + ], + events: [ + EventDefinition { + name: "on_click", + parameters: [], + line: 114, + column: 5, + }, + EventDefinition { + name: "on_hover", + parameters: [], + line: 115, + column: 5, + }, + ], + static_properties: [], + static_methods: [], + line: 110, + column: 1, +} + +Statement #32: ContainerInstantiation { + container_type: "Button", + instance_name: "my_button", + arguments: [], + property_initializers: [ + PropertyInitializer { + name: "label", + value: Literal( + String( + "Submit", + ), + 130, + 14, + ), + line: 130, + column: 5, + }, + PropertyInitializer { + name: "clicked", + value: Literal( + Integer( + 0, + ), + 131, + 16, + ), + line: 131, + column: 5, + }, + ], + line: 129, + column: 1, +} + +Statement #33: ExpressionStatement { + expression: MethodCall { + object: Variable( + "my_button", + 134, + 1, + ), + method: "click", + arguments: [], + line: 134, + column: 1, + }, + line: 135, + column: 1, +} + +Statement #34: ExpressionStatement { + expression: MethodCall { + object: Variable( + "my_button", + 135, + 1, + ), + method: "hover", + arguments: [], + line: 135, + column: 1, + }, + line: 136, + column: 1, +} + +Statement #35: ExpressionStatement { + expression: MethodCall { + object: Variable( + "my_button", + 136, + 1, + ), + method: "click", + arguments: [], + line: 136, + column: 1, + }, + line: 137, + column: 1, +} + +Statement #36: DisplayStatement { + value: Literal( + String( + "", + ), + 137, + 9, + ), + line: 140, + column: 1, +} + +Statement #37: DisplayStatement { + value: Literal( + String( + "5. Container Type Checking Test", + ), + 140, + 9, + ), + line: 141, + column: 1, +} + +Statement #38: ContainerDefinition { + name: "TypedContainer", + extends: None, + implements: [], + properties: [ + PropertyDefinition { + name: "text_prop", + property_type: Some( + Text, + ), + default_value: None, + validation_rules: [], + visibility: Public, + is_static: false, + line: 142, + column: 5, + }, + PropertyDefinition { + name: "num_prop", + property_type: Some( + Number, + ), + default_value: None, + validation_rules: [], + visibility: Public, + is_static: false, + line: 143, + column: 5, + }, + PropertyDefinition { + name: "bool_prop", + property_type: Some( + Boolean, + ), + default_value: None, + validation_rules: [], + visibility: Public, + is_static: false, + line: 144, + column: 5, + }, + ], + methods: [ + ActionDefinition { + name: "set_props", + parameters: [ + Parameter { + name: "t", + param_type: Some( + Text, + ), + default_value: None, + line: 146, + column: 28, + }, + Parameter { + name: "n", + param_type: Some( + Number, + ), + default_value: None, + line: 146, + column: 37, + }, + Parameter { + name: "b", + param_type: Some( + Boolean, + ), + default_value: None, + line: 146, + column: 48, + }, + ], + body: [ + VariableDeclaration { + name: "text_prop", + value: Variable( + "t", + 147, + 28, + ), + is_constant: false, + line: 147, + column: 9, + }, + VariableDeclaration { + name: "num_prop", + value: Variable( + "n", + 148, + 27, + ), + is_constant: false, + line: 148, + column: 9, + }, + VariableDeclaration { + name: "bool_prop", + value: Variable( + "b", + 149, + 28, + ), + is_constant: false, + line: 149, + column: 9, + }, + ], + return_type: None, + line: 0, + column: 0, + }, + ActionDefinition { + name: "display_props", + parameters: [], + body: [ + DisplayStatement { + value: Concatenation { + left: Literal( + String( + "Text: ", + ), + 153, + 17, + ), + right: Concatenation { + left: Variable( + "text_prop", + 153, + 31, + ), + right: Concatenation { + left: Literal( + String( + " (type: ", + ), + 153, + 46, + ), + right: Concatenation { + left: FunctionCall { + function: Variable( + "typeof", + 153, + 62, + ), + arguments: [ + Argument { + name: None, + value: Variable( + "text_prop", + 153, + 72, + ), + }, + ], + line: 153, + column: 69, + }, + right: Literal( + String( + ")", + ), + 153, + 87, + ), + line: 153, + column: 82, + }, + line: 153, + column: 57, + }, + line: 153, + column: 41, + }, + line: 153, + column: 26, + }, + line: 154, + column: 9, + }, + DisplayStatement { + value: Concatenation { + left: Literal( + String( + "Number: ", + ), + 154, + 17, + ), + right: Concatenation { + left: Variable( + "num_prop", + 154, + 33, + ), + right: Concatenation { + left: Literal( + String( + " (type: ", + ), + 154, + 47, + ), + right: Concatenation { + left: FunctionCall { + function: Variable( + "typeof", + 154, + 63, + ), + arguments: [ + Argument { + name: None, + value: Variable( + "num_prop", + 154, + 73, + ), + }, + ], + line: 154, + column: 70, + }, + right: Literal( + String( + ")", + ), + 154, + 87, + ), + line: 154, + column: 82, + }, + line: 154, + column: 58, + }, + line: 154, + column: 42, + }, + line: 154, + column: 28, + }, + line: 155, + column: 9, + }, + DisplayStatement { + value: Concatenation { + left: Literal( + String( + "Boolean: ", + ), + 155, + 17, + ), + right: Concatenation { + left: Variable( + "bool_prop", + 155, + 34, + ), + right: Concatenation { + left: Literal( + String( + " (type: ", + ), + 155, + 49, + ), + right: Concatenation { + left: FunctionCall { + function: Variable( + "typeof", + 155, + 65, + ), + arguments: [ + Argument { + name: None, + value: Variable( + "bool_prop", + 155, + 75, + ), + }, + ], + line: 155, + column: 72, + }, + right: Literal( + String( + ")", + ), + 155, + 90, + ), + line: 155, + column: 85, + }, + line: 155, + column: 60, + }, + line: 155, + column: 44, + }, + line: 155, + column: 29, + }, + line: 156, + column: 5, + }, + ], + return_type: None, + line: 0, + column: 0, + }, + ], + events: [], + static_properties: [], + static_methods: [], + line: 141, + column: 1, +} + +Statement #39: ContainerInstantiation { + container_type: "TypedContainer", + instance_name: "typed_obj", + arguments: [], + property_initializers: [ + PropertyInitializer { + name: "text_prop", + value: Literal( + String( + "Hello", + ), + 160, + 18, + ), + line: 160, + column: 5, + }, + PropertyInitializer { + name: "num_prop", + value: Literal( + Integer( + 42, + ), + 161, + 17, + ), + line: 161, + column: 5, + }, + PropertyInitializer { + name: "bool_prop", + value: Literal( + Boolean( + true, + ), + 162, + 18, + ), + line: 162, + column: 5, + }, + ], + line: 159, + column: 1, +} + +Statement #40: ExpressionStatement { + expression: MethodCall { + object: Variable( + "typed_obj", + 165, + 1, + ), + method: "display_props", + arguments: [], + line: 165, + column: 1, + }, + line: 166, + column: 1, +} + +Statement #41: ExpressionStatement { + expression: MethodCall { + object: Variable( + "typed_obj", + 166, + 1, + ), + method: "set_props", + arguments: [ + Argument { + name: None, + value: Literal( + String( + "World", + ), + 166, + 21, + ), + }, + Argument { + name: None, + value: Literal( + Integer( + 84, + ), + 166, + 30, + ), + }, + Argument { + name: None, + value: Literal( + Boolean( + false, + ), + 166, + 34, + ), + }, + ], + line: 166, + column: 1, + }, + line: 167, + column: 1, +} + +Statement #42: ExpressionStatement { + expression: MethodCall { + object: Variable( + "typed_obj", + 167, + 1, + ), + method: "display_props", + arguments: [], + line: 167, + column: 1, + }, + line: 168, + column: 1, +} + +Statement #43: DisplayStatement { + value: Literal( + String( + "", + ), + 168, + 9, + ), + line: 171, + column: 1, +} + +Statement #44: DisplayStatement { + value: Literal( + String( + "6. Multi-level Inheritance Test", + ), + 171, + 9, + ), + line: 172, + column: 1, +} + +Statement #45: ContainerDefinition { + name: "Animal", + extends: None, + implements: [], + properties: [ + PropertyDefinition { + name: "species", + property_type: Some( + Text, + ), + default_value: None, + validation_rules: [], + visibility: Public, + is_static: false, + line: 173, + column: 5, + }, + ], + methods: [ + ActionDefinition { + name: "make_sound", + parameters: [], + body: [ + DisplayStatement { + value: Concatenation { + left: Literal( + String( + "The ", + ), + 176, + 17, + ), + right: Concatenation { + left: Variable( + "species", + 176, + 29, + ), + right: Literal( + String( + " makes a sound", + ), + 176, + 42, + ), + line: 176, + column: 37, + }, + line: 176, + column: 24, + }, + line: 177, + column: 5, + }, + ], + return_type: None, + line: 0, + column: 0, + }, + ], + events: [], + static_properties: [], + static_methods: [], + line: 172, + column: 1, +} + +Statement #46: ContainerDefinition { + name: "Mammal", + extends: Some( + "Animal", + ), + implements: [], + properties: [ + PropertyDefinition { + name: "fur_color", + property_type: Some( + Text, + ), + default_value: None, + validation_rules: [], + visibility: Public, + is_static: false, + line: 181, + column: 5, + }, + ], + methods: [ + ActionDefinition { + name: "shed_fur", + parameters: [], + body: [ + DisplayStatement { + value: Concatenation { + left: Literal( + String( + "The ", + ), + 184, + 17, + ), + right: Concatenation { + left: Variable( + "species", + 184, + 29, + ), + right: Concatenation { + left: Literal( + String( + " sheds ", + ), + 184, + 42, + ), + right: Concatenation { + left: Variable( + "fur_color", + 184, + 57, + ), + right: Literal( + String( + " fur", + ), + 184, + 72, + ), + line: 184, + column: 67, + }, + line: 184, + column: 52, + }, + line: 184, + column: 37, + }, + line: 184, + column: 24, + }, + line: 185, + column: 5, + }, + ], + return_type: None, + line: 0, + column: 0, + }, + ], + events: [], + static_properties: [], + static_methods: [], + line: 180, + column: 1, +} + +Statement #47: ContainerDefinition { + name: "Dog", + extends: Some( + "Mammal", + ), + implements: [], + properties: [ + PropertyDefinition { + name: "breed", + property_type: Some( + Text, + ), + default_value: None, + validation_rules: [], + visibility: Public, + is_static: false, + line: 189, + column: 5, + }, + ], + methods: [ + ActionDefinition { + name: "make_sound", + parameters: [], + body: [ + DisplayStatement { + value: Concatenation { + left: Literal( + String( + "The ", + ), + 192, + 17, + ), + right: Concatenation { + left: Variable( + "breed", + 192, + 29, + ), + right: Literal( + String( + " dog barks!", + ), + 192, + 40, + ), + line: 192, + column: 35, + }, + line: 192, + column: 24, + }, + line: 193, + column: 5, + }, + ], + return_type: None, + line: 0, + column: 0, + }, + ActionDefinition { + name: "fetch", + parameters: [], + body: [ + DisplayStatement { + value: Concatenation { + left: Literal( + String( + "The ", + ), + 196, + 17, + ), + right: Concatenation { + left: Variable( + "breed", + 196, + 29, + ), + right: Literal( + String( + " fetches the ball", + ), + 196, + 40, + ), + line: 196, + column: 35, + }, + line: 196, + column: 24, + }, + line: 197, + column: 5, + }, + ], + return_type: None, + line: 0, + column: 0, + }, + ], + events: [], + static_properties: [], + static_methods: [], + line: 188, + column: 1, +} + +Statement #48: ContainerInstantiation { + container_type: "Dog", + instance_name: "buddy", + arguments: [], + property_initializers: [ + PropertyInitializer { + name: "species", + value: Literal( + String( + "Canis lupus", + ), + 201, + 16, + ), + line: 201, + column: 5, + }, + PropertyInitializer { + name: "fur_color", + value: Literal( + String( + "golden", + ), + 202, + 18, + ), + line: 202, + column: 5, + }, + PropertyInitializer { + name: "breed", + value: Literal( + String( + "Golden Retriever", + ), + 203, + 14, + ), + line: 203, + column: 5, + }, + ], + line: 200, + column: 1, +} + +Statement #49: ExpressionStatement { + expression: MethodCall { + object: Variable( + "buddy", + 206, + 1, + ), + method: "make_sound", + arguments: [], + line: 206, + column: 1, + }, + line: 207, + column: 1, +} + +Statement #50: ExpressionStatement { + expression: MethodCall { + object: Variable( + "buddy", + 207, + 1, + ), + method: "shed_fur", + arguments: [], + line: 207, + column: 1, + }, + line: 208, + column: 1, +} + +Statement #51: ExpressionStatement { + expression: MethodCall { + object: Variable( + "buddy", + 208, + 1, + ), + method: "fetch", + arguments: [], + line: 208, + column: 1, + }, + line: 209, + column: 1, +} + +Statement #52: DisplayStatement { + value: Literal( + String( + "", + ), + 209, + 9, + ), + line: 211, + column: 1, +} + +Statement #53: DisplayStatement { + value: Literal( + String( + "=== Container System Tests Completed ===", + ), + 211, + 9, + ), + line: 211, + column: 9, +} + diff --git a/TestPrograms/debug_container_method.wfl b/TestPrograms/debug_container_method.wfl new file mode 100644 index 00000000..993bce9f --- /dev/null +++ b/TestPrograms/debug_container_method.wfl @@ -0,0 +1,13 @@ +create container Person: + property name: Text + + action greet: + display "Hello, I am " with name + end +end + +create new Person as alice: + name is "Alice" +end + +alice.greet() \ No newline at end of file diff --git a/TestPrograms/stack_overflow_test.wfl b/TestPrograms/stack_overflow_test.wfl new file mode 100644 index 00000000..8e096259 --- /dev/null +++ b/TestPrograms/stack_overflow_test.wfl @@ -0,0 +1,44 @@ +// Test case to reproduce stack overflow with nested operations +// This should trigger the same async recursion issue as args_comprehensive.wfl + +store test_args as ["--azusa" and "--ui" and "--mio" and "--ritsu"] +store result as [] + +for each arg in test_args: + store current as arg + + check if substring of current and 0 and 2 is "--": + store flag_name as substring of current and 2 and length of current + + check if flag_name is "azusa": + store processed as "Character: " with flag_name + push with result and processed + otherwise: + check if flag_name is "ui": + store processed as "Character: " with flag_name + push with result and processed + otherwise: + check if flag_name is "mio": + store processed as "Character: " with flag_name + push with result and processed + otherwise: + check if flag_name is "ritsu": + store processed as "Character: " with flag_name + push with result and processed + otherwise: + store processed as "Unknown: " with flag_name + push with result and processed + end check + end check + end check + end check + otherwise: + store processed as "Not a flag: " with current + push with result and processed + end check +end for + +display "Results:" +for each item in result: + display item +end for \ No newline at end of file diff --git a/TestPrograms/test_bad_return_type.wfl.ast.txt b/TestPrograms/test_bad_return_type.wfl.ast.txt new file mode 100644 index 00000000..870e9615 --- /dev/null +++ b/TestPrograms/test_bad_return_type.wfl.ast.txt @@ -0,0 +1,41 @@ +AST output for: TestPrograms/test_bad_return_type.wfl +============================================== + +Program with 1 statements: + +Statement #1: ContainerDefinition { + name: "TestContainer", + extends: None, + implements: [], + properties: [], + methods: [ + ActionDefinition { + name: "bad_return", + parameters: [], + body: [ + ReturnStatement { + value: Some( + Literal( + Integer( + 0, + ), + 4, + 16, + ), + ), + line: 4, + column: 9, + }, + ], + return_type: None, + line: 0, + column: 0, + }, + ], + events: [], + static_properties: [], + static_methods: [], + line: 2, + column: 1, +} + diff --git a/TestPrograms/test_length2_debug.txt b/TestPrograms/test_length2_debug.txt deleted file mode 100644 index f5946a15..00000000 --- a/TestPrograms/test_length2_debug.txt +++ /dev/null @@ -1,19 +0,0 @@ -=== WFL Debug Report === -Script: TestPrograms/test_length2.wfl -Time: 2025-08-11 03:05:38 - -=== Error Summary === -Runtime error at line 3, column 21: Error in native function: Runtime error at line 0, column 0: Expected text, got List - -=== Stack Trace === -In main script at line 3, column 21 - -=== Source Code === - 1: // Test length function more carefully - 2: store numbers as [1 and 2 and 3] ->> 3: store len as length of numbers - 4: display len - 5: - -=== Local Variables === -(No local variables in global scope) diff --git a/TestPrograms/test_length3_debug.txt b/TestPrograms/test_length3_debug.txt deleted file mode 100644 index 03cecb46..00000000 --- a/TestPrograms/test_length3_debug.txt +++ /dev/null @@ -1,18 +0,0 @@ -=== WFL Debug Report === -Script: TestPrograms/test_length3.wfl -Time: 2025-08-11 03:06:28 - -=== Error Summary === -Runtime error at line 10, column 22: Error in native function: Runtime error at line 0, column 0: Expected text, got List - -=== Stack Trace === -In main script at line 10, column 22 - -=== Source Code === - 8: // Method 2: Using "of" - 9: store temp_list as numbers ->> 10: store len2 as length of temp_list - 11: display "Method 2: " with len2 - -=== Local Variables === -(No local variables in global scope) diff --git a/TestPrograms/test_length_debug.txt b/TestPrograms/test_length_debug.txt deleted file mode 100644 index 2cbf16dc..00000000 --- a/TestPrograms/test_length_debug.txt +++ /dev/null @@ -1,19 +0,0 @@ -=== WFL Debug Report === -Script: TestPrograms/test_length.wfl -Time: 2025-08-11 03:05:01 - -=== Error Summary === -Runtime error at line 3, column 26: Error in native function: Runtime error at line 0, column 0: Expected text, got List - -=== Stack Trace === -In main script at line 3, column 26 - -=== Source Code === - 1: // Test length function - 2: store numbers as [1 and 2 and 3 and 4 and 5] ->> 3: store list_len as length of numbers - 4: display "List length: " with list_len - 5: - -=== Local Variables === -(No local variables in global scope) diff --git a/bug.md b/bug.md deleted file mode 100644 index b84db716..00000000 --- a/bug.md +++ /dev/null @@ -1,190 +0,0 @@ -# Bug Report: Incomplete Builtin Function Arity Definitions - -## Bug Summary -The WFL typechecker has incomplete builtin function arity definitions, causing false argument-count errors. When builtin function names are converted to Function types, they use a small hardcoded match set to determine param_count, defaulting to 1 for unknown functions. This causes many builtin functions that require 2 or more arguments to incorrectly report type errors claiming they only expect 1 argument. - -## Evidence Collected - -### 1. Problematic Code Location -**File**: `C:\logbie\wfl\src\typechecker\mod.rs` -**Lines**: 1230-1237 - -```rust -let param_count = match name.as_str() { - "substring" => 3, // text, start, length - "replace" => 3, // text, old, new - "clamp" => 3, // value, min, max - "padleft" | "padright" => 2, // text, length - "indexof" | "index_of" | "lastindexof" | "last_index_of" => 2, // text, substring - _ => 1, // Most functions take 1 parameter -}; -``` - -### 2. Current Match Cases and Their Param Counts -Only 6 functions have correct arity definitions: -- `substring`: 3 arguments (text, start, length) -- `replace`: 3 arguments (text, old, new) -- `clamp`: 3 arguments (value, min, max) -- `padleft`, `padright`: 2 arguments (text, length) -- `indexof`, `index_of`, `lastindexof`, `last_index_of`: 2 arguments (text, substring) -- **All others default to 1 argument** - -### 3. Complete List of Builtin Functions by Module - -**From `C:\logbie\wfl\src\builtins.rs` (Complete Registry)**: - -#### Implemented Functions (with verified arities): - -**Math Module** (`src/stdlib/math.rs`): -- `abs`: 1 argument ✓ (correctly defaulted) -- `round`: 1 argument ✓ (correctly defaulted) -- `floor`: 1 argument ✓ (correctly defaulted) -- `ceil`: 1 argument ✓ (correctly defaulted) -- `random`: 0 arguments ❌ (incorrectly defaulted to 1) -- `clamp`: 3 arguments ✓ (correctly defined) - -**Text Module** (`src/stdlib/text.rs`): -- `touppercase`, `to_uppercase`: 1 argument ✓ (correctly defaulted) -- `tolowercase`, `to_lowercase`: 1 argument ✓ (correctly defaulted) -- `contains`: 2 arguments ❌ (incorrectly defaulted to 1) -- `substring`: 3 arguments ✓ (correctly defined) - -**List Module** (`src/stdlib/list.rs`): -- `length`: 1 argument ✓ (correctly defaulted) -- `push`: 2 arguments ❌ (incorrectly defaulted to 1) -- `pop`: 1 argument ✓ (correctly defaulted) -- `contains`: 2 arguments ❌ (incorrectly defaulted to 1) -- `indexof`, `index_of`: 2 arguments ✓ (correctly defined) - -#### Recognized but NOT Implemented Functions: - -**Math Functions** (listed in builtins but not implemented): -- `min`: **2+ arguments** ❌ (incorrectly defaulted to 1) -- `max`: **2+ arguments** ❌ (incorrectly defaulted to 1) -- `power`: **2 arguments** ❌ (incorrectly defaulted to 1) -- `sqrt`, `sin`, `cos`, `tan`: 1 argument ✓ (correctly defaulted) - -**Text Functions** (listed in builtins but not implemented): -- `replace`: 3 arguments ✓ (correctly defined) -- `trim`: 1 argument ✓ (correctly defaulted) -- `padleft`, `padright`: 2 arguments ✓ (correctly defined) -- `capitalize`, `reverse`: 1 argument ✓ (correctly defaulted) -- `startswith`, `starts_with`: **2 arguments** ❌ (incorrectly defaulted to 1) -- `endswith`, `ends_with`: **2 arguments** ❌ (incorrectly defaulted to 1) -- `split`: **2 arguments** ❌ (incorrectly defaulted to 1) -- `join`: **2 arguments** ❌ (incorrectly defaulted to 1) - -**List Functions** (listed in builtins but not implemented): -- Most list operations: varying arities, mostly defaulted incorrectly - -## Reproduction Steps - -1. Create a simple WFL program with multi-argument builtin function calls: -```wfl -store a as 10 -store b as 5 -display min of a and b -``` - -2. Run with: `./target/release/wfl.exe test.wfl` - -3. Observe the type checking error: -``` -Type checking warnings: -error: Function expects 1 arguments, but 2 were provided -``` - -## Analysis - -### Root Cause Investigation - -The issue stems from a **hardcoded lookup table** approach in the typechecker that only covers a small subset of builtin functions. The code at lines 1230-1237 in `src/typechecker/mod.rs` uses a match statement to determine parameter counts, but it only handles 6 specific cases and defaults everything else to 1 argument. - -### Key Findings - -1. **Incomplete Coverage**: Only 6 out of 80+ builtin functions have correct arity definitions -2. **Default is Wrong**: The default of 1 argument is incorrect for many functions -3. **Disconnect**: The arity definitions are disconnected from the actual function implementations -4. **Scale of Impact**: This affects most multi-argument builtin functions - -### Impact Assessment - -**Severity**: High - Prevents use of essential builtin functions - -**Scope**: Affects all builtin functions requiring 2+ arguments that are not in the hardcoded list: - -**Incorrectly Defaulted Functions (Major Impact)**: -- `min`, `max`: Basic math operations requiring 2+ arguments -- `power`: Exponentiation requiring 2 arguments -- `contains`: Text/list searching requiring 2 arguments -- `push`: List manipulation requiring 2 arguments -- `starts_with`, `ends_with`: Text checking requiring 2 arguments -- `split`, `join`: String manipulation requiring 2 arguments -- `random`: Actually requires 0 arguments but defaults to 1 - -**Test Case Evidence**: The test program `TestPrograms/stdlib_comprehensive.wfl` contains multiple calls to these functions that would trigger this bug, including: -- `min of a and b` -- `max of a and b` -- `power of 5 and 2` -- `contains of sample_text and search_text` - -## Root Cause - -The fundamental issue is that **parameter count determination is hardcoded and incomplete**. The typechecker attempts to create Function types for builtin functions using a small lookup table, but this table covers less than 10% of the declared builtin functions. - -The problem occurs in the identifier type checking logic where builtin function names are converted to Function types. The param_count is determined by a match statement that only handles a few specific cases and defaults to 1 for everything else. - -## Recommended Investigation Areas - -### 1. Immediate Fix Areas -- **File**: `C:\logbie\wfl\src\typechecker\mod.rs`, lines 1230-1237 -- **Issue**: Expand the match statement to include correct arities for all builtin functions -- **Priority**: High - This is the core bug location - -### 2. Design Improvements -- **Synchronization**: Create a centralized arity registry that both the typechecker and interpreter can use -- **Validation**: Ensure arity definitions match actual function implementations -- **Testing**: Add comprehensive tests for builtin function arity validation - -### 3. Implementation Gaps -- **Missing Functions**: Many builtin functions are declared but not implemented (`min`, `max`, `power`, etc.) -- **File**: Various stdlib modules need to implement missing functions -- **Priority**: Medium - These are functionality gaps beyond the arity bug - -### 4. Test Coverage -- **File**: Need specific tests for builtin function arity validation -- **Focus**: Create test cases that specifically verify correct argument count handling -- **Location**: Add to existing test suites or create new arity-specific tests - -## Suggested Fix Approach - -### Minimal Expansion (Quick Fix) -Expand the match statement in `src/typechecker/mod.rs` to include the most commonly used multi-argument functions: - -```rust -let param_count = match name.as_str() { - // Existing entries... - "substring" => 3, - "replace" => 3, - "clamp" => 3, - "padleft" | "padright" => 2, - "indexof" | "index_of" | "lastindexof" | "last_index_of" => 2, - - // Critical additions: - "min" | "max" => 2, // Note: Actually variadic but minimum 2 - "power" => 2, - "contains" => 2, - "push" => 2, - "starts_with" | "startswith" => 2, - "ends_with" | "endswith" => 2, - "split" | "join" => 2, - "random" => 0, - - _ => 1, -}; -``` - -### Comprehensive Solution (Recommended) -Create a centralized arity registry that can be shared between the typechecker and function implementations, eliminating the possibility of drift between the two systems. - -This bug significantly impacts the usability of WFL's builtin functions and should be prioritized for fixing. \ No newline at end of file diff --git a/bug1.md b/bug1.md new file mode 100644 index 00000000..240f260c --- /dev/null +++ b/bug1.md @@ -0,0 +1,203 @@ +# WFL Stack Overflow Bug Report + +## Bug Summary + +A stack overflow occurs when running WFL programs that process command line arguments containing flags starting with `--` during the "Flag/Option Parsing" section. The bug is specifically triggered when executing the nested conditional logic that handles flag detection and list operations in a complex program structure. + +**Command that triggers bug**: `cargo run -- args_comprehensive.wfl --azusa is cool` +**Command that works**: `cargo run -- args_comprehensive.wfl Azusa is cool` + +## Evidence Collected + +### 1. Reproduction Steps +The stack overflow occurs consistently when: +1. Running `TestPrograms/args_comprehensive.wfl` with arguments starting with `--` +2. The program reaches section "7. Flag/Option Parsing" +3. Execution begins processing the nested conditional logic +4. Stack overflow occurs before any output from the flag parsing section + +### 2. Isolated Testing Results +Through systematic isolation testing, I found: + +- **Simple conditional logic**: Works fine +- **Simple for loops over args**: Works fine +- **Substring function calls**: Work fine +- **List operations (push)**: Work fine +- **Simple nested conditionals**: Work fine +- **Concatenation with 'with'**: Works fine + +However, the **exact combination** from the original program triggers the stack overflow. + +### 3. Exact Problematic Code Section +The issue occurs in this specific code block (lines 118-157 of `args_comprehensive.wfl`): + +```wfl +display "7. Flag/Option Parsing" +check if arg_count is greater than 0: + store has_help as no + store has_version as no + store has_verbose as no + store non_flag_args as [] + + for each arg in args: + check if arg is "--help" or arg is "-h": + change has_help to yes + otherwise: + check if arg is "--version" or arg is "-v": + change has_version to yes + otherwise: + check if arg is "--verbose": + change has_verbose to yes + otherwise: + check if substring of arg and 0 and 1 is "-": + display " Unknown flag: " with arg + otherwise: + push with non_flag_args and arg + end check + end check + end check + end check + end for + + display "Flags detected:" + display " Help flag: " with has_help + display " Version flag: " with has_version + display " Verbose flag: " with has_verbose + + display "Non-flag arguments:" + for each non_flag in non_flag_args: + display " - " with non_flag + end for +otherwise: + display "No arguments for flag parsing" +end check +``` + +### 4. Stack Overflow Error Details +``` +thread 'main' has overflowed its stack +error: process didn't exit successfully: `target\debug\wfl.exe args_comprehensive.wfl --azusa is cool` +(exit code: 0xc00000fd, STATUS_STACK_OVERFLOW) +``` + +## Analysis + +### Root Cause Investigation + +#### Hypothesis 1: Deep Async Recursion +The WFL interpreter uses `Box::pin` for async recursion handling in: +- `evaluate_expression()` -> `_evaluate_expression()` +- `execute_block()` -> `_execute_block()` +- `call_function()` + +The exact combination of: +1. Nested conditional statements (4 levels deep) +2. Function calls (`substring`) +3. String concatenation with `with` +4. List operations (`push`) +5. Variable access and modification +6. Loop iteration over lists + +...may be causing excessive stack frame allocation during async execution. + +#### Hypothesis 2: Environment Chain Corruption +The problem occurs when combining: +- Multiple variable definitions in nested scopes +- For-each loop variable binding (`arg`, `non_flag`) +- Environment scope creation for loop iterations +- Complex expression evaluation within nested conditionals + +This combination may lead to circular references or deep environment chain traversal. + +#### Hypothesis 3: Async Context Explosion +The specific interaction between: +- String concatenation expressions (`" - " with non_flag`) +- Function call expressions (`substring of arg and 0 and 1`) +- Conditional evaluation in nested structure +- Loop variable binding + +...may create an exponential number of async contexts or cause recursive evaluation cycles. + +## Root Cause + +**Most Probable Root Cause**: **Excessive Async Recursion in Complex Expression Evaluation** + +The WFL interpreter's async architecture, while designed to handle recursion with `Box::pin`, appears to encounter a stack overflow when processing the specific combination of: + +1. **Deep nested conditional structure** (4 levels of `check...otherwise...end check`) +2. **Complex expressions within loops** (`for each` with function calls and concatenation) +3. **Variable mutation within nested contexts** (`change` statements) +4. **String concatenation in display statements** (`display "..." with variable`) + +The recursion occurs during expression evaluation where each `with` concatenation, `substring` function call, and variable access triggers additional async function calls, and the combination of all these in the specific nested structure exceeds the stack limit. + +## Impact Assessment + +### Severity: **HIGH** +- Causes complete program termination with stack overflow +- Affects any WFL program processing command-line arguments with `--` flags +- No graceful error handling or recovery possible + +### Scope: **MEDIUM** +- Specific to complex nested conditional structures combined with: + - Command-line argument processing + - String concatenation operations + - Function calls within loops + - List manipulation + +### User Impact: **HIGH** +- Any WFL program that processes command-line flags will crash +- Makes WFL unsuitable for command-line tools +- No workaround available for complex argument parsing + +## Recommended Investigation Areas + +### 1. Async Stack Frame Management +**File**: `C:\logbie\wfl\src\interpreter\mod.rs` +**Functions**: +- `evaluate_expression()` (line 2883) +- `_evaluate_expression()` (line 2893) +- `execute_block()` (line 2840) +- `call_function()` (line 3737) + +**Investigation**: Examine if `Box::pin` usage is causing stack frame accumulation in complex nested scenarios. + +### 2. Expression Concatenation Logic +**File**: `C:\logbie\wfl\src\interpreter\mod.rs` +**Function**: Expression::Concatenation handler (line 3335) + +**Investigation**: Check if recursive `evaluate_expression` calls in concatenation chains are properly tail-call optimized. + +### 3. For-Each Loop Implementation +**File**: `C:\logbie\wfl\src\interpreter\mod.rs` +**Lines**: 1248-1324 (for-each loop execution) + +**Investigation**: Verify environment creation and cleanup in nested loop contexts. + +### 4. Variable Environment Management +**File**: `C:\logbie\wfl\src\interpreter\mod.rs` +**Functions**: Environment creation and variable binding in loops + +**Investigation**: Check for potential circular references or excessive environment chain depth. + +### 5. Conditional Statement Execution +**File**: `C:\logbie\wfl\src\interpreter\mod.rs` +**Function**: Check statement execution logic + +**Investigation**: Examine if deeply nested conditionals cause stack frame accumulation. + +## Reproduction Environment + +- **OS**: Windows 10/11 +- **WFL Version**: v25.8.26 +- **Rust Version**: Latest stable +- **Build**: Debug mode (issue may not occur in release due to optimizations) + +## Test Cases for Fix Validation + +1. **Basic reproduction**: `cargo run -- TestPrograms/args_comprehensive.wfl --azusa is cool` +2. **Simple flag**: `cargo run -- TestPrograms/args_comprehensive.wfl --help` +3. **Multiple flags**: `cargo run -- TestPrograms/args_comprehensive.wfl --verbose --test arg` +4. **No crash case**: `cargo run -- TestPrograms/args_comprehensive.wfl Azusa is cool` + +The fix should ensure all test cases execute successfully without stack overflow while maintaining identical program behavior. \ No newline at end of file diff --git a/src/analyzer/mod.rs b/src/analyzer/mod.rs index d740f74e..4ec7849f 100644 --- a/src/analyzer/mod.rs +++ b/src/analyzer/mod.rs @@ -159,6 +159,7 @@ pub struct Analyzer { errors: Vec, action_parameters: std::collections::HashSet, containers: HashMap, + current_container: Option, } impl Default for Analyzer { @@ -309,6 +310,7 @@ impl Analyzer { errors: Vec::new(), action_parameters: std::collections::HashSet::new(), containers: HashMap::new(), + current_container: None, } } @@ -388,9 +390,20 @@ impl Analyzer { column: *column, }; - if let Err(error) = self.current_scope.define(symbol) { - self.errors.push(error); - } + // Check if this is actually a container property assignment (including inherited) + let is_property_assignment = if let Some(container_name) = &self.current_container { + self.is_container_property(container_name, name) + } else { + false + }; + + // This is actually a property assignment, not a variable declaration + // Don't treat it as an error - the interpreter will handle it + + if !is_property_assignment + && let Err(error) = self.current_scope.define(symbol) { + self.errors.push(error); + } } Statement::Assignment { name, @@ -422,11 +435,21 @@ impl Analyzer { } } } else { - self.errors.push(SemanticError::new( - format!("Variable '{name}' is not defined"), - 0, // Need location info - 0, - )); + // Check if it's a container property assignment (including inherited) + let is_container_property = + if let Some(container_name) = &self.current_container { + self.is_container_property(container_name, name) + } else { + false + }; + + if !is_container_property { + self.errors.push(SemanticError::new( + format!("Variable '{name}' is not defined"), + *line, + *column, + )); + } } // Only analyze the value expression if the assignment is potentially valid @@ -954,6 +977,9 @@ impl Analyzer { .insert(prop.name.clone(), prop_info); } + // Register the container early so properties are available during method analysis + self.register_container(container_info.clone()); + // Process static properties for prop in static_properties { let prop_type = prop @@ -1005,6 +1031,15 @@ impl Analyzer { // Analyze method body self.push_scope(); + + // Set current container context + let previous_container = self.current_container.clone(); + self.current_container = Some(name.clone()); + + // Properties will be resolved through container context + // Don't add them as variables to avoid conflicts with assignments + + // Add method parameters for param in parameters { let param_type = param.param_type.as_ref().cloned().unwrap_or(Type::Unknown); @@ -1021,6 +1056,9 @@ impl Analyzer { for stmt in body { self.analyze_statement(stmt); } + + // Restore previous container context + self.current_container = previous_container; self.pop_scope(); } } @@ -1050,8 +1088,31 @@ impl Analyzer { .static_methods .insert(method_name.clone(), method_info); - // Analyze method body + // Analyze static method body self.push_scope(); + + // Set current container context + let previous_container = self.current_container.clone(); + self.current_container = Some(name.clone()); + + // Add static properties as accessible variables (not instance properties) + for prop in static_properties { + let prop_type = prop + .property_type + .as_ref() + .cloned() + .unwrap_or(Type::Unknown); + let symbol = Symbol { + name: prop.name.clone(), + kind: SymbolKind::Variable { mutable: true }, + symbol_type: Some(prop_type), + line: prop.line, + column: prop.column, + }; + let _ = self.current_scope.define(symbol); + } + + // Add method parameters for param in parameters { let param_type = param.param_type.as_ref().cloned().unwrap_or(Type::Unknown); @@ -1068,12 +1129,15 @@ impl Analyzer { for stmt in body { self.analyze_statement(stmt); } + + // Restore previous container context + self.current_container = previous_container; self.pop_scope(); } } - // Register the container - self.register_container(container_info); + // Re-register the container with all methods now that they've been processed + self.register_container(container_info.clone()); // Also register as a type symbol let container_symbol = Symbol { @@ -1108,6 +1172,27 @@ impl Analyzer { } } + Statement::InterfaceDefinition { + name, + extends: _, + required_actions: _, + line, + column, + } => { + // Register the interface as a type symbol + let interface_symbol = Symbol { + name: name.clone(), + kind: SymbolKind::Variable { mutable: false }, + symbol_type: Some(Type::Interface(name.clone())), + line: *line, + column: *column, + }; + + if let Err(e) = self.current_scope.define(interface_symbol) { + self.errors.push(e); + } + } + Statement::CreateListStatement { name, initial_values, @@ -1249,6 +1334,26 @@ impl Analyzer { self.containers.insert(container.name.clone(), container); } + fn is_container_property(&self, container_name: &str, property_name: &str) -> bool { + if let Some(container_info) = self.containers.get(container_name) { + // Check direct instance properties + if container_info.properties.contains_key(property_name) { + return true; + } + + // Check direct static properties + if container_info.static_properties.contains_key(property_name) { + return true; + } + + // Check inherited properties (both instance and static) + if let Some(parent_name) = &container_info.extends { + return self.is_container_property(parent_name, property_name); + } + } + false + } + pub fn get_container(&self, name: &str) -> Option<&ContainerInfo> { self.containers.get(name) } @@ -1299,11 +1404,21 @@ impl Analyzer { } if self.current_scope.resolve(name).is_none() { - self.errors.push(SemanticError::new( - format!("Variable '{name}' is not defined"), - *line, - *column, - )); + // Check if it's a container property (including inherited) + let is_container_property = + if let Some(container_name) = &self.current_container { + self.is_container_property(container_name, name) + } else { + false + }; + + if !is_container_property { + self.errors.push(SemanticError::new( + format!("Variable '{name}' is not defined"), + *line, + *column, + )); + } } } Expression::FunctionCall { @@ -1636,4 +1751,125 @@ mod tests { .contains("expects 1 arguments, but 0 were provided") ); } + + #[test] + fn test_container_static_property_recognition() { + use std::collections::HashMap; + + let mut analyzer = Analyzer::new(); + + // Register a container with static properties + let mut properties = HashMap::new(); + properties.insert( + "name".to_string(), + PropertyInfo { + name: "name".to_string(), + property_type: Type::Text, + is_public: true, + line: 1, + column: 1, + }, + ); + + let mut static_properties = HashMap::new(); + static_properties.insert( + "total_count".to_string(), + PropertyInfo { + name: "total_count".to_string(), + property_type: Type::Number, + is_public: true, + line: 1, + column: 1, + }, + ); + + let container_info = ContainerInfo { + name: "Counter".to_string(), + properties, + static_properties, + methods: HashMap::new(), + static_methods: HashMap::new(), + extends: None, + implements: Vec::new(), + line: 1, + column: 1, + }; + + analyzer.register_container(container_info); + + // Test instance property recognition + assert!(analyzer.is_container_property("Counter", "name")); + // Test static property recognition + assert!(analyzer.is_container_property("Counter", "total_count")); + // Test non-existent property + assert!(!analyzer.is_container_property("Counter", "nonexistent")); + } + + #[test] + fn test_container_inherited_static_property_recognition() { + use std::collections::HashMap; + + let mut analyzer = Analyzer::new(); + + // Register base container with static properties + let mut base_static_properties = HashMap::new(); + base_static_properties.insert( + "base_count".to_string(), + PropertyInfo { + name: "base_count".to_string(), + property_type: Type::Number, + is_public: true, + line: 1, + column: 1, + }, + ); + + let base_container = ContainerInfo { + name: "BaseContainer".to_string(), + properties: HashMap::new(), + static_properties: base_static_properties, + methods: HashMap::new(), + static_methods: HashMap::new(), + extends: None, + implements: Vec::new(), + line: 1, + column: 1, + }; + + analyzer.register_container(base_container); + + // Register derived container with its own static properties + let mut derived_static_properties = HashMap::new(); + derived_static_properties.insert( + "derived_count".to_string(), + PropertyInfo { + name: "derived_count".to_string(), + property_type: Type::Number, + is_public: true, + line: 1, + column: 1, + }, + ); + + let derived_container = ContainerInfo { + name: "DerivedContainer".to_string(), + properties: HashMap::new(), + static_properties: derived_static_properties, + methods: HashMap::new(), + static_methods: HashMap::new(), + extends: Some("BaseContainer".to_string()), + implements: Vec::new(), + line: 1, + column: 1, + }; + + analyzer.register_container(derived_container); + + // Test derived container can access its own static properties + assert!(analyzer.is_container_property("DerivedContainer", "derived_count")); + // Test derived container can access inherited static properties + assert!(analyzer.is_container_property("DerivedContainer", "base_count")); + // Test base container cannot access derived properties + assert!(!analyzer.is_container_property("BaseContainer", "derived_count")); + } } diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 9c472eef..3a9ddf79 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -929,7 +929,15 @@ impl Interpreter { env.borrow_mut() .define_constant(name, evaluated_value.clone()) } else { - env.borrow_mut().define(name, evaluated_value.clone()) + // Check if this variable already exists in the current environment + // This handles container property assignment in methods + if env.borrow().get(name).is_some() { + // Variable exists, use assignment instead of definition + env.borrow_mut().assign(name, evaluated_value.clone()) + } else { + // Variable doesn't exist, use normal definition + env.borrow_mut().define(name, evaluated_value.clone()) + } }; match result { @@ -2339,7 +2347,7 @@ impl Interpreter { implements, properties, methods, - events: _events, + events, static_properties: _static_properties, static_methods: _static_methods, line, @@ -2400,13 +2408,26 @@ impl Interpreter { } } + // Process events + let mut container_events = HashMap::new(); + for event in events { + let container_event = ContainerEventValue { + name: event.name.clone(), + params: event.parameters.iter().map(|p| p.name.clone()).collect(), + handlers: Vec::new(), + line: event.line, + column: event.column, + }; + container_events.insert(event.name.clone(), container_event); + } + let container_def = ContainerDefinitionValue { name: name.clone(), extends: extends.clone(), implements: implements.clone(), properties: container_properties, methods: container_methods, - events: HashMap::new(), // Future feature + events: container_events, static_properties: HashMap::new(), // Future feature static_methods: HashMap::new(), // Future feature line: *line, @@ -2972,8 +2993,31 @@ impl Interpreter { } }; - // Look up the method - if let Some(method_val) = container_def.methods.get(method) { + // Look up the method (with inheritance support) + let mut found_method = container_def.methods.get(method).cloned(); + let mut current_container_name = container_type.clone(); + + // If method not found, check parent containers + while found_method.is_none() { + if let Some(Value::ContainerDefinition(def)) = env.borrow().get(¤t_container_name) { + if let Some(parent_name) = &def.extends { + current_container_name = parent_name.clone(); + if let Some(Value::ContainerDefinition(parent_def)) = + env.borrow().get(parent_name) + { + found_method = parent_def.methods.get(method).cloned(); + } else { + break; + } + } else { + break; + } + } else { + break; + } + } + + if let Some(method_val) = found_method { // Create a function value from the method let function = FunctionValue { name: Some(method_val.name.clone()), @@ -2990,6 +3034,31 @@ impl Interpreter { // Add 'this' to the environment let _ = method_env.borrow_mut().define("this", object_val.clone()); + // Add container properties and events as accessible variables + if let Value::ContainerInstance(instance_rc) = &object_val_clone { + let instance = instance_rc.borrow(); + + // Add properties + for (prop_name, prop_value) in &instance.properties { + let _ = method_env + .borrow_mut() + .define(prop_name, prop_value.clone()); + } + + // Add events from the container definition + if let Some(Value::ContainerDefinition(container_def_rc)) = + env.borrow().get(&instance.container_type) + { + let container_def = container_def_rc.clone(); + for (event_name, event_value) in &container_def.events { + let _ = method_env.borrow_mut().define( + event_name, + Value::ContainerEvent(Rc::new(event_value.clone())), + ); + } + } + } + // Evaluate the arguments let mut arg_values = Vec::with_capacity(arguments.len()); for arg in arguments { @@ -2999,9 +3068,19 @@ impl Interpreter { arg_values.push(arg_val); } - // Call the function + // Create a modified function with the method environment + let method_function = FunctionValue { + name: function.name.clone(), + params: function.params.clone(), + body: function.body.clone(), + env: Rc::downgrade(&method_env), + line: function.line, + column: function.column, + }; + + // Call the function with the method environment let result = self - .call_function(&function, arg_values, line, column) + .call_function(&method_function, arg_values, line, column) .await?; Ok(result) @@ -3342,9 +3421,7 @@ impl Interpreter { let left_future = Box::pin(self.evaluate_expression(left, Rc::clone(&env))); let left_val = left_future.await?; - // Use Box::pin to handle recursion in async fn - let right_future = Box::pin(self.evaluate_expression(right, Rc::clone(&env))); - let right_val = right_future.await?; + let right_val = self.evaluate_expression(right, Rc::clone(&env)).await?; let result = format!("{left_val}{right_val}"); Ok(Value::Text(Rc::from(result.as_str()))) diff --git a/src/main.rs b/src/main.rs index 0d4b3d7d..2581aef0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -35,7 +35,7 @@ fn print_help() { println!(" --step Run in single-step execution mode"); println!(" --edit Open the specified file in the default editor"); println!(" --lex Dump lexer output to a text file and exit"); - println!(" --ast Dump abstract syntax tree to a text file and exit"); + println!(" --ast, --parse Dump abstract syntax tree to a text file and exit"); println!(" --time Measure and display execution time"); println!(); println!("Configuration Maintenance:"); @@ -100,7 +100,7 @@ async fn main() -> io::Result<()> { lex_dump = true; i += 1; } - "--ast" => { + "--ast" | "--parse" => { ast_dump = true; i += 1; } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 9b80b1fd..f89af8d1 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -837,7 +837,14 @@ impl<'a> Parser<'a> { if let Some(type_token) = self.tokens.peek() { if let Token::Identifier(type_name) = &type_token.token { self.tokens.next(); // Consume type name - Some(Type::Custom(type_name.clone())) + Some(match type_name.as_str() { + "Text" => Type::Text, + "Number" => Type::Number, + "Boolean" => Type::Boolean, + "Nothing" => Type::Nothing, + "Pattern" => Type::Pattern, + _ => Type::Custom(type_name.clone()), + }) } else { return Err(ParseError::new( "Expected type name after ':'".to_string(), @@ -940,7 +947,14 @@ impl<'a> Parser<'a> { if let Some(type_name_token) = self.tokens.peek() { if let Token::Identifier(type_name) = &type_name_token.token { self.tokens.next(); // Consume type name - Some(Type::Custom(type_name.clone())) + Some(match type_name.as_str() { + "Text" => Type::Text, + "Number" => Type::Number, + "Boolean" => Type::Boolean, + "Nothing" => Type::Nothing, + "Pattern" => Type::Pattern, + _ => Type::Custom(type_name.clone()), + }) } else { return Err(ParseError::new( "Expected type name after ':'".to_string(), @@ -4723,10 +4737,47 @@ impl<'a> Parser<'a> { parameters = self.parse_parameter_list()?; } - // For now, container actions don't support explicit return types - let return_type = None; - - self.expect_token(Token::Colon, "Expected ':' after action declaration")?; + // Parse return type if present (after parameters or action name) + let return_type = if let Some(token) = self.tokens.peek().cloned() + && matches!(token.token, Token::Colon) + { + self.tokens.next(); // Consume ':' + + // Check if the next token is actually a type identifier + // If it's not, this colon just marks the start of the action body (no return type) + if let Some(type_token) = self.tokens.peek().cloned() { + if let Token::Identifier(type_name) = &type_token.token { + // Check if this identifier is a valid type name + let is_type = matches!( + type_name.as_str(), + "Text" | "Number" | "Boolean" | "Nothing" | "Pattern" + ) || type_name.chars().next().is_some_and(|c| c.is_uppercase()); + + if is_type { + self.tokens.next(); // Consume type name + Some(match type_name.as_str() { + "Text" => Type::Text, + "Number" => Type::Number, + "Boolean" => Type::Boolean, + "Nothing" => Type::Nothing, + "Pattern" => Type::Pattern, + _ => Type::Custom(type_name.clone()), + }) + } else { + // This identifier is not a type, so no return type specified + None + } + } else { + // Next token after ':' is not an identifier, so no return type + None + } + } else { + // End of input after ':', so no return type + None + } + } else { + None + }; let mut body = Vec::new(); diff --git a/src/typechecker/mod.rs b/src/typechecker/mod.rs index addbc85d..eadbf28b 100644 --- a/src/typechecker/mod.rs +++ b/src/typechecker/mod.rs @@ -88,6 +88,7 @@ pub struct TypeChecker { analyzer: Analyzer, errors: Vec, analyzer_already_run: bool, + current_container: Option, } impl Default for TypeChecker { @@ -106,6 +107,7 @@ impl TypeChecker { analyzer, errors: Vec::new(), analyzer_already_run: false, + current_container: None, } } @@ -116,6 +118,7 @@ impl TypeChecker { analyzer, errors: Vec::new(), analyzer_already_run: true, // Analyzer has already been run when passed in + current_container: None, } } @@ -346,7 +349,28 @@ impl TypeChecker { return; } + // Check if this is a container property assignment within a method + // In this case, we might know the property type from the container definition + let mut is_container_property_assignment = false; if inferred_type == Type::Unknown { + // Check if we're in a container method and this is a property assignment + if let Some(ref container_name) = self.current_container + && let Some(container_info) = self.analyzer.get_container(container_name) + && container_info.properties.contains_key(name) { + // This is a container property assignment + is_container_property_assignment = true; + } + + // Also check if the analyzer has this symbol (fallback) + if !is_container_property_assignment + && let Some(symbol) = self.analyzer.get_symbol(name) + && symbol.symbol_type.is_some() { + // Variable already exists with a known type + is_container_property_assignment = true; + } + } + + if inferred_type == Type::Unknown && !is_container_property_assignment { self.type_error( format!("Could not infer type for variable '{name}'"), None, @@ -1066,9 +1090,16 @@ impl TypeChecker { for method in methods { if let Statement::ActionDefinition { body, .. } = method { + // Set container context for method body analysis + let previous_container = self.current_container.clone(); + self.current_container = Some(_name.clone()); + for stmt in body { self.check_statement_types(stmt); } + + // Restore previous container context + self.current_container = previous_container; } } diff --git a/tests/colon_consumption_test.rs b/tests/colon_consumption_test.rs new file mode 100644 index 00000000..bf1286b2 --- /dev/null +++ b/tests/colon_consumption_test.rs @@ -0,0 +1,76 @@ +// Test specifically for the double colon consumption issue +use wfl::lexer::lex_wfl_with_positions; +use wfl::parser::Parser; + +#[test] +fn test_colon_consumption_bug() { + // This test is designed to expose the double colon consumption bug + // If the bug exists, the parser should consume the colon twice: + // 1. Once when checking for return type + // 2. Again when expecting colon for action body + + let source = r#" +create container Test: + action greet: + display "Hello" + end +end +"#; + + let tokens = lex_wfl_with_positions(source); + println!( + "Tokens: {:?}", + tokens.iter().map(|t| &t.token).collect::>() + ); + + let mut parser = Parser::new(&tokens); + let result = parser.parse(); + + if let Err(errors) = result { + for error in errors { + println!("Parse error: {}", error.message); + // Check for specific error patterns that would indicate double colon consumption + if error.message.contains("Expected ':' after") + || error + .message + .contains("Unexpected token in expression: Colon") + { + panic!("Double colon consumption detected: {}", error.message); + } + } + } + + // If no specific colon errors, the test passes + assert!(true, "No double colon consumption detected"); +} + +#[test] +fn test_explicit_token_stream_analysis() { + // Test that manually checks token consumption patterns + let source = "action greet: display \"Hello\" end"; + + let tokens = lex_wfl_with_positions(source); + let token_types: Vec = tokens.iter().map(|t| format!("{:?}", t.token)).collect(); + + println!("Token sequence: {:?}", token_types); + + // Expected sequence: action, identifier(greet), colon, keyword(display), string, keyword(end) + // The colon should only be consumed once + + let expected = vec![ + "KeywordAction", + "Identifier", // greet + "Colon", + "KeywordDisplay", + "String", // "Hello" + "KeywordEnd", + ]; + + assert_eq!( + token_types.len(), + expected.len(), + "Token count mismatch. Expected: {:?}, Got: {:?}", + expected, + token_types + ); +} diff --git a/tests/container_ast_corruption_test.rs b/tests/container_ast_corruption_test.rs new file mode 100644 index 00000000..9834b102 --- /dev/null +++ b/tests/container_ast_corruption_test.rs @@ -0,0 +1,142 @@ +// Test for AST corruption bug in container action parsing +use wfl::lexer::lex_wfl_with_positions; +use wfl::parser::Parser; +use wfl::parser::ast::{Expression, Literal, Statement}; + +#[test] +fn test_container_action_ast_structure() { + let source = r#" +create container Test: + property name: Text + + action greet: + display "Hello" + end +end +"#; + + let tokens = lex_wfl_with_positions(source); + let mut parser = Parser::new(&tokens); + let result = parser.parse(); + + assert!(result.is_ok(), "Parser should succeed: {:?}", result.err()); + + let program = result.unwrap(); + assert_eq!( + program.statements.len(), + 1, + "Should have exactly one statement (container definition)" + ); + + // Verify the container structure is correct + if let Statement::ContainerDefinition { methods, .. } = &program.statements[0] { + assert_eq!(methods.len(), 1, "Should have exactly one method"); + + if let Statement::ActionDefinition { name, body, .. } = &methods[0] { + assert_eq!(name, "greet", "Method name should be 'greet'"); + assert_eq!( + body.len(), + 1, + "Method should have exactly one statement in body" + ); + + // Verify the display statement structure + if let Statement::DisplayStatement { value, .. } = &body[0] { + if let Expression::Literal(Literal::String(s), line, column) = value { + assert_eq!(s, "Hello", "Display text should be 'Hello'"); + // These coordinates should point to the string literal in the action body, + // not to some corrupted position from earlier parsing + assert_eq!(*line, 6, "String literal should be on line 6 (action body)"); + assert!( + *column > 10, + "String literal should be at reasonable column position in action body, got column {}", + column + ); + } else { + panic!("Display value should be a string literal, got: {:?}", value); + } + } else { + panic!( + "Method body should contain a display statement, got: {:?}", + body[0] + ); + } + } else { + panic!( + "Container method should be an ActionDefinition, got: {:?}", + methods[0] + ); + } + } else { + panic!( + "Statement should be a ContainerDefinition, got: {:?}", + program.statements[0] + ); + } +} + +#[test] +fn test_action_with_parameters_ast_structure() { + let source = r#" +create container Test: + property name: Text + + action set_name needs new_name: Text: + store name as new_name + end +end +"#; + + let tokens = lex_wfl_with_positions(source); + let mut parser = Parser::new(&tokens); + let result = parser.parse(); + + assert!(result.is_ok(), "Parser should succeed: {:?}", result.err()); + + let program = result.unwrap(); + + if let Statement::ContainerDefinition { methods, .. } = &program.statements[0] { + if let Statement::ActionDefinition { + name, + parameters, + body, + .. + } = &methods[0] + { + assert_eq!(name, "set_name"); + assert_eq!(parameters.len(), 1); + assert_eq!(parameters[0].name, "new_name"); + assert_eq!(body.len(), 1); + + // Check that the store statement has correct structure + if let Statement::VariableDeclaration { + name: var_name, + value, + .. + } = &body[0] + { + assert_eq!(var_name, "name"); + if let Expression::Variable(param_name, line, column) = value { + assert_eq!(param_name, "new_name"); + // This should point to the parameter usage in the action body + assert_eq!(*line, 6, "Parameter usage should be on line 6"); + assert!( + *column > 20, + "Parameter should be at reasonable column in store statement, got column {}", + column + ); + } else { + panic!( + "Store value should be a variable reference to new_name, got: {:?}", + value + ); + } + } else { + panic!( + "Action body should contain a store statement, got: {:?}", + body[0] + ); + } + } + } +} diff --git a/tests/container_parsing_fixes.rs b/tests/container_parsing_fixes.rs new file mode 100644 index 00000000..47478a9f --- /dev/null +++ b/tests/container_parsing_fixes.rs @@ -0,0 +1,99 @@ +// TDD tests for container parsing fixes +// These tests MUST fail first, then implementation will be written to make them pass + +use wfl::lexer::lex_wfl_with_positions; +use wfl::parser::Parser; + +#[test] +fn test_container_action_without_return_type_should_parse() { + let source = r#" +create container Test: + action greet: + display "Hello" + end +end +"#; + + let tokens = lex_wfl_with_positions(source); + let mut parser = Parser::new(&tokens); + let result = parser.parse(); + + // This should pass once the bug is fixed + assert!( + result.is_ok(), + "Parser should handle action without return type: {:?}", + result.err() + ); +} + +#[test] +fn test_container_action_with_parameters_should_parse() { + let source = r#" +create container Test: + action set_name needs new_name: Text: + display "Setting name" + end +end +"#; + + let tokens = lex_wfl_with_positions(source); + let mut parser = Parser::new(&tokens); + let result = parser.parse(); + + // This should pass once the 'needs' keyword bug is fixed + assert!( + result.is_ok(), + "Parser should handle 'needs' parameters: {:?}", + result.err() + ); +} + +#[test] +fn test_nested_end_tokens_should_parse() { + let source = r#" +create container Test: + action greet: + display "Hello" + end +end +"#; + + let tokens = lex_wfl_with_positions(source); + let mut parser = Parser::new(&tokens); + let result = parser.parse(); + + // This should pass once the nested end token handling is fixed + assert!( + result.is_ok(), + "Parser should handle nested end tokens: {:?}", + result.err() + ); +} + +#[test] +fn test_all_container_parsing_issues_combined() { + let source = r#" +create container Person: + property name: Text + + action greet: + display "Hello, I am " with name + end + + action set_name needs new_name: Text: + store name as new_name + end +end +"#; + + let tokens = lex_wfl_with_positions(source); + let mut parser = Parser::new(&tokens); + let result = parser.parse(); + + // This comprehensive test should pass once all bugs are fixed + assert!( + result.is_ok(), + "Parser should handle complete container with all features: {:?}", + result.err() + ); +}