New little Parser example

New to FreeBASIC? Post your questions here.
Post Reply
Löwenherz
Posts: 278
Joined: Aug 27, 2008 6:26
Location: Bad Sooden-Allendorf, Germany

New little Parser example

Post by Löwenherz »

hello all.. I have made this new little parser example just for fun and served me as a learning chapter,
perhaps anybody out there can use it too. this example needed complete about three hours for correct running (I hope so). Its raining with me all day long.. feel free for Testing and asking
regards, loewenherz

Code: Select all

' 29-05-2025, parser example and a new approach by loewenherz as a learning chapter
' some month ago in february I have started another parser but this one is better ;)
' 
' freebasic test example for making inputs like this kind..
' dim a,b,c as integer
' dim s as string
'
' a=10
' b=20
' c=a*b
' s= "Hello result :" + c
' Print s ' output: Hello result 200
'

' Token types
Enum TokenType
    TOKEN_IDENTIFIER
    TOKEN_NUMBER
    TOKEN_OPERATOR
    TOKEN_STRING
    TOKEN_ASSIGN
    TOKEN_PRINT
    TOKEN_SLEEP
    TOKEN_EOF
    TOKEN_UNKNOWN
End Enum

Type Token
    As Integer kind
    As String value
End Type

Dim Shared As Token tokens(0 To 1000)
Dim Shared As Integer tokenCount = 0

' Symbol table
Dim Shared As String symbolTable(0 To 255)
Dim Shared As String symbolNames(0 To 255)
Dim Shared As Integer symbolCount = 0

Function IsLetter(ch As String) As Boolean
    Return (ch >= "a" And ch <= "z") Or (ch >= "A" And ch <= "Z")
End Function

Function IsDigit(ch As String) As Boolean
    Return (ch >= "0" And ch <= "9")
End Function

Function GetSymbolIndex(names As String) As Integer
    For i As Integer = 0 To symbolCount - 1
        If symbolNames(i) = names Then Return i
    Next
    symbolNames(symbolCount) = names
    symbolCount += 1
    Return symbolCount - 1
End Function

Sub Tokenize(lines As String)
    Dim As Integer i = 0
    tokenCount = 0
    Do While i < Len(lines)
        Dim As String ch = Mid(lines, i + 1, 1)

        If ch = " " Then
            i += 1
            Continue Do
        ElseIf IsLetter(ch) Then
            Dim As String ident = ""
            Do While i < Len(lines) AndAlso (IsLetter(Mid(lines, i + 1, 1)) Or IsDigit(Mid(lines, i + 1, 1)))
                ident &= Mid(lines, i + 1, 1)
                i += 1
            Loop
            tokens(tokenCount).value = ident
            If ident = "print" Then
                tokens(tokenCount).kind = TOKEN_PRINT
            ElseIf ident = "sleep" Then
                tokens(tokenCount).kind = TOKEN_SLEEP
            Else
                tokens(tokenCount).kind = TOKEN_IDENTIFIER
            End If
            tokenCount += 1
        ElseIf IsDigit(ch) Then
            Dim As String num = ""
            Do While i < Len(lines) AndAlso IsDigit(Mid(lines, i + 1, 1))
                num &= Mid(lines, i + 1, 1)
                i += 1
            Loop
            tokens(tokenCount).kind = TOKEN_NUMBER
            tokens(tokenCount).value = num
            tokenCount += 1
        ElseIf ch = """" Then
            Dim As String strx = ""
            i += 1 ' Skip opening quote
            Do While i < Len(lines) AndAlso Mid(lines, i + 1, 1) <> """"
                strx &= Mid(lines, i + 1, 1)
                i += 1
            Loop
            i += 1 ' Skip closing quote
            tokens(tokenCount).kind = TOKEN_STRING
            tokens(tokenCount).value = strx
            tokenCount += 1
        ElseIf ch = "=" Then
            tokens(tokenCount).kind = TOKEN_ASSIGN
            tokens(tokenCount).value = "="
            tokenCount += 1
            i += 1
        ElseIf ch = "+" Or ch = "-" Or ch = "*" Or ch = "/" Then
            tokens(tokenCount).kind = TOKEN_OPERATOR
            tokens(tokenCount).value = ch
            tokenCount += 1
            i += 1
        Else
            tokens(tokenCount).kind = TOKEN_UNKNOWN
            tokens(tokenCount).value = ch
            tokenCount += 1
            i += 1
        End If
    Loop
    tokens(tokenCount).kind = TOKEN_EOF
End Sub

Function EvalExpression(ByRef index As Integer) As String
    Dim As String result
    Dim As String resultType ' "int" or "str"

    If tokens(index).kind = TOKEN_NUMBER Then
        result = tokens(index).value
        resultType = "int"
        index += 1
    ElseIf tokens(index).kind = TOKEN_IDENTIFIER Then
        Dim As Integer varIdx = GetSymbolIndex(tokens(index).value)
        result = symbolTable(varIdx)
        resultType = "int" ' Assume it's an integer for simplicity
        index += 1
    ElseIf tokens(index).kind = TOKEN_STRING Then
        result = tokens(index).value
        resultType = "str"
        index += 1
    End If

    While index < tokenCount AndAlso tokens(index).kind = TOKEN_OPERATOR
        Dim As String op = tokens(index).value
        index += 1
        Dim As String rhs
        Dim As String rhsType

        If tokens(index).kind = TOKEN_NUMBER Then
            rhs = tokens(index).value
            rhsType = "int"
        ElseIf tokens(index).kind = TOKEN_IDENTIFIER Then
            rhs = symbolTable(GetSymbolIndex(tokens(index).value))
            rhsType = "int" ' Assume it's an integer for simplicity
        ElseIf tokens(index).kind = TOKEN_STRING Then
            rhs = tokens(index).value
            rhsType = "str"
        End If

        If resultType = "int" AndAlso rhsType = "int" Then
            Select Case op
                Case "+"
                    result = Str(Val(result) + Val(rhs))
                Case "-"
                    result = Str(Val(result) - Val(rhs))
                Case "*"
                    result = Str(Val(result) * Val(rhs))
                Case "/"
                    If Val(rhs) <> 0 Then
                        result = Str(Val(result) / Val(rhs))
                    Else
                        Print "Error: Divide by zero"
                        Exit Function
                    End If
            End Select
        Else
            ' Handle string concatenation
            result = result + rhs
            resultType = "str"
        End If
        index += 1
    Wend

    Return result
End Function

Sub Parse()
    Dim As Integer index = 0
    If tokens(index).kind = TOKEN_IDENTIFIER Then
        Dim As Integer varIdx = GetSymbolIndex(tokens(index).value)
        index += 1
        If tokens(index).kind = TOKEN_ASSIGN Then
            index += 1
            symbolTable(varIdx) = EvalExpression(index)
        End If
    ElseIf tokens(index).kind = TOKEN_PRINT Then
        index += 1
        Dim As String printResult = EvalExpression(index)
        Print printResult
    ElseIf tokens(index).kind = TOKEN_SLEEP Then
        ' Handle a sleep token
        Sleep
    End If
End Sub

' Input for calculations and strings
Dim As String inputLine
Do
    Line Input "> ", inputLine
    If inputLine = "exit" Then Exit Do
    Tokenize(inputLine)
    Parse()
Loop
' ends, 29-05-2025, parser by loewenherz as a learning chapter
Löwenherz
Posts: 278
Joined: Aug 27, 2008 6:26
Location: Bad Sooden-Allendorf, Germany

Re: New little Parser example

Post by Löwenherz »

hello all, I have done a little update
with true and false statements...
all work in progress. Examples for Testing See at Second Code example

' if.. then.. else.. are still a problem here for
' running perfect
' must check for next update
'
thanks, loewenherz
'

Code: Select all

' freebasic newParser update 1, loewenherz
' 07-06-2025, true and false
'' info:
' true = 1, false = 0
' a = 10
' b = 20
' c = a < b Then TRUE
' Print c
' result 1 ' ok

' code example 
'
' Token types
Enum TokenType
    TOKEN_IDENTIFIER
    TOKEN_NUMBER
    TOKEN_OPERATOR
    TOKEN_STRING
    TOKEN_ASSIGN
    TOKEN_PRINT
    TOKEN_SLEEP
    TOKEN_EOF
    TOKEN_UNKNOWN
    TOKEN_MODULUS
    TOKEN_LESS
    TOKEN_GREATER
    TOKEN_LPAREN
    TOKEN_RPAREN
    TOKEN_IF
    TOKEN_THEN
    TOKEN_TRUE
    TOKEN_FALSE
    TOKEN_ELSE
    TOKEN_ENDIF
End Enum

Type Token
    As Integer kind
    As String value
End Type

Dim Shared As Token tokens(0 To 1000)
Dim Shared As Integer tokenCount = 0

' Symbol table
Dim Shared As String symbolTable(0 To 255)
Dim Shared As String symbolNames(0 To 255)
Dim Shared As Integer symbolCount = 0

Function IsLetter(ch As String) As Boolean
    Return (ch >= "a" And ch <= "z") Or (ch >= "A" And ch <= "Z")
End Function

Function IsDigit(ch As String) As Boolean
    Return (ch >= "0" And ch <= "9")
End Function

Function GetSymbolIndex(names As String) As Integer
    For i As Integer = 0 To symbolCount - 1
        If symbolNames(i) = names Then Return i
    Next
    symbolNames(symbolCount) = names
    symbolCount += 1
    Return symbolCount - 1
End Function

