@@ -3,7 +3,7 @@ LineIndex = 1 ' Our program counter
33Tokens = {} ' Tokens array for the current line
44Vars = {} ' Array containing variables of current proogram
55WhileStack = "WhileStack" ' Stack name for WHILE loops
6- LoopStack = "LoopStack " ' Stack name for FOR loops
6+ ForStack = "ForStack " ' Stack name for FOR loops
77
88EditorTextBox = ""
99LblFormat = ""
@@ -133,6 +133,9 @@ EndSub
133133Sub Run()
134134 TW.Clear()
135135 Vars = {}
136+ Stack.Clear(WhileStack)
137+ Stack.Clear(ForStack)
138+
136139 Statements = Text.Split(EditorTextBox.Text, Chars.Lf, False, False)
137140 LineIndex = 1
138141 statementsCount = Statements.Count
@@ -145,11 +148,13 @@ Sub Run()
145148 EndIf
146149
147150 If TW.IsClosed Then
151+ EditorTextBox.Focus()
148152 Return
149153 EndIf
150154 Wend
151155
152156 Terminate(ProEndMsg, False)
157+ EditorTextBox.Focus()
153158EndSub
154159
155160Sub EndProgram()
@@ -326,22 +331,26 @@ EndFunction
326331
327332Sub ProcessExitLoop(line)
328333 If Stack.GetCount(WhileStack) > 0 Then
329- LineIndex = FindClosingToken(_While, _Wend, LineIndex + 1)
330- If LineIndex = 0 Then
331- ReportError("'" & _Wend & "' غير موجودة", line, 1)
332- Return
333- Else
334- Stack.PopValue(WhileStack)
335- EndIf
336-
337- ElseIf Stack.GetCount(LoopStack) > 0 Then
338- LineIndex = FindClosingToken(_For, _Next, LineIndex + 1)
339- If LineIndex = 0 Then
340- ReportError("'" & _Next & "' غير موجودة", line, 1)
341- Return
342- EndIf
343- Stack.PopValue(LoopStack)
334+ lastWhile = Stack.PeekValue(WhileStack)
335+ lastWhileLine = lastWhile[1]
336+ Else
337+ lastWhileLine = 0
338+ EndIf
339+
340+ If Stack.GetCount(ForStack) > 0 Then
341+ lastFor = Stack.PeekValue(ForStack)
342+ lastForLine = lastFor[4]
343+ Else
344+ lastForLine = 0
345+ EndIf
346+
347+ If lastWhileLine > lastForLine Then
348+ LineIndex = lastWhile[2]
349+ Stack.PopValue(WhileStack)
344350
351+ ElseIf lastForLine > 0 Then
352+ LineIndex = lastFor[5]
353+ Stack.PopValue(ForStack)
345354 Else
346355 ReportError("لا توجد حلقة مفتوحة للخروج منها.", line, 1)
347356 EndIf
@@ -400,29 +409,57 @@ Sub ProcessForLoopHeader()
400409 stepValue = result[1]
401410 EndIf
402411
403- Vars[varName] = startValue
404- loopObj = {varName, endValue, stepValue, LineIndex}
405- Stack.PushValue(LoopStack, loopObj)
412+ nextLineIndex = FindClosingToken(_For, _Next, LineIndex + 1)
413+ If nextLineIndex = 0 Then
414+ ReportError("'" & _Next & " " & varName & "' مفقودة", LineIndex, 1)
415+ Else
416+ Vars[varName] = startValue
417+ loopObj = {varName, endValue, stepValue, LineIndex, nextLineIndex}
418+ Stack.PushValue(ForStack, loopObj)
419+ EndIf
406420EndSub
407421
408422Function IsValidVarName(name, line, tokenIndex)
409- If Text.IsNumeric(name[1]) Or
410- (Text.GetLength(name) = 1 And Chars.IsLetter(name) = False) Then
411- ReportError("الاسم '" & name & "' لا يصلح كاسم متغير", line, tokenIndex)
423+ If Text.IsNumeric(name[1]) Then
424+ ReportError("الاسم '" & name & "' لا يصلح كاسم متغير لأنه يبدأ برقم", line, tokenIndex)
425+ Return False
426+ ElseIf name = "_" Then
427+ ReportError("العلامة _ لا تصلح بمفردها كاسم متغير، لكن يمكن أن تستخدم ضصمن اسم متغير", line, tokenIndex)
412428 Return False
429+ Else
430+ ForEach c In name
431+ If (c = "_" Or Chars.IsDigit(c) Or Chars.IsLetter(c)) = False Then
432+ ReportError("الاسم '" & name & "' لا يصلح كاسم متغير بسبب وجود الرمز '" & c & "'", line, tokenIndex)
433+ Return False
434+ EndIf
435+ Next
436+ EndIf
437+
438+ Return True
439+ EndFunction
440+
441+ Function IsValidVarName2(name)
442+ If name = _And Or Text.IsNumeric(name[1]) Then
443+ Return False
444+ Else
445+ ForEach c In name
446+ If (c = "_" Or Chars.IsDigit(c) Or Chars.IsLetter(c)) = False Then
447+ Return False
448+ EndIf
449+ Next
413450 EndIf
414451
415452 Return True
416453EndFunction
417454
418455Sub ProcessForLoopNext()
419456 varName = Tokens[2][3]
420- If Stack.GetCount(LoopStack ) = 0 Then
457+ If Stack.GetCount(ForStack ) = 0 Then
421458 ReportError("لا توجد حلقة تكرار مفتوحة", LineIndex, 1)
422459 Return
423460 EndIf
424461
425- loopObj = Stack.PeekValue(LoopStack )
462+ loopObj = Stack.PeekValue(ForStack )
426463 If loopObj[1] <> varName Then
427464 ReportError("اسم العداد '" & varName & "' غير صحيح:" & Text.NewLine &
428465 "يجب أن تستخدم '" & loopObj[1] & "'", LineIndex, 2)
@@ -437,7 +474,7 @@ Sub ProcessForLoopNext()
437474 (stepValue < 0 And newVal >= end) Then
438475 LineIndex = loopObj[4] ' loop
439476 Else
440- Stack.PopValue(LoopStack )
477+ Stack.PopValue(ForStack )
441478 EndIf
442479EndSub
443480
@@ -449,9 +486,15 @@ Sub ProcessWhileLoopHeader()
449486 Return
450487 EndIf
451488
489+ wendLineIndex = FindClosingToken(_While, _Wend, LineIndex + 1)
490+ If wendLineIndex = 0 Then
491+ ReportError("'" & _Wend & "' مفقودة", LineIndex, 1)
492+ Return
493+ EndIf
494+
452495 condition = result[1]
453496 If condition Then
454- Stack.PushValue(WhileStack, LineIndex)
497+ Stack.PushValue(WhileStack, { LineIndex, wendLineIndex} )
455498 Else
456499 lineId = LineIndex
457500 LineIndex = FindClosingToken(_While, _Wend, LineIndex + 1)
@@ -467,7 +510,8 @@ Sub ProcessWhileLoopEnd()
467510 Return
468511 EndIf
469512
470- whileLinIndex = Stack.PeekValue(WhileStack)
513+ lastWhile = Stack.PeekValue(WhileStack)
514+ whileLinIndex = lastWhile[1]
471515 Tokenize(Statements[whileLinIndex], whileLinIndex)
472516 result = EvaluateExpression(2)
473517
@@ -519,7 +563,7 @@ Sub ProcessIfBlock()
519563 EndIf
520564
521565 While True
522- LineIndex = FindNextBranch (LineIndex + 1, {_ElseIf, _Else} )
566+ LineIndex = FindNextIfBranch (LineIndex + 1)
523567 Tokenize(Statements[LineIndex], LineIndex)
524568 branchToken = Tokens[1][3]
525569
@@ -583,44 +627,73 @@ Function FindClosingToken(openToken, closeToken, startLine)
583627 nestingLevel = 0
584628 For i = startLine To Statements.Count
585629 statement = Text.Trim(Statements[i])
586- If statement.StartsWith( openToken) Then
630+ If StartsWithToken(statement, openToken) Then
587631 nestingLevel = nestingLevel + 1
588- ElseIf statement.StartsWith( closeToken) Then
632+ ElseIf StartsWithToken(statement, closeToken) Then
589633 If nestingLevel = 0 Then
590634 Return i
591635 Else
592636 nestingLevel = nestingLevel - 1
593637 EndIf
638+
639+ ' Next conditions prevents messed up blocks
640+ ElseIf StartsWithToken(statement, _If) Then ' Works only with While and For
641+ i = FindClosingToken(_If, _EndIf, i + 1)
642+ If i = 0 Then
643+ Return 0
644+ EndIf
645+ ElseIf StartsWithToken(statement, _While) Then ' Works only with If and For
646+ i = FindClosingToken(_While, _Wend, i + 1)
647+ If i = 0 Then
648+ Return 0
649+ EndIf
650+ ElseIf StartsWithToken(statement, _For) Then ' Works only with If and while
651+ i = FindClosingToken(_For, _Next, i + 1)
652+ If i = 0 Then
653+ Return 0
654+ EndIf
655+ ElseIf StartsWithToken(statement, _EndIf)
656+ Or StartsWithToken(statement, _Wend)
657+ Or StartsWithToken(statement, _Next) Then
658+ Return 0
594659 EndIf
595660 Next
596661 Return 0
597662EndFunction
598663
599664
600- Function FindNextBranch (startLine, branchTokens )
665+ Function FindNextIfBranch (startLine)
601666 nestingLevel = 0
602667 For i = startLine To Statements.Count
603668 statement = Text.Trim(Statements[i])
604- If statement.StartsWith( _If) Then
669+ If StartsWithToken(statement, _If) Then
605670 nestingLevel = nestingLevel + 1
606- ElseIf statement.StartsWith( _EndIf) Then
671+ ElseIf StartsWithToken(statement, _EndIf) Then
607672 If nestingLevel = 0 Then
608673 Return i
609674 Else
610675 nestingLevel = nestingLevel - 1
611676 EndIf
612677 ElseIf nestingLevel = 0 Then
613- ForEach token In branchTokens
614- If statement.StartsWith(token) Then
615- Return i
616- EndIf
617- Next
678+ If StartsWithToken(statement, _ElseIf) Or StartsWithToken(statement, _Else) Then
679+ Return i
680+ EndIf
618681 EndIf
619682 Next
620683
621684 Return 0
622685EndFunction
623686
687+ Function StartsWithToken(statement, token)
688+ If Text.StartsWith(statement, token) = False Then
689+ Return False
690+ ElseIf statement = token Then
691+ Return True
692+ EndIf
693+
694+ nextChar = statement[Text.GetLength(token) + 1]
695+ Return (nextChar = "_" Or Chars.IsDigit(nextChar) Or Chars.IsLetter(nextChar)) = False
696+ EndFunction
624697
625698Function EvaluateExpression(start)
626699 result = ParseExpression(start)
@@ -1298,31 +1371,26 @@ Function AutoCompleteBlock()
12981371 spaces = Text.Repeat(" ", prev.Length - word.Length)
12991372
13001373 If word = _If Then
1301- EditorTextBox.Text =
1302- Text.Trim(txt.SubText(1, pos - 1)) & " " & "شرط " & Chars.CrLf
1374+ EditorTextBox.SelectedText = " " & "شرط " & Chars.CrLf
13031375 & spaces & " " & Chars.CrLf
1304- & spaces & _EndIf & Chars.CrLf
1305- & Text.Trim(txt.SubText(lineEnd + 1, txt.Length - pos))
1376+ & spaces & _EndIf
13061377 EditorTextBox.CaretIndex = pos + 1
13071378 Return 3
13081379
13091380 ElseIf word = _While Then
1310- EditorTextBox.Text =
1311- Text.Trim(txt.SubText(1, pos - 1)) & " " & _True & Chars.CrLf
1381+ EditorTextBox.SelectedText =
1382+ " " & _True & Chars.CrLf
13121383 & spaces & spaces & " " & Chars.CrLf
1313- & spaces & _Wend & Chars.CrLf
1314- & Text.Trim(txt.SubText(lineEnd + 1, txt.Length - pos))
1384+ & spaces & _Wend
13151385 EditorTextBox.CaretIndex = pos + 1
13161386 Return _True.Length
13171387
13181388 ElseIf word = _For Then
13191389 forVar = GetForVar()
1320- x = " " & forVar & " = 1 " & _To1 & " 10"
1321- EditorTextBox.Text =
1322- Text.Trim(txt.SubText(1, pos - 1)) & x & Chars.CrLf
1390+ EditorTextBox.SelectedText =
1391+ " " & forVar & " = 1 " & _To1 & " 10" & Chars.CrLf
13231392 & spaces & " " & Chars.CrLf
1324- & spaces & _Next & " " & forVar & Chars.CrLf
1325- & Text.Trim(txt.SubText(lineEnd + 1, txt.Length - pos))
1393+ & spaces & _Next & " " & forVar
13261394 EditorTextBox.CaretIndex = pos + 1
13271395 Return 1
13281396 EndIf
@@ -1406,7 +1474,7 @@ Function GetVarNames()
14061474 space = Text.IndexOf(line, " ", pos, True)
14071475 If space = 0 Or space = pos - 1 Then
14081476 varName = Text.Trim(Text.GetSubText(line, 1, space - 1))
1409- If varName <> "" And varName.Contains(" ") = False
1477+ If varName <> "" And IsValidVarName2(varName)
14101478 And Vars.ContainsValue(varName) = False Then
14111479 Vars.Append(varName)
14121480 EndIf
@@ -1415,4 +1483,40 @@ Function GetVarNames()
14151483 Next
14161484
14171485 Return Vars
1418- EndFunction
1486+ EndFunction
1487+
1488+ Sub FixNextVarName(ixdex, lineText)
1489+ forPos = lineText.IndexOf(_For, 1, False)
1490+ If forPos = 0 Then
1491+ Return
1492+ EndIf
1493+
1494+ Tokenize(lineText, 1)
1495+ varName = Tokens[2][3]
1496+ If varName = "" Then
1497+ Return
1498+ EndIf
1499+
1500+ code = Text.GetSubTextToEnd(EditorTextBox.Text, ixdex)
1501+ Statements = Text.Split(code, Chars.Lf, False, False)
1502+ nextLineIndex = FindClosingToken(_For, _Next, 2)
1503+ If nextLineIndex = 0 Then
1504+ Return
1505+ EndIf
1506+
1507+ Tokenize(Statements[nextLineIndex], nextLineIndex)
1508+ nextVarName = Tokens[2][3]
1509+
1510+ If nextVarName <> varName Then
1511+ lineStart = ixdex
1512+ For i = 1 To nextLineIndex - 1
1513+ lineStart = lineStart + Text.GetLength(Statements[i]) + 1
1514+ Next
1515+
1516+ caretPos = EditorTextBox.CaretIndex
1517+ EditorTextBox.SelectionStart = lineStart + Tokens[2][2] - 1
1518+ EditorTextBox.SelectionLength = Text.GetLength(nextVarName)
1519+ EditorTextBox.SelectedText = varName
1520+ EditorTextBox.CaretIndex = caretPos
1521+ EndIf
1522+ EndSub
0 commit comments