I've decided to write a A DBP BASIC Syntax Scripting Engine for my
DarkMORG: A Multiplayer Online Roleplaying Game which I have lovenly dubbed DarkMachine. DarkMachine will feature a P-Code Compiler & Virtual Machine, and Command Line Interface (CLI) Interpreter.
Below is working Concept Code that I'm building upon to develope DarkMachine. Others may find it useful to write their own Scripting Engine, Compiler, Virtual Machine, Interpreter, or Parser.
` Title: DarkCompiler
` Author: F.L.Taylor
` Date: 2007-10-22
` Description: Very simple compiler written in DBP Based on Mark Sibly's Blitz Compiler code.
` It takes a 'simple BASIC' source program And converts it into x86 assembler.
` The x86 wont even work 'as-is'. In particular, the condition code stuff in x86
` is downright bizarre And I didn't bother implementing it properly,
` but it shouldn't take too much hacking to get working.
` If someone's really keen, they might want To fix this up, stick the output
` through 'NASM' (a cool, free assembler) And see what happens! The included
` Print command is implemented as a library call, so it'll have To be linked with
` something that exports a 'Print' Function.
` This is designed (hopefully) For simplicity - Not efficiency!
` Have fun!
` Concept Code For DarkMachine Scripting Engine
` Statements supported: let, Print, End, If, Then, Else
` Operators supported: binary + , -, *, /, < , = , > , <= , <> , >= And unary -
` *, / have precedence over + , -, which have precendence over < , = , > , <= , <> , >=
` Parenthesis can be used To group subexpressions.
#Constant TOKE_IDENT 1
#Constant TOKE_CONST 2
#Constant TOKE_LET 3
#Constant TOKE_PRINT 4
#Constant TOKE_END 5
#Constant TOKE_ADD 6
#Constant TOKE_SUB 7
#Constant TOKE_MUL 8
#Constant TOKE_DIV 9
#Constant TOKE_OPENPAR 10
#Constant TOKE_CLOSEPAR 11
#Constant TOKE_EQ 12
#Constant TOKE_LT 13
#Constant TOKE_GT 14
#Constant TOKE_LE 15
#Constant TOKE_GE 16
#Constant TOKE_NE 17
#Constant TOKE_IF 18
#Constant TOKE_THEN 19
#Constant TOKE_ELSE 20
Global toke$
Global lin$
Global label
Type glob
name$
EndType
dim glob() as glob
`========================================================================================
`MAIN
Set Display Mode 800,600,32
Set Window On
Print "`startup..."
Print " sub edx,edx" `'coz idiv uses edx...
t = GetToke()
Do
Print "`" + toke$ + lin$
t = ParseStmt( t )
Loop
End
A_Program:
Data "let a = 1"
Data "let b = 2"
Data "let c = a*b + a/b"
Data "if a < b then print a else print b"
Data "end"
`=======================================================================================
Function ParseStmt( t )
Select t
Case TOKE_LET
If GetToke() <> TOKE_IDENT Then SynErr()
id$ = toke$
Addglob( toke$ )
If GetToke() <> TOKE_EQ Then SynErr()
t = ParseExpr()
Print " mov [_" + id$ + "],eax"
EndCase
Case TOKE_IF
If ParseExpr() <> TOKE_THEN Then SynErr()
Inc label
lab = label
Print " and eax,eax"
Print " jz __" + Str$(lab)
t = ParseStmt( GetToke() )
If t = TOKE_ELSE
Inc label
lab2 = label
Print " jmp __" + Str$(lab2)
Print "__" + Str$(lab) + ":"
t = ParseStmt( GetToke() )
lab = lab2
EndIf
Print "__" + Str$(lab) + ":"
EndCase
Case TOKE_PRINT
t = ParseExpr()
Print " call PRINT"
EndCase
Case TOKE_END
Print " ret"
Print
For Each = 0 to array count (glob())
Print "_" + glob(Each).name$ + ":"
Print " dd 0"
Next 0
Print
Print "Done!"
Wait Key
End
EndCase
Case Default
SynErr()
EndCase
EndSelect
ExitFunction t
EndFunction 0
Function ParseExpr()
returnvalue = ParseComp()
ExitFunction returnvalue
EndFunction 0
Function ParseComp()
t = ParseTerm()
Do
Select t
Case TOKE_LT
op$ = "lt" `these ops are wrong! x86 is weird!
EndCase
Case TOKE_GT
op$ = "gt"
EndCase
Case TOKE_LE
op$ = "le"
EndCase
Case TOKE_GE
op$ = "ge"
EndCase
Case TOKE_EQ
op$ = "eq"
EndCase
Case TOKE_NE
op$ = "ne"
EndCase
Case Default
ExitFunction t
EndCase
EndSelect
Print " push eax"
t = ParseTerm()
Print " pop ecx"
Print " cmp ecx,eax"
Print " s" + op$ + " eax"
Print " and eax,255"
Loop
EndFunction 0
Function ParseTerm()
t = ParseFact()
Do
Select t
Case TOKE_ADD
Print " push eax"
t = ParseFact()
Print " pop ecx"
Print " add eax,ecx"
EndCase
Case TOKE_SUB
Print " push eax"
t = ParseFact()
Print " mov ecx,eax"
Print " pop eax"
Print " sub eax,ecx"
EndCase
Case Default
ExitFunction t
EndCase
EndSelect
Loop
EndFunction 0
Function ParseFact()
t = ParseLeaf()
Do
Select t
Case TOKE_MUL
Print " push eax"
t = ParseLeaf()
Print " pop ecx"
Print " imul eax,ecx"
EndCase
Case TOKE_DIV
Print " push eax"
t = ParseLeaf()
Print " mov ecx,eax"
Print " pop ecx"
Print " idiv eax,ecx"
EndCase
Case Default
ExitFunction t
EndCase
EndSelect
Loop
EndFunction 0
Function ParseLeaf()
t = GetToke()
Select t
Case TOKE_SUB
t = ParseLeaf()
Print " neg eax"
EndCase
Case TOKE_OPENPAR
If ParseExpr() <> TOKE_CLOSEPAR Then SynErr()
t = GetToke()
EndCase
Case TOKE_IDENT
Print " mov eax,[_" + toke$ + "]"
Addglob( toke$ )
t = GetToke()
EndCase
Case TOKE_CONST
Print " mov eax," + toke$
t = GetToke()
EndCase
Case Default
SynErr()
EndCase
EndSelect
ExitFunction t
EndFunction 0
Function Addglob( name$ )
For Each = 0 to array count(glob())
If glob(Each).name$ = name$ Then ExitFunction 0
Next 0
Add To Queue glob()
glob().name$ = name$
EndFunction 0
Function SynErr()
Print "Syntax Error"
Wait Key
End
EndFunction 0
Function CurrCh$()
If lin$ = ""
Read lin$
lin$ = Lower$( lin$ ) + " "
EndIf
returnvalue$ = Left$( lin$,1 )
EndFunction returnvalue$
Function NextCh()
lin$ = Right$(lin$,Len(lin$)-1)
EndFunction 0
Function GetToke()
Repeat
toke$ = CurrCh$()
NextCh()
Until toke$ <> " "
If toke$ >= "0" And toke$ <= "9"
Do
t$ = CurrCh$()
If t$ < "0" Or t$ > "9" Then ExitFunction TOKE_CONST
toke$ = toke$ + t$
NextCh()
Loop
EndIf
If toke$ >= "a" And toke$ <= "z"
Do
t$ = CurrCh$()
If t$ < "a" Or t$ >= "z"
Select toke$
Case "let"
ExitFunction TOKE_LET
EndCase
Case "print"
ExitFunction TOKE_PRINT
EndCase
Case "end"
ExitFunction TOKE_END
EndCase
Case "if"
ExitFunction TOKE_IF
EndCase
Case "then"
ExitFunction TOKE_THEN
EndCase
Case "else"
ExitFunction TOKE_ELSE
EndCase
EndSelect
ExitFunction TOKE_IDENT
EndIf
toke$ = toke$ + t$
NextCh()
Loop
EndIf
Select toke$
Case "+"
ExitFunction TOKE_ADD
EndCase
Case "-"
ExitFunction TOKE_SUB
EndCase
Case "*"
ExitFunction TOKE_MUL
EndCase
Case "/"
ExitFunction TOKE_DIV
EndCase
Case "("
ExitFunction TOKE_OPENPAR
EndCase
Case ")"
ExitFunction TOKE_CLOSEPAR
EndCase
Case "="
If CurrCh$() = "<"
NextCh()
ExitFunction TOKE_LE
EndIf
If CurrCh$() = ">"
NextCh()
ExitFunction TOKE_GE
EndIf
ExitFunction TOKE_EQ
EndCase
Case "<"
If CurrCh$() = "="
NextCh()
ExitFunction TOKE_LE
EndIf
If currCh$() = ">"
NextCh()
ExitFunction TOKE_NE
EndIf
ExitFunction TOKE_LT
EndCase
Case ">"
If CurrCh$() = "="
NextCh()
ExitFunction TOKE_GE
EndIf
If CurrCh$() = "<"
NextCh()
ExitFunction TOKE_NE
EndIf
ExitFunction TOKE_GT
EndCase
EndSelect
SynErr()
EndFunction 0