Sub Tokenize(lines As String)
    Dim As Integer i = 0
    tokenCount = 0
    Do While i < Len(lines)
        Dim As String ch = Mid(lines, i + 1, 1)

        If ch = " " Then
            i += 1
            Continue Do
        ElseIf IsLetter(ch) Then
            Dim As String ident = ""
            Do While i < Len(lines) AndAlso (IsLetter(Mid(lines, i + 1, 1)) Or IsDigit(Mid(lines, i + 1, 1)))
                ident &= Mid(lines, i + 1, 1)
                i += 1
            Loop
            tokens(tokenCount).value = ident
            Select Case ident
                Case "print"
                    tokens(tokenCount).kind = TOKEN_PRINT
                Case "sleep"
                    tokens(tokenCount).kind = TOKEN_SLEEP
                Case "if"
                    tokens(tokenCount).kind = TOKEN_IF
                Case "then"
                    tokens(tokenCount).kind = TOKEN_THEN
                Case "true"
                    tokens(tokenCount).kind = TOKEN_TRUE
                Case "false"
                    tokens(tokenCount).kind = TOKEN_FALSE
                Case "else"
                    tokens(tokenCount).kind = TOKEN_ELSE
                Case "endif"
                    tokens(tokenCount).kind = TOKEN_ENDIF
                Case Else
                    tokens(tokenCount).kind = TOKEN_IDENTIFIER
            End Select
            tokenCount += 1
        ElseIf IsDigit(ch) Then
            Dim As String num = ""
            Do While i < Len(lines) AndAlso IsDigit(Mid(lines, i + 1, 1))
                num &= Mid(lines, i + 1, 1)
                i += 1
            Loop
            tokens(tokenCount).kind = TOKEN_NUMBER
            tokens(tokenCount).value = num
            tokenCount += 1
        ElseIf ch = """" Then
            Dim As String strx = ""
            i += 1 ' Skip opening quote
            Do While i < Len(lines) AndAlso Mid(lines, i + 1, 1) <> """"
                strx &= Mid(lines, i + 1, 1)
                i += 1
            Loop
            i += 1 ' Skip closing quote
            tokens(tokenCount).kind = TOKEN_STRING
            tokens(tokenCount).value = strx
            tokenCount += 1
        ElseIf ch = "=" Then
            tokens(tokenCount).kind = TOKEN_ASSIGN
            tokens(tokenCount).value = "="
            tokenCount += 1
            i += 1
        ElseIf ch = "+" Or ch = "-" Or ch = "*" Or ch = "/" Or ch = "%" Then
            tokens(tokenCount).kind = TOKEN_OPERATOR
            tokens(tokenCount).value = ch
            tokenCount += 1
            i += 1
        ElseIf ch = "<" Then
            tokens(tokenCount).kind = TOKEN_LESS
            tokens(tokenCount).value = "<"
            tokenCount += 1
            i += 1
        ElseIf ch = ">" Then
            tokens(tokenCount).kind = TOKEN_GREATER
            tokens(tokenCount).value = ">"
            tokenCount += 1
            i += 1
        ElseIf ch = "(" Then
            tokens(tokenCount).kind = TOKEN_LPAREN
            tokens(tokenCount).value = "("
            tokenCount += 1
            i += 1
        ElseIf ch = ")" Then
            tokens(tokenCount).kind = TOKEN_RPAREN
            tokens(tokenCount).value = ")"
            tokenCount += 1
            i += 1
        Else
            tokens(tokenCount).kind = TOKEN_UNKNOWN
            tokens(tokenCount).value = ch
            tokenCount += 1
            i += 1
        End If
    Loop
    tokens(tokenCount).kind = TOKEN_EOF
End Sub

Function EvalExpression(ByRef index As Integer) As String
    Dim As String result
    Dim As String resultType ' "int" or "str"

    If tokens(index).kind = TOKEN_NUMBER Then
        result = tokens(index).value
        resultType = "int"
        index += 1
    ElseIf tokens(index).kind = TOKEN_IDENTIFIER Then
        Dim As Integer varIdx = GetSymbolIndex(tokens(index).value)
        result = symbolTable(varIdx)
        resultType = "int" ' Assume it's an integer for simplicity
        index += 1
    ElseIf tokens(index).kind = TOKEN_STRING Then
        result = tokens(index).value
        resultType = "str"
        index += 1
    ElseIf tokens(index).kind = TOKEN_TRUE Then
        result = "1"
        resultType = "int"
        index += 1
    ElseIf tokens(index).kind = TOKEN_FALSE Then
        result = "0"
        resultType = "int"
        index += 1
    ElseIf tokens(index).kind = TOKEN_LPAREN Then
        index += 1
        result = EvalExpression(index)
        If tokens(index).kind <> TOKEN_RPAREN Then
            Print "Error: Expected ')'"
            Exit Function
        End If
        index += 1
    End If

    While index < tokenCount AndAlso (tokens(index).kind = TOKEN_OPERATOR Or tokens(index).kind = TOKEN_LESS Or tokens(index).kind = TOKEN_GREATER)
        Dim As String op = tokens(index).value
        index += 1
        Dim As String rhs
        Dim As String rhsType

        If tokens(index).kind = TOKEN_NUMBER Then
            rhs = tokens(index).value
            rhsType = "int"
        ElseIf tokens(index).kind = TOKEN_IDENTIFIER Then
            rhs = symbolTable(GetSymbolIndex(tokens(index).value))
            rhsType = "int" ' Assume it's an integer for simplicity
        ElseIf tokens(index).kind = TOKEN_STRING Then
            rhs = tokens(index).value
            rhsType = "str"
        ElseIf tokens(index).kind = TOKEN_TRUE Then
            rhs = "1"
            rhsType = "int"
        ElseIf tokens(index).kind = TOKEN_FALSE Then
            rhs = "0"
            rhsType = "int"
        ElseIf tokens(index).kind = TOKEN_LPAREN Then
            index += 1
            rhs = EvalExpression(index)
            If tokens(index).kind <> TOKEN_RPAREN Then
                Print "Error: Expected ')'"
                Exit Function
            End If
            index += 1
            rhsType = "int" ' Assume it's an integer for simplicity
        End If

        If resultType = "int" AndAlso rhsType = "int" Then
            Select Case op
                Case "+"
                    result = Str(Val(result) + Val(rhs))
                Case "-"
                    result = Str(Val(result) - Val(rhs))
                Case "*"
                    result = Str(Val(result) * Val(rhs))
                Case "/"
                    If Val(rhs) <> 0 Then
                        result = Str(Val(result) / Val(rhs))
                    Else
                        Print "Error: Divide by zero"
                        Exit Function
                    End If
                Case "%"
                    result = Str(Val(result) Mod Val(rhs))
                Case "<"
                    result = Str(-(Val(result) < Val(rhs)))
                Case ">"
                    result = Str(-(Val(result) > Val(rhs)))
            End Select
        Else
            ' Handle string concatenation
            result = result + rhs
            resultType = "str"
        End If
        index += 1
    Wend

    Return result
End Function

Sub ParseStatement(ByRef index As Integer)
    If tokens(index).kind = TOKEN_IDENTIFIER Then
        Dim As Integer varIdx = GetSymbolIndex(tokens(index).value)
        index += 1
        If tokens(index).kind = TOKEN_ASSIGN Then
            index += 1
            symbolTable(varIdx) = EvalExpression(index)
        End If
    ElseIf tokens(index).kind = TOKEN_PRINT Then
        index += 1
        Dim As String printResult = EvalExpression(index)
        Print printResult
    ElseIf tokens(index).kind = TOKEN_SLEEP Then
        ' Handle a sleep token
        Sleep
        index += 1
    End If
End Sub

Sub Parse()
    Dim As Integer index = 0
    While index < tokenCount
        If tokens(index).kind = TOKEN_IDENTIFIER Then
            Dim As Integer varIdx = GetSymbolIndex(tokens(index).value)
            index += 1
            If tokens(index).kind = TOKEN_ASSIGN Then
                index += 1
                symbolTable(varIdx) = EvalExpression(index)
            End If
        ElseIf tokens(index).kind = TOKEN_PRINT Then
            index += 1
            Dim As String printResult = EvalExpression(index)
            Print printResult
        ElseIf tokens(index).kind = TOKEN_SLEEP Then
            ' Handle a sleep token
            Sleep
            index += 1
        ElseIf tokens(index).kind = TOKEN_IF Then
            index += 1
            Dim As String condition = EvalExpression(index)
            If tokens(index).kind <> TOKEN_THEN Then
                Print "Error: Expected 'then'"
                Exit Sub
            End If
            index += 1
            If Val(condition) <> 0 Then
                ' Parse the block inside the IF statement
                While index < tokenCount AndAlso tokens(index).kind <> TOKEN_ELSE AndAlso tokens(index).kind <> TOKEN_ENDIF
                    ParseStatement(index)
                Wend
            Else
                ' Skip the block inside the IF statement
                While index < tokenCount AndAlso tokens(index).kind <> TOKEN_ELSE AndAlso tokens(index).kind <> TOKEN_ENDIF
                    index += 1
                Wend
            End If
            If tokens(index).kind = TOKEN_ELSE Then
                index += 1
                ' Parse the block inside the ELSE statement
                While index < tokenCount AndAlso tokens(index).kind <> TOKEN_ENDIF
                    ParseStatement(index)
                Wend
            End If
            If tokens(index).kind = TOKEN_ENDIF Then
                index += 1
            End If
        Else
            index += 1
        End If
    Wend
End Sub

' Input for some calculations and strings
Dim As String inputLine
Do
    Line Input "> ", inputLine
    If inputLine = "exit" Then Exit Do
    Tokenize(inputLine)
    Parse()
Loop

Addendum:
I add here some examples for testing:

Code: Select all

' modulus
c=2 + 3 * 10 % 4 ' result 2 
print c

c=( 9 * 20 ) + ( 9 - 10 ) ' result 180-1
print c

c=2 + 3 * 10 ' result 50
print c

c = 5 > 6 then
print c
'result 0 ' ok

this example is also running fine:
' I am surprised ;)

dim as long a=20
dim as integer b=30
dim as ULong c=a*b*1/2 
print c ' 300 ok
sleep
32-bit tested
syn9
Posts: 186
Joined: May 27, 2005 17:11
Contact:

Re: New little Parser example

Post by syn9 »

Excellent. One thing that has helped me in the past with writing parser/interpreters is to keep a running unit test file.

Basically I would make a little example for what I wanted to be able to execute correctly, then I'd implement the feature in the parser/interp. Once it was working I added the example to the unit test file. Usually I would rewrite the example in the form of something that could assert and throw an error if it didn't get parsed and executed correctly.

Over time this file grows into hundreds of tiny test cases that makes it easy to catch regressions and helps you feel confident in the outputs.

You can also use these to start building up error reporting to help the user find their bugs.
Löwenherz
Posts: 278
Joined: Aug 27, 2008 6:26
Location: Bad Sooden-Allendorf, Germany

Re: New little Parser example

Post by Löwenherz »

Thank you syn9 for Tips and your praise :-)
Excellent. One thing that has helped me in the past with writing parser/interpreters is to keep a running unit test file.

Basically I would make a little example for what I wanted to be able to execute correctly, then I'd implement the feature in the parser/interp. Once it was working I added the example to the unit test file. Usually I would rewrite the example in the form of something that could assert and throw an error if it didn't get parsed and executed correctly.

Over time this file grows into hundreds of tiny test cases that makes it easy to catch regressions and helps you feel confident in the outputs.

What do you are meaning with this unit File? I have a Demo Test File for my purpose whats my next step for this Parser and compiling. But this Demo file will Change every time ..

Next Update will come soon.

If you want to Test the Parser (lionparser) you must Take Input Line by Line. Copy Paste of whole example with several lines a Block doesn't Work (yet) ;-) I Made this example without any Help and was a Project I have started Last year with freebasic and for better understanding this great Basic language. I am still a beginner / advanced beginner lol

I am Always interested in understanding how does a Compiler / Interpreter Works. Freebasic offers a Lot of examples to study including Help files Wiki Manual and the Forum. I appreciate this Help.

Compiled all examples with freebasic 1.10.1, 32-bit
syn9
Posts: 186
Joined: May 27, 2005 17:11
Contact:

Re: New little Parser example

Post by syn9 »

Here's an example from my unit tests.

The parser/interpreter would read the whole file at once and attempt to execute it. Throughout the file are assertions on what intermediate results should be.

While I was adding new language features I would make a ton of test cases to make sure it was working correctly, then I would add them as a new section to this unit test mega file.

First it checks to see if it can correctly use the FILELINE command to get a string with the filename and line number.

After that it performs things like creating variables, assigning values, arithmetic, trig operations, logical operations, for and while loops, breaks and continues, to-from string conversions, fixed and dynamic arrays, reading and writing to files, string operations, and a stack overflow and memory leak test.

If the parser/interpreter runs correctly then the console should print out the names of the tests but not any "Test Failed" lines. Each set of tests are wrapped in scope blocks so that the environment is cleared between tests.

