I would do select..case conditions and conditional loops basically the same as I did if..then conditions: the "TOKEN_IF" case takes care of the entire if..then..else..endif" and all it's statements just like Mark Sibley's example. Here's my if..then..else..endif code (single or multi line):
Note: As you can see, GSC_NextToken() does not return anything! Instead of returning the token type, I find it cleaner and more logical to store the token and token type in global variables (GSC_Token, GSC_TokenType).
I haven't gotten to overloadable functions yet, but I don't think I'll have much trouble with the overloading part; just search all functions for the best match. For the actual function part I'm not sure - probably just as simple as a call stack and "scoping" variables properly.
To impliment custom types I had to "invent" a few special opcodes. These opcodes move a type field to/from another variable/eax/ecx:
Then the compiler can use these to compile code like this:
Into this:
Case TOKEN_IF GSC_ParseExpression() If GSC_TokenType <> TOKEN_THEN Then GSC_AddError("Expected "+Chr(34)+"Then"+Chr(34)) GSC_SkipLine(): GSC_NextToken() Return End If LabelElse = GSC_UniqueLabel() WriteLine GSC_OutputFile, " jz _" + LabelElse GSC_NextToken() If GSC_TokenType = TOKEN_TERMINATOR Then ;Multi-line IF statement Repeat GSC_ParseStatement() If GSC_Done Then GSC_AddError("Expected "+Chr(34)+"Else"+Chr(34)+" or "+Chr(34)+"EndIf"+Chr(34)) Return End If Until GSC_TokenType = TOKEN_ELSE Or GSC_TokenType = TOKEN_ENDIF If GSC_TokenType = TOKEN_ELSE LabelEndif = GSC_UniqueLabel() WriteLine GSC_OutputFile, " jmp _" + LabelEndif WriteLine GSC_OutputFile, "_" + LabelElse + ":" GSC_NextToken() If GSC_TokenType = TOKEN_IF Then GSC_AddWarning("Ambiguous coding style (may cause unexpected errors - see language manual)") Repeat GSC_ParseStatement() If GSC_Done Then GSC_AddError("Expected "+Chr(34)+"EndIf"+Chr(34)) Return End If Until GSC_TokenType = TOKEN_ENDIF WriteLine GSC_OutputFile, "_" + LabelEndif + ":" GSC_NextToken() Else ;EndIf WriteLine GSC_OutputFile, "_" + LabelElse + ":" GSC_NextToken() EndIf Else ;Single-line IF statement GSC_ParseStatement(False) If GSC_TokenType = TOKEN_ELSE LabelEndif = GSC_UniqueLabel() WriteLine GSC_OutputFile, " jmp _" + LabelEndif WriteLine GSC_OutputFile, "_" + LabelElse + ":" GSC_NextToken() GSC_ParseStatement() WriteLine GSC_OutputFile, "_" + LabelEndif + ":" Else WriteLine GSC_OutputFile, "_" + LabelElse + ":" EndIf End If Case TOKEN_ELSE, TOKEN_ENDIF GSC_AddError("Keyword is out of place (there are no open "+Chr(34)+"If"+Chr(34)+" statements)") GSC_NextToken() Return
Note: As you can see, GSC_NextToken() does not return anything! Instead of returning the token type, I find it cleaner and more logical to store the token and token type in global variables (GSC_Token, GSC_TokenType).
I haven't gotten to overloadable functions yet, but I don't think I'll have much trouble with the overloading part; just search all functions for the best match. For the actual function part I'm not sure - probably just as simple as a call stack and "scoping" variables properly.
To impliment custom types I had to "invent" a few special opcodes. These opcodes move a type field to/from another variable/eax/ecx:
mfc a, b Moves the contents of the ecx."b" (type ecx, field "b") into "a" mfa a, b Moves the contents of the eax."b" (type eax, field "b") into "a" (same as mfc except uses eax instead of ecx) xfc a, b Moves the contents of "b" into ecx."a" (type ecx, field "a") xfa a, b Moves the contents of "b" into eax."a" (type eax, field "a") (same as xfc except uses eax instead of ecx)
Then the compiler can use these to compile code like this:
Type Car Field a:Int, b:Float EndType Local test:Car, tmp:Float test = New Car test.a = 23 tmp = test.a * 10 Delete test
Into this:
;test = New Car new eax,{car} mov [test], eax ;test.a = 23 mov eax,23 mov ecx, [test] xfc <a>, eax ;tmp = test.a * 10 mov eax,[test] mfa eax,<a> push eax mov eax,10 pop ecx mul eax,ecx mov [tmp], eax mov eax,[test] ;Delete test del eax end