December 04, 2020, 10:42:51 AM

Author Topic: [bb] Blitz Compiler by marksibly [ 1+ years ago ]  (Read 432 times)

Offline BlitzBot

  • Jr. Member
  • **
  • Posts: 1
[bb] Blitz Compiler by marksibly [ 1+ years ago ]
« on: June 29, 2017, 12:28:40 AM »
Title : Blitz Compiler
Author : marksibly
Posted : 1+ years ago

Description : I get quite a few emails about just what's involved in writing a compiler, so thought I'd throw this together.

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! Blitz code generation does considerably better than this, and will improve further over time.

Have fun!


Code :
Code: BlitzBasic
  1.  
  2. ;Very simple compiler.
  3. ;
  4. ;Blitz does NOT generate code like this! But the parsing is similar...
  5. ;
  6. ;Statements supported: let, print, end, if, then, else
  7. ;
  8. ;Operators supported: binary +, -, *, /, <, =, >, <=, <>, >= and unary -
  9. ;
  10. ;*, / have precedence over +, -, which have precendence over <,=,>,<=,<>,>=
  11. ;
  12. ;Parenthesis can be used to group subexpressions.
  13.  
  14. ;**** The program *****
  15. ;
  16. Data "let a=1"
  17. Data "let b=2"
  18. Data "let c=a*b+a/b"
  19. Data "if a<b then print a else print b"
  20. Data "end"
  21.  
  22. Graphics 320,700,0,2
  23. SetFont LoadFont( "blitz" )
  24. Color 0,255,0
  25.  
  26. Type Glob
  27.         Field name$
  28. End Type
  29.  
  30. Global toke$,lin$,label
  31.  
  32. Const TOKE_IDENT=1,TOKE_CONST=2
  33. Const TOKE_LET=3,TOKE_PRINT=4,TOKE_END=5
  34. Const TOKE_ADD=6,TOKE_SUB=7,TOKE_MUL=8,TOKE_DIV=9
  35. Const TOKE_OPENPAR=10,TOKE_CLOSEPAR=11
  36. Const TOKE_EQ=12,TOKE_LT=13,TOKE_GT=14,TOKE_LE=15,TOKE_GE=16,TOKE_NE=17
  37. Const TOKE_IF=18,TOKE_THEN=19,TOKE_ELSE=20
  38.  
  39. Print ";startup..."
  40. Print "    sub   edx,edx"       ;'coz idiv uses edx...
  41.  
  42. t=GetToke()
  43. Repeat
  44.         Print ";"+toke$+lin$
  45.         t=ParseStmt( t )
  46. Forever
  47.  
  48. Function ParseStmt( t )
  49.         Select t
  50.         Case TOKE_LET
  51.                 If GetToke()<>TOKE_IDENT SynErr()
  52.                 id$=toke$
  53.                 AddGlob( toke$ )
  54.                 If GetToke()<>TOKE_EQ SynErr()
  55.                 t=ParseExpr()
  56.                 Print "    mov   [_"+id$+"],eax"
  57.         Case TOKE_IF
  58.                 If ParseExpr()<>TOKE_THEN SynErr()
  59.                 label=label+1:lab=label
  60.                 Print "    and   eax,eax"
  61.                 Print "    jz    __"+lab
  62.                 t=ParseStmt( GetToke() )
  63.                 If t=TOKE_ELSE
  64.                         label=label+1:lab2=label
  65.                         Print "    jmp   __"+lab2
  66.                         Print "__"+lab+":"
  67.                         t=ParseStmt( GetToke() )
  68.                         lab=lab2
  69.                 EndIf
  70.                 Print "__"+lab+":"
  71.         Case TOKE_PRINT
  72.                 t=ParseExpr()
  73.                 Print "    call  PRINT"
  74.         Case TOKE_END
  75.                 Print "    ret"
  76.                 Print
  77.                 For gl.Glob=Each Glob
  78.                         Print "_"+gl
  79. ame+":"
  80.                         Print "    dd    0"
  81.                 Next
  82.                 Print:Print "Done!":WaitKey
  83.                 End
  84.         Default
  85.                 SynErr()
  86.         End Select
  87.         Return t
  88. End Function
  89.  
  90. Function ParseExpr()
  91.         Return ParseComp()
  92. End Function
  93.  
  94. Function ParseComp()
  95.         t=ParseTerm()
  96.         Repeat
  97.                 Select t
  98.                 Case TOKE_LT op$="lt"   ;these ops are wrong! x86 is weird!
  99.                 Case TOKE_GT op$="gt"
  100.                 Case TOKE_LE op$="le"
  101.                 Case TOKE_GE op$="ge"
  102.                 Case TOKE_EQ op$="eq"
  103.                 Case TOKE_NE op$="ne"
  104.                 Default Return t
  105.                 End Select
  106.                 Print "    push  eax"
  107.                 t=ParseTerm()
  108.                 Print "    pop   ecx"
  109.                 Print "    cmp   ecx,eax"
  110.                 Print "    s"+op$+"   eax"
  111.                 Print "    and   eax,255"
  112.         Forever
  113. End Function
  114.  
  115. Function ParseTerm()
  116.         t=ParseFact()
  117.         Repeat
  118.                 Select t
  119.                 Case TOKE_ADD
  120.                         Print "    push  eax"
  121.                         t=ParseFact()
  122.                         Print "    pop   ecx"
  123.                         Print "    add   eax,ecx"
  124.                 Case TOKE_SUB
  125.                         Print "    push  eax"
  126.                         t=ParseFact()
  127.                         Print "    mov   ecx,eax"
  128.                         Print "    pop   eax"
  129.                         Print "    sub   eax,ecx"
  130.                 Default
  131.                         Return t
  132.                 End Select
  133.         Forever
  134. End Function
  135.  
  136. Function ParseFact()
  137.         t=ParseLeaf()
  138.         Repeat
  139.                 Select t
  140.                 Case TOKE_MUL
  141.                         Print "    push  eax"
  142.                         t=ParseLeaf()
  143.                         Print "    pop   ecx"
  144.                         Print "    imul  eax,ecx"
  145.                 Case TOKE_DIV
  146.                         Print "    push  eax"
  147.                         t=ParseLeaf()
  148.                         Print "    mov   ecx,eax"
  149.                         Print "    pop   ecx"
  150.                         Print "    idiv  eax,ecx"
  151.                 Default
  152.                         Return t
  153.                 End Select
  154.         Forever
  155. End Function
  156.  
  157. Function ParseLeaf()
  158.         t=GetToke()
  159.         Select t
  160.         Case TOKE_SUB
  161.                 t=ParseLeaf()
  162.                 Print "    neg   eax"
  163.         Case TOKE_OPENPAR
  164.                 If ParseExpr() <> TOKE_CLOSEPAR SynErr()
  165.                 t=GetToke()
  166.         Case TOKE_IDENT
  167.                 Print "    mov   eax,[_"+toke$+"]"
  168.                 AddGlob( toke$ )
  169.                 t=GetToke()
  170.         Case TOKE_CONST
  171.                 Print "    mov   eax,"+toke$
  172.                 t=GetToke()
  173.         Default
  174.                 SynErr()
  175.         End Select
  176.         Return t
  177. End Function
  178.  
  179. Function AddGlob( name$ )
  180.         For gl.Glob=Each Glob
  181.                 If gl
  182. ame=name Return
  183.         Next
  184.         gl=New Glob
  185.         gl
  186. ame=name
  187. End Function
  188.  
  189. Function SynErr()
  190.         RuntimeError "Syntax Error"
  191. End Function
  192.        
  193. Function CurrCh$()
  194.         If lin$=""
  195.                 Read lin$
  196.                 lin$=Lower$( lin$ )+" "
  197.         EndIf
  198.         Return Left( lin$,1 )
  199. End Function
  200.  
  201. Function NextCh()
  202.         lin$=Mid$( lin$,2 )
  203. End Function
  204.  
  205. Function GetToke()
  206.  
  207.         Repeat
  208.                 toke$=CurrCh$()
  209.                 NextCh()
  210.         Until toke$<>" "
  211.        
  212.         If toke$>="0" And toke$<="9"
  213.                 Repeat
  214.                         t$=CurrCh$()
  215.                         If t$<"0" Or t$>"9" Return TOKE_CONST
  216.                         toke$=toke$+t$
  217.                         nextCh()
  218.                 Forever
  219.         EndIf
  220.        
  221.         If toke$>="a" And toke$<="z"
  222.                 Repeat
  223.                         t$=CurrCh$()
  224.                         If t$<"a" Or t$>="z"
  225.                                 Select toke$
  226.                                 Case "let" Return TOKE_LET
  227.                                 Case "print" Return TOKE_PRINT
  228.                                 Case "end" Return TOKE_END
  229.                                 Case "if" Return TOKE_IF
  230.                                 Case "then" Return TOKE_THEN
  231.                                 Case "else" Return TOKE_ELSE
  232.                                 End Select
  233.                                 Return TOKE_IDENT
  234.                         EndIf
  235.                         toke$=toke$+t$
  236.                         NextCh()
  237.                 Forever
  238.         EndIf
  239.        
  240.         Select toke$
  241.         Case "+" Return TOKE_ADD
  242.         Case "-" Return TOKE_SUB
  243.         Case "*" Return TOKE_MUL
  244.         Case "/" Return TOKE_DIV
  245.         Case "(" Return TOKE_OPENPAR
  246.         Case ")" Return TOKE_CLOSEPAR
  247.         Case "="
  248.                 If CurrCh$()="<" NextCh():Return TOKE_LE
  249.                 If CurrCh$()=">" NextCh():Return TOKE_GE
  250.                 Return TOKE_EQ
  251.         Case "<"
  252.                 If CurrCh$()="=" NextCh():Return TOKE_LE
  253.                 If currCh$()=">" NextCh():Return TOKE_NE
  254.                 Return TOKE_LT
  255.         Case ">"
  256.                 If CurrCh$()="=" NextCh():Return TOKE_GE
  257.                 If CurrCh$()="<" NextCh():Return TOKE_NE
  258.                 Return TOKE_GT
  259.         End Select
  260.        
  261.         SynErr()
  262.        
  263. End Function


Comments :


Agamer(Posted 1+ years ago)

 Thought I better comment on this after coming across it and I must say very interesting!


Techlord(Posted 1+ years ago)

 This is an excellent start for writing your own scripting language in blitz. Now if I can only figure out how to handle procedures, loops, arrays, and types.


Erroneouss(Posted 1+ years ago)

 case without select error?anyone else get that?


Floyd(Posted 1+ years ago)

 Sometimes the displayed code is corrupted.Whenever that happens you should click the download link at the top of the thread.


Boulderdash(Posted 1+ years ago)

 Cool... I have also made a compiler for 2650,6502,Z80 etc sort of CPU's useful for retro dev's making vintage console games, its intesting if not very useful to most people.my compiler uses a pre-compiler to reformat if...then constructs and expressiions.


Nate the Great(Posted 1+ years ago)

 I know this is old but its still pretty cool. helpful for a scripting language im messing around with


 

SimplePortal 2.3.6 © 2008-2014, SimplePortal