Writing a parser/interpreter is a really fun project and really helps you understand what languages are doing under the hood. I used the book Crafting Interpreters to help guide me: https://craftinginterpreters.com/

Unit Tests:

Code: Select all

println("-- FILELINE command test");
if FILELINE != "File:unit_test.tt, Line:2" {
    println("Failed FILELINE test, " + FILELINE);
}

//-----------------------------------------------------------------------------
println("-- default initialization test for basic types");
{
	i32 i; f32 f; bool b; string s; enum e;
	if 0     != i { println("Test Failed, " + FILELINE); }
	if f < -0.0000000001 || f > 0.0000000001 { println("Test Failed, " + FILELINE); }
	if false != b { println("Test Failed, " + FILELINE); }
	if ""    != s { println("Test Failed, " + FILELINE); }
	//if ""    != e as string { println("Test Failed, " + FILELINE); }
}

//-----------------------------------------------------------------------------
println("-- assignment test for basic types");
{
	i16 h = 3; i32 i = 1; i64 g = 5;
	f32 m = 5.5; f64 f = 2.2;
	bool b = true; string s = "test"; enum e = :TEST;
	
	// lhs test
	if 3       != h { println("Test Failed, " + FILELINE); }
	if 1       != i { println("Test Failed, " + FILELINE); }
	if 5       != g { println("Test Failed, " + FILELINE); }
	if abs(5.5 - m) > 0.0000001 { println("Test Failed, " + FILELINE); }
	if abs(2.2 - f) > 0.0000001 { println("Test Failed, " + FILELINE); }
	if true    != b { println("Test Failed, " + FILELINE); }
	if !b           { println("Test Failed, " + FILELINE); }
	if "test"  != s { println("Test Failed, " + FILELINE); }
	if :TEST   != e { println("Test Failed, " + FILELINE); }
	
	// rhs test
	if h != 3      { println("Test Failed, " + FILELINE); }
	if i != 1      { println("Test Failed, " + FILELINE); }
	if g != 5      { println("Test Failed, " + FILELINE); }
	if 0.0000001 < abs(5.5 - m) { println("Test Failed, " + FILELINE); }
	if 0.0000001 < abs(2.2 - f) { println("Test Failed, " + FILELINE); }
	if b != true   { println("Test Failed, " + FILELINE); }
	if s != "test" { println("Test Failed, " + FILELINE); }
	if e != :TEST  { println("Test Failed, " + FILELINE); }

	// float to int automatic cast
	i32 c = 0.3;
	if c != 0 { println("Test Failed, " + FILELINE); }
	f64 d = 1;
	if abs(1.0 - d) > 0.0000001 { println("Test Failed, " + FILELINE); }
	
	// redefinition test
	i32 aa = 5;
	if 5 != aa { println("Test Failed, " + FILELINE); }
	f64 aa = 6.7;
	if abs(6.7 - aa) > 0.0000001 { println("Test Failed, " + FILELINE); }
}


//-----------------------------------------------------------------------------
println("-- absolute value test");
{
	f64 x = 0.1;
	f64 y = -0.006;
	f64 z = abs(x - y);
	if abs(0.106 - z) > 0.0000001 { println("Test Failed, " + FILELINE); }

	i32 a = 5;
	i32 b = 33;
	i32 c = abs(a - b);
	if abs(28 - c) > 0 { println("Test Failed, " + FILELINE); }
	if 28 != c { println("Test Failed, " + FILELINE); }
}


//-----------------------------------------------------------------------------
println("-- basic math test");
{
	f32 f = (1 + 4 * 6 - 2 * 8) / 3;
	if abs(3 - f) > 0.0000001 { println("Test Failed, " + FILELINE); }
	if 2 != 7 % 5 { println("Test Failed, " + FILELINE); }
}


//-----------------------------------------------------------------------------
println("-- trig ops test");
{
	f32 x = 90;
	f32 y = deg2rad(x);
	f32 z = rad2deg(y);
	if abs(1.570796 - y) > 0.000001 { println("Test Failed, " + FILELINE); }
	if abs(90 - z) > 0.0000001 { println("Test Failed, " + FILELINE); }
	
	if abs(cos(30) - 0.154251) > 0.000001 { println("Test Failed, " + FILELINE); }
	if abs(sin(30) - -0.988032) > 0.000001 { println("Test Failed, " + FILELINE); }
	if abs(tan(30) - -6.405331) > 0.000001 { println("Test Failed, " + FILELINE); }
	
	if abs(pi - 3.1415926535897932384626433832795028841971693993751058) > 0.0000001 { println("Test Failed, " + FILELINE); }
	if (rad2deg(pi) - 180) > 0.0000001 { println("Test Failed, " + FILELINE); }
	if (deg2rad(180) - pi) > 0.0000001 { println("Test Failed, " + FILELINE); }
}


//-----------------------------------------------------------------------------
println("-- conditional ops test");
{
	i32 a = 1;
	i32 b = 2;
	i32 c = 3;
	if a < b {
		if c > b {
			if b != b {
				println("Test Failed, " + FILELINE);
			}
			else {
				if b < b {
					println("Test Failed, " + FILELINE);
				}
				else if b == b {
					if c >= c {
						// no op, all tests pass
					}
					else {
						println("Test Failed, " + FILELINE);
					}
				}
				else {
					println("Test Failed, " + FILELINE);
				}
			}
		}
		else {
			println("Test Failed, " + FILELINE);
		}
	}
	else {
		println("Test Failed, " + FILELINE);
	}
}


//-----------------------------------------------------------------------------
println("-- logical ops tests");
{
	i32 a = 1;
	i32 b = 2;
	i32 c = 3;
	bool p1 = false;
	bool p2 = false;
	if a < b && b < c { p1 = true; } else { println("Test Failed, " + FILELINE); }
	if !p1 { println("Test Failed, " + FILELINE); }
	if a > b || b < c { p2 = true; } else { println("Test Failed, " + FILELINE); }
	if !p2 { println("Test Failed, " + FILELINE); }
	if !(1 == 1) { println("Test Failed, " + FILELINE); }
}


//-----------------------------------------------------------------------------
println("-- for loop test");
{
	i32 i;
	for x in 0..5 {
		i = i + 1;
	}
	if 5 != i { println("Test Failed, " + FILELINE); }
}


//-----------------------------------------------------------------------------
println("-- while loop test");
{
	i32 i;
	while i < 10 {
		i = i + 1;
	}
	if 10 != i { println("Test Failed, " + FILELINE); }
}


//-----------------------------------------------------------------------------
println("-- break and continue test");
{
	i32 a;
	loop {
		a = a + 1;
		if a > 5 { break; }
		continue;
		a = a - 1;
	}
	if 6 != a { println("Test Failed, " + FILELINE); }
}


//-----------------------------------------------------------------------------
println("-- double break and continue test");
{
	i32 j = 0;
	for i in 0..6 {
		if i > 4 { break; }
		if i > 2 { continue; }
		i32 a;
		loop {
			a = a + 1;
			if a > 5 { break; }
			continue;
			a = a - 1;
		}
		j = j + a;
		if 6 != a { println("Test Failed, " + FILELINE); }
	}
	if 18 != j { println("Test Failed, " + FILELINE); }
}


//-----------------------------------------------------------------------------
println("-- random number generation test");
{
	f32 r = rand();
	if abs(r) < 0.000000000001 { println("Test Failed, " + FILELINE); }
	i32 r = rand(1..5);
	if r < 1 { println("Test Failed, " + FILELINE); }
}


//-----------------------------------------------------------------------------
println("-- string conversion test");
{
	i32 a = 1;
	f32 b = 12.3;
	if a as string != "1" { println("Test Failed, " + FILELINE); }
	if b as string != "12.300000" { println("Test Failed, " + FILELINE); }
	
	string s = "s" + a as string + b as string + "t";
	if s != "s112.300000t" { println("Test Failed, " + FILELINE); }

	i32 c = "5" as i32;
	if 5 != c { println("Test Failed, " + FILELINE); }
	
	f64 d = "33.2" as f64;
	if abs(d - 33.2) > 0.0000001 { println("Test Failed, " + FILELINE); }
	
	string e = "test" as string;
	if "test" != e { println("Test Failed, " + FILELINE); }
	
	// compound string assignment test
	string s = 5 as string + ", " + 1.1 as string + ", " + true as string;
	if s != "5, 1.100000, true" { println("Test Failed, " + FILELINE); }
}


//-----------------------------------------------------------------------------
println("-- fixed vec i32 test");
{
	// zero initializer
	vec<i32,1> ivec;
	if 1 != len(ivec) { println("Test Failed, " + FILELINE); }
	if 0 != ivec[0] { println("Test Failed, " + FILELINE); }

	// bracket initializer list with redefinition
	vec<i32,3> ivec = [1, 2, 3];
	if 3 != len(ivec) { println("Test Failed, " + FILELINE); }
	if ivec[0] + ivec[1] + ivec[2] != 6 { println("Test Failed, " + FILELINE); }

	// index assignment
	ivec[1] = 4;
	if ivec[0] + ivec[1] + ivec[2] != 8 { println("Test Failed, " + FILELINE); }

	// deep copy using assignment
	vec<i32,3> avec;
	avec = ivec;
	if 3 != len(avec) { println("Test Failed, " + FILELINE); }
	if avec[0] + avec[1] + avec[2] != 8 { println("Test Failed, " + FILELINE); }

	// deep copy using initializer list
	vec<i32,3> bvec = avec;
	if 3 != len(bvec) { println("Test Failed, " + FILELINE); }
	if bvec[0] + bvec[1] + bvec[2] != 8 { println("Test Failed, " + FILELINE); }

	// assignment after deep copy
	bvec[1] = 5;
	if avec[1] == bvec[1] { println("Test Failed, " + FILELINE); }

	// index variable test
	i32 idx = 1;
	bvec[idx] = 6;
	if bvec[1] != 6 { println("Test Failed, " + FILELINE); }
	if bvec[idx] != 6 { println("Test Failed, " + FILELINE); }
	bvec[idx] = idx;
	if bvec[1] != 1 { println("Test Failed, " + FILELINE); }
	if bvec[idx] != 1 { println("Test Failed, " + FILELINE); }

	// assignment to index from variable with index
	i32 i0 = 1;
	i32 i1 = 2;
	bvec[i1] = avec[i0];
	if bvec[0] + bvec[1] + bvec[2] != 6 { println("Test Failed, " + FILELINE); }
	bvec[1] = avec[2];
	if bvec[0] + bvec[1] + bvec[2] != 8 { println("Test Failed, " + FILELINE); }

	// bracket initializer with replicator
	vec<i32,5> ivec = [2; 5];
	if 5 != len(ivec) { println("Test Failed, " + FILELINE); }
	i32 sum = 0; // simple for loop summation
	for i in len(ivec) {
		sum = sum + ivec[i];
	}
	if 10 != sum { println("Test Failed, " + FILELINE); }
	i32 sum = 0; // by value summation
	for i in ivec {
		sum = sum + i;
	}
	if 10 != sum { println("Test Failed, " + FILELINE); }
	i32 sum = 0; // by key,value summation
	for k,v in ivec {
		sum = sum + ivec[k] + v;
	}
	if 20 != sum { println("Test Failed, " + FILELINE); }

	// bracket initializer with replicator and list
	vec<i32,7> ivec = [1, 2;5, 3];
	if 7 != len(ivec) { println("Test Failed, " + FILELINE); }
	i32 sum = 0;
	for i in len(ivec) {
		sum = sum + ivec[i];
	}
	if 14 != sum { println("Test Failed, " + FILELINE); }
	
	//vec<i32> x = [1, 2, 3];
	//if !vec::contains(x, 1) { println("Test Failed, " + FILELINE); }
	//if vec::contains(x, 4) { println("Test Failed, " + FILELINE); }
	
	// constant size variable test
	const i32 i = 3;
	vec<i32,i> ii;
	if 3 != len(ii) { println("Test Failed, " + FILELINE); }

}


//-----------------------------------------------------------------------------
println("-- dynamic vec i32 test");
{
	// zero initializer
	vec<i32> ivec;
	if 0 != len(ivec) { println("Test Failed, " + FILELINE); }

	// bracket initializer list with redefinition
	vec<i32> ivec = [1, 2, 3];
	if 3 != len(ivec) { println("Test Failed, " + FILELINE); }
	if ivec[0] + ivec[1] + ivec[2] != 6 { println("Test Failed, " + FILELINE); }
	
	// bracket assignment
	vec<i32> ivec;
	ivec = [1, 2, 3];
	if 3 != len(ivec) { println("Test Failed, " + FILELINE); }
	if ivec[0] + ivec[1] + ivec[2] != 6 { println("Test Failed, " + FILELINE); }

	// index assignment
	ivec[1] = 4;
	if ivec[0] + ivec[1] + ivec[2] != 8 { println("Test Failed, " + FILELINE); }
	
	// deep copy using assignment
	vec<i32> avec;
	avec = ivec;
	if 3 != len(avec) { println("Test Failed, " + FILELINE); }
	if avec[0] + avec[1] + avec[2] != 8 { println("Test Failed, " + FILELINE); }
	
	// deep copy using initializer list
	vec<i32> bvec = avec;
	if 3 != len(bvec) { println("Test Failed, " + FILELINE); }
	if bvec[0] + bvec[1] + bvec[2] != 8 { println("Test Failed, " + FILELINE); }

	// assignment after deep copy
	bvec[1] = 5;
	if avec[1] == bvec[1] { println("Test Failed, " + FILELINE); }

	// index variable test
	i32 idx = 1;
	bvec[idx] = 6;
	if bvec[1] != 6 { println("Test Failed, " + FILELINE); }
	if bvec[idx] != 6 { println("Test Failed, " + FILELINE); }
	bvec[idx] = idx;
	if bvec[1] != 1 { println("Test Failed, " + FILELINE); }
	if bvec[idx] != 1 { println("Test Failed, " + FILELINE); }

	// assignment to index from variable with index
	i32 i0 = 1;
	i32 i1 = 2;
	bvec[i1] = avec[i0];
	if bvec[0] + bvec[1] + bvec[2] != 6 { println("Test Failed, " + FILELINE); }
	bvec[1] = avec[2];
	if bvec[0] + bvec[1] + bvec[2] != 8 { println("Test Failed, " + FILELINE); }

	// bracket initializer with replicator
	//vec<i32> ivec = [2; 5];
	vec<i32> ivec = [2, 2, 2, 2, 2];
	if 5 != len(ivec) { println("Test Failed, " + FILELINE); }
	i32 sum = 0;
	for i in len(ivec) {
		sum = sum + ivec[i];
	}
	if 10 != sum { println("Test Failed, " + FILELINE); }
	i32 sum = 0; // by value summation
	for i in ivec {
		sum = sum + i;
	}
	if 10 != sum { println("Test Failed, " + FILELINE); }
	i32 sum = 0; // by key,value summation
	for k,v in ivec {
		sum = sum + ivec[k] + v;
	}
	if 20 != sum { println("Test Failed, " + FILELINE); }

	// bracket initializer with replicator and list
	vec<i32> ivec = [1, 2.1;5, 3]; // test replicator type hint to change float to i32
	if 7 != len(ivec) { println("Test Failed, " + FILELINE); }
	i32 sum = 0;
	for i in len(ivec) {
		sum = sum + ivec[i];
	}
	if 14 != sum { println("Test Failed, " + FILELINE); }
	
	// append test
	vec<i32> tvec = [1, 2];
	vec::append(tvec, 3);
	if 3 != len(tvec) { println("Test Failed, " + FILELINE); }
	if tvec[2] != 3 { println("Test Failed, " + FILELINE); }
	
	//vec<i32> x = [1, 2, 3];
	//if !vec::contains(x, 1) { println("Test Failed, " + FILELINE); }
	//if vec::contains(x, 4) { println("Test Failed, " + FILELINE); }
	
	//vec<i32> v = vec::fill(-1, 5);
	//i32 sum = 0;
	//for i in len(v) {
	//	if v[i] == -1 { sum = sum + 1; }
	//}
	//if sum != 5 { println("Test Failed, " + FILELINE); }
}


//-----------------------------------------------------------------------------
println("-- fixed vec f32 test");
{
	// zero initializer
	vec<f32,1> ivec;
	if 1 != len(ivec) { println("Test Failed, " + FILELINE); }
	if abs(0 - ivec[0]) > 0.0000001 { println("Test Failed, " + FILELINE); }

	// bracket initializer list with redefinition
	vec<f32,3> ivec = [1, 2, 3];
	if 3 != len(ivec) { println("Test Failed, " + FILELINE); }
	if abs(6 - (ivec[0] + ivec[1] + ivec[2])) > 0.000001 { println("Test Failed, " + FILELINE); }

	// index assignment
	ivec[1] = 4.4;
	if abs(8.4 - (ivec[0] + ivec[1] + ivec[2])) > 0.000001 { println("Test Failed, " + FILELINE); }

	// deep copy using assignment
	vec<f32,3> avec;
	avec = ivec;
	if 3 != len(avec) { println("Test Failed, " + FILELINE); }
	if abs(8.4 - (avec[0] + avec[1] + avec[2])) > 0.000001 { println("Test Failed, " + FILELINE); }

	// deep copy using initializer list
	vec<f32,3> bvec = avec;
	if 3 != len(bvec) { println("Test Failed, " + FILELINE); }
	if abs(8.4 - (bvec[0] + bvec[1] + bvec[2])) > 0.000001 { println("Test Failed, " + FILELINE); }

	// assignment after deep copy
	bvec[1] = 5.5;
	if abs(avec[1] - bvec[1]) < 0.000001 { println("Test Failed, " + FILELINE); }

	// index variable test
	i32 idx = 1;
	bvec[idx] = 6.6;
	if abs(6.6 - bvec[1]) > 0.000001 { println("Test Failed, " + FILELINE); }
	if abs(6.6 - bvec[idx]) > 0.000001 { println("Test Failed, " + FILELINE); }
	bvec[idx] = idx;
	if abs(1 - bvec[1]) > 0.000001 { println("Test Failed, " + FILELINE); }
	if abs(1 - bvec[idx]) > 0.000001 { println("Test Failed, " + FILELINE); }

	// assignment to index from variable with index
	i32 i0 = 1;
	i32 i1 = 2;
	bvec[i1] = avec[i0];
	if abs(6.4 - (bvec[0] + bvec[1] + bvec[2])) > 0.000001 { println("Test Failed, " + FILELINE); }
	bvec[1] = avec[2];
	if abs(8.4 - (bvec[0] + bvec[1] + bvec[2])) > 0.000001 { println("Test Failed, " + FILELINE); }

	// bracket initializer with replicator
	vec<f32,5> ivec = [2.2; 5];
	if 5 != len(ivec) { println("Test Failed, " + FILELINE); }
	f32 sum = 0;
	for i in len(ivec) {
		sum = sum + ivec[i];
	}
	if abs(11 - sum) > 0.000001 { println("Test Failed, " + FILELINE); }
	f32 sum = 0; // by value summation
	for i in ivec {
		sum = sum + i;
	}
	if abs(11 - sum) > 0.000001 { println("Test Failed, " + FILELINE); }
	f32 sum = 0; // by key,value summation
	for k,v in ivec {
		sum = sum + ivec[k] + v;
	}
	if abs(22 - sum) > 0.00001 { println("Test Failed, " + FILELINE); }

	// bracket initializer with replicator and list
	vec<f32,7> ivec = [1.1, 2.3;5, 3.4];
	if 7 != len(ivec) { println("Test Failed, " + FILELINE); }
	f32 sum = 0;
	for i in len(ivec) {
		sum = sum + ivec[i];
	}
	if abs(16 - sum) > 0.000001 { println("Test Failed, " + FILELINE); }

	// vec<f32> x = [1, 2, 3];
	// if !vec::contains(x, 1) { println("Test Failed, " + FILELINE); }
	// if vec::contains(x, 4) { println("Test Failed, " + FILELINE); }

}


//-----------------------------------------------------------------------------
println("-- dynamic vec f32 test");
{
	// zero initializer
	vec<f32> ivec;
	if 0 != len(ivec) { println("Test Failed, " + FILELINE); }
	
	// bracket initializer list with redefinition
	vec<f32> ivec = [1, 2, 3];
	if 3 != len(ivec) { println("Test Failed, " + FILELINE); }
	if abs(6 - (ivec[0] + ivec[1] + ivec[2])) > 0.000001 { println("Test Failed, " + FILELINE); }
	
	// bracket assignment
	vec<f32> ivec;
	ivec = [1, 2, 3];
	if 3 != len(ivec) { println("Test Failed, " + FILELINE); }
	if abs(6 - (ivec[0] + ivec[1] + ivec[2])) > 0.000001 { println("Test Failed, " + FILELINE); }

	// index assignment
	ivec[1] = 4.4;
	if abs(8.4 - (ivec[0] + ivec[1] + ivec[2])) > 0.000001 { println("Test Failed, " + FILELINE); }

	// deep copy using assignment
	vec<f32> avec;
	avec = ivec;
	if 3 != len(avec) { println("Test Failed, " + FILELINE); }
	if abs(8.4 - (avec[0] + avec[1] + avec[2])) > 0.000001 { println("Test Failed, " + FILELINE); }

	// deep copy using initializer list
	vec<f32> bvec = avec;
	if 3 != len(bvec) { println("Test Failed, " + FILELINE); }
	if abs(8.4 - (bvec[0] + bvec[1] + bvec[2])) > 0.000001 { println("Test Failed, " + FILELINE); }

	// assignment after deep copy
	bvec[1] = 5.5;
	if abs(avec[1] - bvec[1]) < 0.000001 { println("Test Failed, " + FILELINE); }

	// index variable test
	i32 idx = 1;
	bvec[idx] = 6.6;
	if abs(6.6 - bvec[1]) > 0.000001 { println("Test Failed, " + FILELINE); }
	if abs(6.6 - bvec[idx]) > 0.000001 { println("Test Failed, " + FILELINE); }
	bvec[idx] = idx;
	if abs(1 - bvec[1]) > 0.000001 { println("Test Failed, " + FILELINE); }
	if abs(1 - bvec[idx]) > 0.000001 { println("Test Failed, " + FILELINE); }

	// assignment to index from variable with index
	i32 i0 = 1;
	i32 i1 = 2;
	bvec[i1] = avec[i0];
	if abs(6.4 - (bvec[0] + bvec[1] + bvec[2])) > 0.000001 { println("Test Failed, " + FILELINE); }
	bvec[1] = avec[2];
	if abs(8.4 - (bvec[0] + bvec[1] + bvec[2])) > 0.000001 { println("Test Failed, " + FILELINE); }

	// bracket initializer with replicator
	vec<f32> ivec = [2.2; 5];
	if 5 != len(ivec) { println("Test Failed, " + FILELINE); }
	f32 sum = 0;
	for i in len(ivec) {
		sum = sum + ivec[i];
	}
	if abs(11 - sum) > 0.000001 { println("Test Failed, " + FILELINE); }
	f32 sum = 0; // by value summation
	for i in ivec {
		sum = sum + i;
	}
	if abs(11 - sum) > 0.000001 { println("Test Failed, " + FILELINE); }
	f32 sum = 0; // by key,value summation
	for k,v in ivec {
		sum = sum + ivec[k] + v;
	}
	if abs(22 - sum) > 0.00001 { println("Test Failed, " + FILELINE); }

	// bracket initializer with replicator and list
	vec<f32> ivec = [1.1, 2.3;5, 3.4];
	if 7 != len(ivec) { println("Test Failed, " + FILELINE); }
	f32 sum = 0;
	for i in len(ivec) {
		sum = sum + ivec[i];
	}
	if abs(16 - sum) > 0.000001 { println("Test Failed, " + FILELINE); }
	
	// append test
	vec<f32> tvec = [1.1, 2.2];
	vec::append(tvec, 3.3);
	if 3 != len(tvec) { println("Test Failed, " + FILELINE); }
	if abs(3.3 - tvec[2]) > 0.000001 { println("Test Failed, " + FILELINE); }

	// vec<f32> x = [1, 2, 3];
	// if !vec::contains(x, 1) { println("Test Failed, " + FILELINE); }
	// if vec::contains(x, 4) { println("Test Failed, " + FILELINE); }
	
	//vec<f32> v = vec::fill(1.1, 5);
	//i32 sum = 0;
	//for i in len(v) {
	//	if abs(1.1 - v[i]) < 0.000001 { sum = sum + 1; }
	//}
	//if sum != 5 { println("Test Failed, " + FILELINE); }
	
}


//-----------------------------------------------------------------------------
println("-- fixed vec enum test");
{
	// zero initializer
	vec<enum,1> ivec;
	if 1 != len(ivec) { println("Test Failed, " + FILELINE); }

	// bracket initializer list with redefinition
	vec<enum,3> ivec = [:A, :B, :C];
	if 3 != len(ivec) { println("Test Failed, " + FILELINE); }
	if ivec[0] != :A || :B != ivec[1] || ivec[2] != :C { println("Test Failed, " + FILELINE); }

	// index assignment
	ivec[1] = :D;
	if ivec[0] != :A || :D != ivec[1] || ivec[2] != :C { println("Test Failed, " + FILELINE); }

	// deep copy using assignment
	vec<enum,3> avec;
	avec = ivec;
	if 3 != len(avec) { println("Test Failed, " + FILELINE); }
	if avec[0] != :A || :D != avec[1] || avec[2] != :C { println("Test Failed, " + FILELINE); }

	// deep copy using initializer list
	vec<enum,3> bvec = avec;
	if 3 != len(bvec) { println("Test Failed, " + FILELINE); }
	if bvec[0] != :A || :D != bvec[1] || bvec[2] != :C { println("Test Failed, " + FILELINE); }

	// assignment after deep copy
	bvec[1] = :E;
	if bvec[0] != :A || :E != bvec[1] || bvec[2] != :C { println("Test Failed, " + FILELINE); }

	// index variable test
	i32 idx = 1;
	bvec[idx] = :F;
	if bvec[idx] != :F { println("Test Failed, " + FILELINE); }
	if bvec[1] != :F { println("Test Failed, " + FILELINE); }

	// assignment to index from variable with index
	i32 i0 = 1;
	i32 i1 = 2;
	bvec[i1] = avec[i0];
	if bvec[0] != :A || :F != bvec[1] || bvec[2] != :D { println("Test Failed, " + FILELINE); }
	bvec[1] = avec[2];
	if bvec[0] != :A || :C != bvec[1] || bvec[2] != :D { println("Test Failed, " + FILELINE); }

	// bracket initializer with replicator
	vec<enum,5> ivec = [:G; 5];
	if 5 != len(ivec) { println("Test Failed, " + FILELINE); }
	i32 sum = 0;
	for i in len(ivec) {
		if ivec[i] == :G { sum = sum + 1; }
	}
	if 5 != sum { println("Test Failed, " + FILELINE); }
	i32 sum = 0; // by value summation
	for i in ivec {
		if i == :G { sum = sum + 1; }
	}
	if 5 != sum { println("Test Failed, " + FILELINE); }
	i32 sum = 0; // by key,value summation
	for k,v in ivec {
		if v == :G { sum = sum + 1; }
		if ivec[k] == :G { sum = sum + 1; }
	}
	if 10 != sum { println("Test Failed, " + FILELINE); }
	
	// bracket initializer with replicator and list
	vec<enum,7> ivec = [:H, :I;5, :H];
	if 7 != len(ivec) { println("Test Failed, " + FILELINE); }
	i32 sum = 0;
	for i in len(ivec) {
		if ivec[i] == :H { sum = sum + 1; }
	}
	if 2 != sum { println("Test Failed, " + FILELINE); }

	// vec<enum> x = [1, 2, 3];
	// if !vec::contains(x, 1) { println("Test Failed, " + FILELINE); }
	// if vec::contains(x, 4) { println("Test Failed, " + FILELINE); }
	
}


//-----------------------------------------------------------------------------
println("-- dynamic vec enum test");
{
	// zero initializer
	vec<enum> ivec;
	if 0 != len(ivec) { println("Test Failed, " + FILELINE); }

	// bracket initializer list with redefinition
	vec<enum> ivec = [:A, :B, :C];
	if 3 != len(ivec) { println("Test Failed, " + FILELINE); }
	if ivec[0] != :A || :B != ivec[1] || ivec[2] != :C { println("Test Failed, " + FILELINE); }

	// bracket assignment
	vec<enum> ivec;
	ivec = [:A, :B, :C];
	if 3 != len(ivec) { println("Test Failed, " + FILELINE); }
	if ivec[0] != :A || :B != ivec[1] || ivec[2] != :C { println("Test Failed, " + FILELINE); }

	// index assignment
	ivec[1] = :D;
	if ivec[0] != :A || :D != ivec[1] || ivec[2] != :C { println("Test Failed, " + FILELINE); }
	
	// deep copy using assignment
	vec<enum> avec;
	avec = ivec;
	if 3 != len(avec) { println("Test Failed, " + FILELINE); }
	if avec[0] != :A || :D != avec[1] || avec[2] != :C { println("Test Failed, " + FILELINE); }

	// deep copy using initializer list
	vec<enum> bvec = avec;
	if 3 != len(bvec) { println("Test Failed, " + FILELINE); }
	if bvec[0] != :A || :D != bvec[1] || bvec[2] != :C { println("Test Failed, " + FILELINE); }

	// assignment after deep copy
	bvec[1] = :E;
	if bvec[0] != :A || :E != bvec[1] || bvec[2] != :C { println("Test Failed, " + FILELINE); }

	// index variable test
	i32 idx = 1;
	bvec[idx] = :F;
	if bvec[idx] != :F { println("Test Failed, " + FILELINE); }
	if bvec[1] != :F { println("Test Failed, " + FILELINE); }

	// assignment to index from variable with index
	i32 i0 = 1;
	i32 i1 = 2;
	bvec[i1] = avec[i0];
	if bvec[0] != :A || :F != bvec[1] || bvec[2] != :D { println("Test Failed, " + FILELINE); }
	bvec[1] = avec[2];
	if bvec[0] != :A || :C != bvec[1] || bvec[2] != :D { println("Test Failed, " + FILELINE); }

	// bracket initializer with replicator
	vec<enum> ivec = [:G; 5];
	if 5 != len(ivec) { println("Test Failed, " + FILELINE); }
	i32 sum = 0;
	for i in len(ivec) {
		if ivec[i] == :G { sum = sum + 1; }
	}
	if 5 != sum { println("Test Failed, " + FILELINE); }
	i32 sum = 0; // by value summation
	for i in ivec {
		if i == :G { sum = sum + 1; }
	}
	if 5 != sum { println("Test Failed, " + FILELINE); }
	i32 sum = 0; // by key,value summation
	for k,v in ivec {
		if v == :G { sum = sum + 1; }
		if ivec[k] == :G { sum = sum + 1; }
	}
	if 10 != sum { println("Test Failed, " + FILELINE); }

	// bracket initializer with replicator and list
	vec<enum> ivec = [:H, :I;5, :H];
	if 7 != len(ivec) { println("Test Failed, " + FILELINE); }
	i32 sum = 0;
	for i in len(ivec) {
		if ivec[i] == :H { sum = sum + 1; }
	}
	if 2 != sum { println("Test Failed, " + FILELINE); }
	
	// append test
	vec<enum> tvec = [:A, :B];
	vec::append(tvec, :C);
	if 3 != len(tvec) { println("Test Failed, " + FILELINE); }
	if :C != tvec[2] { println("Test Failed, " + FILELINE); }

	// vec<enum> x = [1, 2, 3];
	// if !vec::contains(x, 1) { println("Test Failed, " + FILELINE); }
	// if vec::contains(x, 4) { println("Test Failed, " + FILELINE); }
	
	//vec<enum> v = vec::fill(:TEST, 5);
	//i32 sum = 0;
	//for i in len(v) {
	//	if :TEST == v[i] { sum = sum + 1; }
	//}
	//if sum != 5 { println("Test Failed, " + FILELINE); }
	
}


//-----------------------------------------------------------------------------
println("-- fixed vec bool test");
{
	// zero initializer
	vec<bool,1> ivec;
	if 1 != len(ivec) { println("Test Failed, " + FILELINE); }
	if true == ivec[0] { println("Test Failed, " + FILELINE); }
	if ivec[0] { println("Test Failed, " + FILELINE); }

	// bracket initializer list with redefinition
	vec<bool,3> ivec = [false, true, true];
	if 3 != len(ivec) { println("Test Failed, " + FILELINE); }
	if ivec[0] || !ivec[1] || !ivec[2] { println("Test Failed, " + FILELINE); }

	// index assignment
	ivec[1] = false;
	if ivec[0] || ivec[1] || !ivec[2] { println("Test Failed, " + FILELINE); }

	// deep copy using assignment
	vec<bool,3> avec;
	avec = ivec;
	if 3 != len(avec) { println("Test Failed, " + FILELINE); }
	if avec[0] || avec[1] || !avec[2] { println("Test Failed, " + FILELINE); }

	// deep copy using initializer list
	vec<bool,3> bvec = avec;
	if 3 != len(bvec) { println("Test Failed, " + FILELINE); }
	if bvec[0] || bvec[1] || !bvec[2] { println("Test Failed, " + FILELINE); }

	// assignment after deep copy
	bvec[1] = true;
	if bvec[0] || !bvec[1] || !bvec[2] { println("Test Failed, " + FILELINE); }

	// index variable test
	i32 idx = 1;
	bvec[idx] = false;
	if bvec[idx] { println("Test Failed, " + FILELINE); }
	if bvec[1] { println("Test Failed, " + FILELINE); }

	// assignment to index from variable with index
	i32 i0 = 1;
	i32 i1 = 2;
	bvec[i1] = avec[i0];
	if bvec[0] || bvec[1] || bvec[2] { println("Test Failed, " + FILELINE); }
	bvec[1] = avec[2];
	if bvec[0] || !bvec[1] || bvec[2] { println("Test Failed, " + FILELINE); }

	// bracket initializer with replicator
	vec<bool,5> ivec = [true; 5];
	if 5 != len(ivec) { println("Test Failed, " + FILELINE); }
	i32 sum = 0;
	for i in len(ivec) {
		if ivec[i] { sum = sum + 1; }
	}
	if 5 != sum { println("Test Failed, " + FILELINE); }
	i32 sum = 0; // by value summation
	for i in ivec {
		if i { sum = sum + 1; }
	}
	if 5 != sum { println("Test Failed, " + FILELINE); }
	i32 sum = 0; // by key,value summation
	for k,v in ivec {
		if v { sum = sum + 1; }
		if ivec[k] { sum = sum + 1; }
	}
	if 10 != sum { println("Test Failed, " + FILELINE); }

	// bracket initializer with replicator and list
	vec<bool,7> ivec = [true, false, true;3, false, true];
	if 7 != len(ivec) { println("Test Failed, " + FILELINE); }
	i32 sum = 0;
	for i in len(ivec) {
		if ivec[i] { sum = sum + 1; }
	}
	if 5 != sum { println("Test Failed, " + FILELINE); }

	// vec<bool> x = [1, 2, 3];
	// if !vec::contains(x, 1) { println("Test Failed, " + FILELINE); }
	// if vec::contains(x, 4) { println("Test Failed, " + FILELINE); }

}


//-----------------------------------------------------------------------------
println("-- dynamic vec bool test");
{
	// zero initializer
	vec<bool> ivec;
	if 0 != len(ivec) { println("Test Failed, " + FILELINE); }
	
	// bracket initializer list with redefinition
	vec<bool> ivec = [false, true, true];
	if 3 != len(ivec) { println("Test Failed, " + FILELINE); }
	if ivec[0] || !ivec[1] || !ivec[2] { println("Test Failed, " + FILELINE); }

	// bracket assignment
	vec<bool> ivec;
	ivec = [false, true, true];
	if 3 != len(ivec) { println("Test Failed, " + FILELINE); }
	if ivec[0] || !ivec[1] || !ivec[2] { println("Test Failed, " + FILELINE); }

	// index assignment
	ivec[1] = false;
	if ivec[0] || ivec[1] || !ivec[2] { println("Test Failed, " + FILELINE); }

	// deep copy using assignment
	vec<bool> avec;
	avec = ivec;
	if 3 != len(avec) { println("Test Failed, " + FILELINE); }
	if avec[0] || avec[1] || !avec[2] { println("Test Failed, " + FILELINE); }

	// deep copy using initializer list
	vec<bool> bvec = avec;
	if 3 != len(bvec) { println("Test Failed, " + FILELINE); }
	if bvec[0] || bvec[1] || !bvec[2] { println("Test Failed, " + FILELINE); }

	// assignment after deep copy
	bvec[1] = true;
	if bvec[0] || !bvec[1] || !bvec[2] { println("Test Failed, " + FILELINE); }

	// index variable test
	i32 idx = 1;
	bvec[idx] = false;
	if bvec[idx] { println("Test Failed, " + FILELINE); }
	if bvec[1] { println("Test Failed, " + FILELINE); }
	
	// assignment to index from variable with index
	i32 i0 = 1;
	i32 i1 = 2;
	bvec[i1] = avec[i0];
	if bvec[0] || bvec[1] || bvec[2] { println("Test Failed, " + FILELINE); }
	bvec[1] = avec[2];
	if bvec[0] || !bvec[1] || bvec[2] { println("Test Failed, " + FILELINE); }

	// bracket initializer with replicator
	vec<bool> ivec = [true; 5];
	if 5 != len(ivec) { println("Test Failed, " + FILELINE); }
	i32 sum = 0;
	for i in len(ivec) {
		if ivec[i] { sum = sum + 1; }
	}
	if 5 != sum { println("Test Failed, " + FILELINE); }
	i32 sum = 0; // by value summation
	for i in ivec {
		if i { sum = sum + 1; }
	}
	if 5 != sum { println("Test Failed, " + FILELINE); }
	i32 sum = 0; // by key,value summation
	for k,v in ivec {
		if v { sum = sum + 1; }
		if ivec[k] { sum = sum + 1; }
	}
	if 10 != sum { println("Test Failed, " + FILELINE); }
	
	// bracket initializer with replicator and list
	vec<bool> ivec = [true, false, true;3, false, true];
	if 7 != len(ivec) { println("Test Failed, " + FILELINE); }
	i32 sum = 0;
	for i in len(ivec) {
		if ivec[i] { sum = sum + 1; }
	}
	if 5 != sum { println("Test Failed, " + FILELINE); }
	
	// append test
	vec<bool> tvec = [true, false];
	vec::append(tvec, true);
	if 3 != len(tvec) { println("Test Failed, " + FILELINE); }
	if !tvec[0] || tvec[1] || !tvec[2] { println("Test Failed, " + FILELINE); }

	
	// vec<bool> x = [1, 2, 3];
	// if !vec::contains(x, 1) { println("Test Failed, " + FILELINE); }
	// if vec::contains(x, 4) { println("Test Failed, " + FILELINE); }
	
	//vec<bool> v = vec::fill(true, 5);
	//i32 sum = 0;
	//for i in len(v) {
	//	if v[i] { sum = sum + 1; }
	//}
	//if sum != 5 { println("Test Failed, " + FILELINE); }
}


//-----------------------------------------------------------------------------
println("-- fixed vec string test");
{
	// zero initializer
	vec<string,1> ivec;
	if 1 != len(ivec) { println("Test Failed, " + FILELINE); }
	if "" != ivec[0] { println("Test Failed, " + FILELINE); }

	// bracket initializer list with redefinition
	vec<string,3> ivec = ["a", "bb", "ccc"];
	if 3 != len(ivec) { println("Test Failed, " + FILELINE); }
	if "a" != ivec[0] || "bb" != ivec[1] || "ccc" != ivec[2] { println("Test Failed, " + FILELINE); }

	// bracket assignment
	vec<string,3> ivec;
	ivec = ["a", "bb", "ccc"];
	if 3 != len(ivec) { println("Test Failed, " + FILELINE); }
	if "a" != ivec[0] || "bb" != ivec[1] || "ccc" != ivec[2] { println("Test Failed, " + FILELINE); }

	// index assignment
	ivec[1] = "d";
	if "a" != ivec[0] || "d" != ivec[1] || "ccc" != ivec[2] { println("Test Failed, " + FILELINE); }

	// deep copy using assignment
	vec<string,3> avec;
	avec = ivec;
	if 3 != len(avec) { println("Test Failed, " + FILELINE); }
	if "a" != avec[0] || "d" != avec[1] || "ccc" != avec[2] { println("Test Failed, " + FILELINE); }

	// deep copy using initializer list
	vec<string,3> bvec = avec;
	if 3 != len(bvec) { println("Test Failed, " + FILELINE); }
	if "a" != bvec[0] || "d" != bvec[1] || "ccc" != bvec[2] { println("Test Failed, " + FILELINE); }

	// assignment after deep copy
	bvec[1] = "fff";
	if "a" != bvec[0] || "fff" != bvec[1] || "ccc" != bvec[2] { println("Test Failed, " + FILELINE); }
	
	// index variable test
	i32 idx = 1;
	bvec[idx] = "g";
	if bvec[idx] != "g" { println("Test Failed, " + FILELINE); }
	if "g" != bvec[1] { println("Test Failed, " + FILELINE); }

	// assignment to index from variable with index
	i32 i0 = 1;
	i32 i1 = 2;
	bvec[i1] = avec[i0];
	if "a" != bvec[0] || "g" != bvec[1] || "d" != bvec[2] { println("Test Failed, " + FILELINE); }
	bvec[1] = avec[2];
	if "a" != bvec[0] || "ccc" != bvec[1] || "d" != bvec[2] { println("Test Failed, " + FILELINE); }
	
	// bracket initializer with replicator
	vec<string,5> ivec = ["h"; 5];
	if 5 != len(ivec) { println("Test Failed, " + FILELINE); }
	i32 sum = 0;
	for i in len(ivec) {
		if ivec[i] == "h" { sum = sum + 1; }
	}
	if 5 != sum { println("Test Failed, " + FILELINE); }
	i32 sum = 0; // by value summation
	for i in ivec {
		if i == "h" { sum = sum + 1; }
	}
	if 5 != sum { println("Test Failed, " + FILELINE); }
	i32 sum = 0; // by key,value summation
	for k,v in ivec {
		if ivec[k] == "h" { sum = sum + 1; }
		if v == "h" { sum = sum + 1; }
	}
	if 10 != sum { println("Test Failed, " + FILELINE); }
	
	// bracket initializer with replicator and list
	vec<string,7> ivec = ["i", "j";5, "k"];
	if 7 != len(ivec) { println("Test Failed, " + FILELINE); }
	i32 sum = 0;
	for i in len(ivec) {
		if ivec[i] == "j" { sum = sum + 1; }
	}
	if 5 != sum { println("Test Failed, " + FILELINE); }
	
	// vec<string> x = [1, 2, 3];
	// if !vec::contains(x, 1) { println("Test Failed, " + FILELINE); }
	// if vec::contains(x, 4) { println("Test Failed, " + FILELINE); }	
}


//-----------------------------------------------------------------------------
println("-- dynamic vec string test");
{
	// zero initializer
	vec<string> ivec;
	if 0 != len(ivec) { println("Test Failed, " + FILELINE); }
	
	// bracket initializer list with redefinition
	vec<string> ivec = ["a", "bb", "ccc"];
	if 3 != len(ivec) { println("Test Failed, " + FILELINE); }
	if "a" != ivec[0] || "bb" != ivec[1] || "ccc" != ivec[2] { println("Test Failed, " + FILELINE); }
	
	// bracket assignment
	vec<string> ivec;
	ivec = ["a", "bb", "ccc"];
	if 3 != len(ivec) { println("Test Failed, " + FILELINE); }
	if "a" != ivec[0] || "bb" != ivec[1] || "ccc" != ivec[2] { println("Test Failed, " + FILELINE); }

	// index assignment
	ivec[1] = "d";
	if "a" != ivec[0] || "d" != ivec[1] || "ccc" != ivec[2] { println("Test Failed, " + FILELINE); }
	
	// deep copy using assignment
	vec<string> avec;
	avec = ivec;
	if 3 != len(avec) { println("Test Failed, " + FILELINE); }
	if "a" != avec[0] || "d" != avec[1] || "ccc" != avec[2] { println("Test Failed, " + FILELINE); }

	// deep copy using initializer list
	vec<string> bvec = avec;
	if 3 != len(bvec) { println("Test Failed, " + FILELINE); }
	if "a" != bvec[0] || "d" != bvec[1] || "ccc" != bvec[2] { println("Test Failed, " + FILELINE); }

	// assignment after deep copy
	bvec[1] = "fff";
	if "a" != bvec[0] || "fff" != bvec[1] || "ccc" != bvec[2] { println("Test Failed, " + FILELINE); }
	
	// index variable test
	i32 idx = 1;
	bvec[idx] = "g";
	if bvec[idx] != "g" { println("Test Failed, " + FILELINE); }
	if "g" != bvec[1] { println("Test Failed, " + FILELINE); }

	// assignment to index from variable with index
	i32 i0 = 1;
	i32 i1 = 2;
	bvec[i1] = avec[i0];
	if "a" != bvec[0] || "g" != bvec[1] || "d" != bvec[2] { println("Test Failed, " + FILELINE); }
	bvec[1] = avec[2];
	if "a" != bvec[0] || "ccc" != bvec[1] || "d" != bvec[2] { println("Test Failed, " + FILELINE); }
	
	// bracket initializer with replicator
	vec<string> ivec = ["h"; 5];
	if 5 != len(ivec) { println("Test Failed, " + FILELINE); }
	i32 sum = 0;
	for i in len(ivec) {
		if ivec[i] == "h" { sum = sum + 1; }
	}
	if 5 != sum { println("Test Failed, " + FILELINE); }
	i32 sum = 0; // by value summation
	for i in ivec {
		if i == "h" { sum = sum + 1; }
	}
	if 5 != sum { println("Test Failed, " + FILELINE); }
	i32 sum = 0; // by key,value summation
	for k,v in ivec {
		if ivec[k] == "h" { sum = sum + 1; }
		if v == "h" { sum = sum + 1; }
	}
	if 10 != sum { println("Test Failed, " + FILELINE); }
	
	// bracket initializer with replicator and list
	vec<string> ivec = ["i", "j";5, "k"];
	if 7 != len(ivec) { println("Test Failed, " + FILELINE); }
	i32 sum = 0;
	for i in len(ivec) {
		if ivec[i] == "j" { sum = sum + 1; }
	}
	if 5 != sum { println("Test Failed, " + FILELINE); }
	
	// append test
	vec<string> tvec = ["m", "n"];
	vec::append(tvec, "o");
	if 3 != len(tvec) { println("Test Failed, " + FILELINE); }
	if "m" != tvec[0] || "n" != tvec[1] || "o" != tvec[2] { println("Test Failed, " + FILELINE); }
	
	// vec<string> x = [1, 2, 3];
	// if !vec::contains(x, 1) { println("Test Failed, " + FILELINE); }
	// if vec::contains(x, 4) { println("Test Failed, " + FILELINE); }
	
	//vec<string> v = vec::fill("test", 5);
	//println(v);
}


//-----------------------------------------------------------------------------
println("-- file read/write test");
{
	vec<string> a = ["a", "b", "c"];
	file::writelines("test.out", a);
	
	vec<string> b = file::readlines("test.out");
	if 3 != len(b) { println("Test Failed, " + FILELINE); }
	if "a" != b[0] { println("Test Failed, " + FILELINE); }
	if b[1] != "b" { println("Test Failed, " + FILELINE); }
	if "c" != b[2] { println("Test Failed, " + FILELINE); }
	
	vec<string,3> a = ["d", "e", "f"];
	file::writelines("test2.out", a);
	
	vec<string> b = file::readlines("test2.out");
	if 3 != len(b) { println("Test Failed, " + FILELINE); }
	if "d" != b[0] { println("Test Failed, " + FILELINE); }
	if b[1] != "e" { println("Test Failed, " + FILELINE); }
	if "f" != b[2] { println("Test Failed, " + FILELINE); }
	
	string a = "test3\ntest4";
	file::writestring("test3.out", a);
	
	string b = file::readstring("test3.out");
	if b != a { println("Test Failed, " + FILELINE); }
}


//-----------------------------------------------------------------------------
println("-- string operation test");
{
	string s = "a,b,c";
	if !str::contains(s, "a") { println("Test Failed, " + FILELINE); }
	if str::contains(s, "d") { println("Test Failed, " + FILELINE); }

	s = str::replace(s, "c", "d");
	if !str::contains(s, "d") { println("Test Failed, " + FILELINE); }

	vec<string> vs = str::split(s, ",");
	if 3 != len(vs) { println("Test Failed, " + FILELINE); }

	vec<string> vs2 = str::split(s, ",b,");
	if len(vs2) != 2 { println("Test Failed, " + FILELINE); }

	vec<string> vs3 = ["lhs", "rhs"];
	string c = str::join(vs3, ",");
	if c != "lhs,rhs" { println("Test Failed, " + FILELINE); }

	string d = str::join(vs3, "--");
	if d != "lhs--rhs" { println("Test Failed, " + FILELINE); }

	string e = str::substr(d, 3);
	if "--rhs" != e { println("Test Failed, " + FILELINE); }
	
	string f = str::substr(d, 2, 3);
	if "s--" != f { println("Test Failed, " + FILELINE); }
	
	string g = str::substr(d, 0, 4);
	if g != "lhs-" { println("Test Failed, " + FILELINE); }
	
	string h = "Ba_Rf";
	if "BA_RF" != str::toupper(h) { println("Test Failed, " + FILELINE); }
	if "ba_rf" != str::tolower(h) { println("Test Failed, " + FILELINE); }

	if "Ba_Rf" != str::ltrim(h) { println("Test Failed, " + FILELINE); }
	if "Ba_Rf" != str::rtrim(h) { println("Test Failed, " + FILELINE); }
	if "Ba_Rf" != str::trim(h) { println("Test Failed, " + FILELINE); }
	
	string i = " aa   ";
	if "aa   " != str::ltrim(i) { println("Test Failed, " + FILELINE); }
	if " aa" != str::rtrim(i) { println("Test Failed, " + FILELINE); }
	if "aa" != str::trim(i) { println("Test Failed, " + FILELINE); }
	
}

//-----------------------------------------------------------------------------
println("-- stack overflow test");
{
	for i in 10000000 {
		i32 j;
		f32 f;
		enum e;
		bool b;
	}
}


//-----------------------------------------------------------------------------
println("-- memory leak test");
{
	for i in 1000000 {
		string s = rand() as string;
	}
}

//
println("Test Complete");
Löwenherz
Posts: 278
Joined: Aug 27, 2008 6:26
Location: Bad Sooden-Allendorf, Germany

Re: New little Parser example

Post by Löwenherz »

hello all.. I tried To implement ZString in my little parser but it failed With SizeOf(), Any help Or ideas are welcome
I have got As result 12, but it must be 11

thanks, loewenherz

Code: Select all

' new little lionParser update 2, 12-06-2025 by lionheart
' error handling added, zstring, len(), sizeof()
'
' info: you must type in values and strings line after line
'
' freebasic example
'
'' Dim As ZString * 11 abc = "helloworld"
'' Print abc 'ok

'' Print Len(abc)     'ok returns 10, the size of the string it contains
'' Print SizeOf(abc)  '-> not ok returns 12, the size of the variable, wrong should be 11 ;) 

'' sizeof() doesn't work correct here don't know why?
''
' Token types
Enum TokenType
    TOKEN_IDENTIFIER
    TOKEN_NUMBER
    TOKEN_OPERATOR
    TOKEN_STRING
    TOKEN_ZSTRING ' new
    TOKEN_ASSIGN
    TOKEN_PRINT
    TOKEN_SLEEP
    TOKEN_EOF
    TOKEN_UNKNOWN
    TOKEN_MODULUS
    TOKEN_LESS
    TOKEN_GREATER
    TOKEN_LPAREN
    TOKEN_RPAREN
    TOKEN_IF
    TOKEN_THEN
    TOKEN_TRUE
    TOKEN_FALSE
    TOKEN_ELSE
    TOKEN_ENDIF
    TOKEN_LEN ' new
    TOKEN_SIZEOF ' new
End Enum

Type Token
    As Integer kind
    As String value
End Type

Dim Shared As Token tokens(0 To 1000)
Dim Shared As Integer tokenCount = 0

' Symbol table
Dim Shared As String symbolTable(0 To 255)
Dim Shared As String symbolNames(0 To 255)
Dim Shared As Integer symbolCount = 0

Function IsLetter(ch As String) As Boolean
    Return (ch >= "a" And ch <= "z") Or (ch >= "A" And ch <= "Z")
End Function

Function IsDigit1(ch As String) As Boolean
    Return (ch >= "0" And ch <= "9")
End Function

Function GetSymbolIndex(names As String) As Integer
    For i As Integer = 0 To symbolCount - 1
        If symbolNames(i) = names Then Return i
    Next
    symbolNames(symbolCount) = names
    symbolCount += 1
    Return symbolCount - 1
End Function

Sub Tokenize(lines As String)
    Dim As Integer i = 0
    tokenCount = 0
    Do While i < Len(lines)
        Dim As String ch = Mid(lines, i + 1, 1)

        If ch = " " Then
            i += 1
            Continue Do
        ElseIf IsLetter(ch) Then
            Dim As String ident = ""
            Do While i < Len(lines) AndAlso (IsLetter(Mid(lines, i + 1, 1)) Or IsDigit1(Mid(lines, i + 1, 1)))
                ident &= Mid(lines, i + 1, 1)
                i += 1
            Loop
            tokens(tokenCount).value = ident
            Select Case ident
                Case "print"
                    tokens(tokenCount).kind = TOKEN_PRINT
                Case "sleep"
                    tokens(tokenCount).kind = TOKEN_SLEEP
                Case "if"
                    tokens(tokenCount).kind = TOKEN_IF
                Case "then"
                    tokens(tokenCount).kind = TOKEN_THEN
                Case "true"
                    tokens(tokenCount).kind = TOKEN_TRUE
                Case "false"
                    tokens(tokenCount).kind = TOKEN_FALSE
                Case "else"
                    tokens(tokenCount).kind = TOKEN_ELSE
                Case "endif"
                    tokens(tokenCount).kind = TOKEN_ENDIF
                Case "len"
                    tokens(tokenCount).kind = TOKEN_LEN
                Case "sizeof"
                    tokens(tokenCount).kind = TOKEN_SIZEOF
                Case Else
                    tokens(tokenCount).kind = TOKEN_IDENTIFIER
            End Select
            tokenCount += 1
        ElseIf IsDigit1(ch) Then
            Dim As String num = ""
            Do While i < Len(lines) AndAlso IsDigit1(Mid(lines, i + 1, 1))
                num &= Mid(lines, i + 1, 1)
                i += 1
            Loop
            tokens(tokenCount).kind = TOKEN_NUMBER
            tokens(tokenCount).value = num
            tokenCount += 1
        ElseIf ch = """" Then
            Dim As String strx = ""
            i += 1 ' Skip opening quote
            Do While i < Len(lines) AndAlso Mid(lines, i + 1, 1) <> """"
                strx &= Mid(lines, i + 1, 1)
                i += 1
            Loop
            i += 1 ' Skip closing quote
            tokens(tokenCount).kind = TOKEN_STRING
            tokens(tokenCount).value = strx
            tokenCount += 1
        ElseIf ch = "`" Then ' Check for ZString literal (new)
            Dim As ZString*64 zstr = ""
            i += 1 ' Skip opening backtick
            Do While i < Len(lines) AndAlso Mid(lines, i + 1, 1) <> "`"
                zstr &= Mid(lines, i + 1, 1)
                i += 1
            Loop
            i += 1 ' Skip closing backtick
            tokens(tokenCount).kind = TOKEN_ZSTRING
            tokens(tokenCount).value = zstr
            tokenCount += 1
        ElseIf ch = "=" Then
            tokens(tokenCount).kind = TOKEN_ASSIGN
            tokens(tokenCount).value = "="
            tokenCount += 1
            i += 1
        ElseIf ch = "+" Or ch = "-" Or ch = "*" Or ch = "/" Or ch = "%" Then
            tokens(tokenCount).kind = TOKEN_OPERATOR
            tokens(tokenCount).value = ch
            tokenCount += 1
            i += 1
        ElseIf ch = "<" Then
            tokens(tokenCount).kind = TOKEN_LESS
            tokens(tokenCount).value = "<"
            tokenCount += 1
            i += 1
        ElseIf ch = ">" Then
            tokens(tokenCount).kind = TOKEN_GREATER
            tokens(tokenCount).value = ">"
            tokenCount += 1
            i += 1
        ElseIf ch = "(" Then
            tokens(tokenCount).kind = TOKEN_LPAREN
            tokens(tokenCount).value = "("
            tokenCount += 1
            i += 1
        ElseIf ch = ")" Then
            tokens(tokenCount).kind = TOKEN_RPAREN
            tokens(tokenCount).value = ")"
            tokenCount += 1
            i += 1
        Else
            tokens(tokenCount).kind = TOKEN_UNKNOWN
            tokens(tokenCount).value = ch
            tokenCount += 1
            i += 1
        End If
    Loop
    tokens(tokenCount).kind = TOKEN_EOF
End Sub

Function EvalExpression(ByRef index As Integer) As String
    Dim As String result
    Dim As String resultType ' "int" or "str"

    If tokens(index).kind = TOKEN_NUMBER Then
        result = tokens(index).value
        resultType = "int"
        index += 1
    ElseIf tokens(index).kind = TOKEN_IDENTIFIER Then
        Dim As Integer varIdx = GetSymbolIndex(tokens(index).value)
        result = symbolTable(varIdx)
        resultType = "int" ' Assume it's an integer for simplicity
        index += 1
    ElseIf tokens(index).kind = TOKEN_STRING Then
        result = tokens(index).value
        resultType = "str"
        index += 1
    ElseIf tokens(index).kind = TOKEN_ZSTRING Then
        result = tokens(index).value
        resultType = "zstr" ' zstring*x
        index += 1
    ElseIf tokens(index).kind = TOKEN_TRUE Then
        result = "1"
        resultType = "int"
        index += 1
    ElseIf tokens(index).kind = TOKEN_FALSE Then
        result = "0"
        resultType = "int"
        index += 1
    ElseIf tokens(index).kind = TOKEN_LEN Then
        index += 1
        If tokens(index).kind <> TOKEN_LPAREN Then
            Print "Error: Expected '(' after LEN"
            Exit Function
        End If
        index += 1
        Dim As String arg = EvalExpression(index)
        If tokens(index).kind <> TOKEN_RPAREN Then
            Print "Error: Expected ')' after LEN argument"
            Exit Function
        End If
        index += 1
        result = Str(Len(arg))
        resultType = "int"
    ElseIf tokens(index).kind = TOKEN_SIZEOF Then
        index += 1
        If tokens(index).kind <> TOKEN_LPAREN Then
            Print "Error: Expected '(' after SIZEOF"
            Exit Function
        End If
        index += 1
        Dim As String arg = EvalExpression(index)
        If tokens(index).kind <> TOKEN_RPAREN Then
            Print "Error: Expected ')' after SIZEOF argument"
            Exit Function
        End If
        index += 1
        result = Str(SizeOf(arg)) 'sizeof()
        resultType = "int" 'zstr
        
    ElseIf tokens(index).kind = TOKEN_LPAREN Then
        index += 1
        result = EvalExpression(index)
        If tokens(index).kind <> TOKEN_RPAREN Then
            Print "Error: Expected ')'"
            Exit Function
        End If
        index += 1
    End If

    While index < tokenCount AndAlso (tokens(index).kind = TOKEN_OPERATOR Or tokens(index).kind = TOKEN_LESS Or tokens(index).kind = TOKEN_GREATER)
        Dim As String op = tokens(index).value
        index += 1
        Dim As String rhs
        Dim As String rhsType

        If tokens(index).kind = TOKEN_NUMBER Then
            rhs = tokens(index).value
            rhsType = "int"
        ElseIf tokens(index).kind = TOKEN_IDENTIFIER Then
            rhs = symbolTable(GetSymbolIndex(tokens(index).value))
            rhsType = "int" ' Assume it's an integer for simplicity
        ElseIf tokens(index).kind = TOKEN_STRING Then
            rhs = tokens(index).value
            rhsType = "str"
        ElseIf tokens(index).kind = TOKEN_ZSTRING Then
            rhs = tokens(index).value
            rhsType = "zstr"
        ElseIf tokens(index).kind = TOKEN_TRUE Then
            rhs = "1"
            rhsType = "int"
        ElseIf tokens(index).kind = TOKEN_FALSE Then
            rhs = "0"
            rhsType = "int"
        ElseIf tokens(index).kind = TOKEN_LPAREN Then
            index += 1
            rhs = EvalExpression(index)
            If tokens(index).kind <> TOKEN_RPAREN Then
                Print "Error: Expected ')'"
                Exit Function
            End If
            index += 1
            rhsType = "int" 
        End If

        If resultType = "int" AndAlso rhsType = "int" Then
            Select Case op
                Case "+"
                    result = Str(Val(result) + Val(rhs))
                Case "-"
                    result = Str(Val(result) - Val(rhs))
                Case "*"
                    result = Str(Val(result) * Val(rhs))
                Case "/"
                    If Val(rhs) <> 0 Then
                        result = Str(Val(result) / Val(rhs))
                    Else
                        Print "Error: Divide by zero"
                        Exit Function
                    End If
                Case "%"
                    result = Str(Val(result) Mod Val(rhs))
                Case "<"
                    result = Str(-(Val(result) < Val(rhs)))
                Case ">"
                    result = Str(-(Val(result) > Val(rhs)))
            End Select
        Else
            ' Handle string concatenation
            result = result + rhs
            resultType = "str" '"ztr"
        End If
        index += 1
    Wend

    Return result
End Function

Sub ParseStatement(ByRef index As Integer)
    If tokens(index).kind = TOKEN_IDENTIFIER Then
        Dim As Integer varIdx = GetSymbolIndex(tokens(index).value)
        index += 1
        If tokens(index).kind = TOKEN_ASSIGN Then
            index += 1
            symbolTable(varIdx) = EvalExpression(index)
        Else
            Print "Error: Expected assignment operator"
            Exit Sub
        End If
    ElseIf tokens(index).kind = TOKEN_PRINT Then
        index += 1
        Dim As String printResult = EvalExpression(index)
        Print printResult
    ElseIf tokens(index).kind = TOKEN_SLEEP Then
        ' Handle a sleep token
        Sleep
        index += 1
    Else
        Print "Error: Unexpected token"
        Exit Sub
    End If
End Sub

Sub Parse()
    Dim As Integer index = 0
    While index < tokenCount
        If tokens(index).kind = TOKEN_IDENTIFIER Then
            Dim As Integer varIdx = GetSymbolIndex(tokens(index).value)
            index += 1
            If tokens(index).kind = TOKEN_ASSIGN Then
                index += 1
                symbolTable(varIdx) = EvalExpression(index)
            End If
        ElseIf tokens(index).kind = TOKEN_PRINT Then
            index += 1
            Dim As String printResult = EvalExpression(index)
            Print printResult
        ElseIf tokens(index).kind = TOKEN_SLEEP Then
            ' Handle a sleep token
            Sleep
            index += 1
        ElseIf tokens(index).kind = TOKEN_IF Then
            index += 1
            Dim As String condition = EvalExpression(index)
            If tokens(index).kind <> TOKEN_THEN Then
                Print "Error: Expected 'then'"
                Exit Sub
            End If
            index += 1
            If Val(condition) <> 0 Then
                ' Parse the block inside the IF statement
                While index < tokenCount AndAlso tokens(index).kind <> TOKEN_ELSE AndAlso tokens(index).kind <> TOKEN_ENDIF
                    ParseStatement(index)
                Wend
            Else
                ' Skip the block inside the IF statement
                While index < tokenCount AndAlso tokens(index).kind <> TOKEN_ELSE AndAlso tokens(index).kind <> TOKEN_ENDIF
                    index += 1
                Wend
            End If
            If tokens(index).kind = TOKEN_ELSE Then
                index += 1
                ' Parse the block inside the ELSE statement
                While index < tokenCount AndAlso tokens(index).kind <> TOKEN_ENDIF
                    ParseStatement(index)
                Wend
            End If
            If tokens(index).kind = TOKEN_ENDIF Then
                index += 1
            Else
                Print "Error: Expected 'endif'"
                Exit Sub
            End If
        Else
            index += 1
        End If
    Wend
End Sub

' Input for some calculations and strings
Dim As String inputLine
Do
    Line Input "> ", inputLine
    If inputLine = "exit" Then Exit Do
    Tokenize(inputLine)
    Parse()
Loop
' ends, 12-6-2025, newParser update 2

Code: Select all

' test example freebasic
' you must type in line by line by hand
'
Dim As ZString * 11 abc = "helloworld" ' you can only copy this line
Print abc 'ok 

Print Len(abc)     'ok returns 10, the size of the string it contains
Print SizeOf(abc)  '-> not ok returns 12, the size of the variable, wrong should be 11 ;) 

'' sizeof() doesn't work correct here don't know why?
Thank you syn9 for your Code example must still Check it
Last edited by Löwenherz on Jun 12, 2025 19:17, edited 1 time in total.
D.J.Peters
Posts: 8641
Joined: May 28, 2005 3:28
Contact:

Re: New little Parser example

Post by D.J.Peters »

sizeOf(String) returns the size of the struct/type of STRING not the number of chars !

Joshy

Code: Select all

dim as string s      = "1"
dim as zstring * 1 z = "1"
print "nChars(s)     : " & len(s)
print "sizeOf(String): " & SizeOf(s)
print "nChars(z)     : " & len(z)
print "sizeOf(z)     : " & SizeOf(z)
sleep
Löwenherz
Posts: 278
Joined: Aug 27, 2008 6:26
Location: Bad Sooden-Allendorf, Germany

Re: New little Parser example

Post by Löwenherz »

Thank you joshy..

Your correct results are
1
12
0
1

My Parser gives Output

1
12
1
12

Thats my Problem Here ;-) so IT must be zstring Handling isnt correct.
Löwenherz
Posts: 278
Joined: Aug 27, 2008 6:26
Location: Bad Sooden-Allendorf, Germany

Re: New little Parser example

Post by Löwenherz »

(deleted, sorry)
Post Reply