RCSNUSP/AutoHotkey

From Rosetta Code
;---------------------------------------------------------------------------
; SNUSP Interpreter.ahk
; by wolf_II
;---------------------------------------------------------------------------
; interpreter for SNUSP code
;---------------------------------------------------------------------------



;---------------------------------------------------------------------------
Code = ; Goodbye, World!
;---------------------------------------------------------------------------
    (
Example taken from RosettaCode.org

$@\G.@\o.o.@\d.--b.@\y.@\e.>@\comma.@\.<-@\W.+@\o.+++r.------l.@\d.>+.! #
  |   |     \@------|#  |    \@@+@@++|+++#-    \\               -
  |   \@@@@=+++++#  |   \===--------!\===!\-----|-------#-------/
  \@@+@@@+++++#     \!#+++++++++++++++++++++++#!/
    )



;---------------------------------------------------------------------------
AutoExecute: ; auto-execute section of the script
;---------------------------------------------------------------------------
    #SingleInstance, Force          ; only one instance allowed
    #NoEnv                          ; don't check empty variables
    ;-----------------------------------------------------------------------
    AppName := "SNUSP"
    Gosub, GuiCreate
    Gui, Show,, %AppName%

Return



;---------------------------------------------------------------------------
GuiCreate: ; create the main window
;---------------------------------------------------------------------------
    ; GUI options
    Gui, Margin,, 10
    Gui, Add, Edit, y0 h0 ; catch the focus
    Gui, Add, Checkbox, vWatch, Watch

    ; SNUSP Code
    Gui, Add, GroupBox, w554 h265, SNUSP Code
    Gui, Font, s8, Courier New
    Gui, Add, Edit, xp+10 yp+20 w534 r15 -Wrap +HScroll vCode HwndhCode, %Code%
    Gui, Font ; normal

    ; buttons
    Gui, Add, Button, xm w101 h30, &Load Code
    Gui, Add, Button, x+19 wp hp, &Save Code As
    Gui, Add, Button, x+19 wp hp Default, &Run Code
    Gui, Add, Button, x+19 wp hp, E&xit

    ; Output
    Gui, Add, GroupBox, xm w554 h248, Output
    Gui, Font, s8, Courier New
    Gui, Add, Edit, xp+10 yp+20 w534 r15 ReadOnly vOutput HwndhOut
    Gui, Font ; normal

Return



;---------------------------------------------------------------------------
ButtonLoadCode: ; load code from file
;---------------------------------------------------------------------------
    Gui, +OwnDialogs
    FileSelectFile, CodeFile,,, Load SNUSP Code, *.snusp
    If (ErrorLevel = 1) {
        ; user dismissed the dialog
        Gui, Show
        Return
    }
    If Not SubStr(CodeFile, -5) = ".snusp"
        CodeFile .= ".snusp"
    If FileExist(CodeFile) {
        FileRead, Code, %CodeFile%
        GuiControl,, Code, %Code%
    } Else
        MsgBox, 16, Error - %AppName%, File not found:`n`n"%CodeFile%"
    GuiControl,, Output ; clear all output
    Gui, Show

Return



;---------------------------------------------------------------------------
ButtonSaveCodeAs: ; save code to file
;---------------------------------------------------------------------------
    Gui, +OwnDialogs
    Gui, Submit, NoHide
    FileSelectFile, CodeFile, S16,, SNUSP Code, *.snusp
    If (ErrorLevel = 1) {
        ; user dismissed the dialog
        Gui, Show
        Return
    }
    If Not SubStr(CodeFile, -5) = ".snusp"
        CodeFile .= ".snusp"
    FileDelete, %CodeFile%
    FileAppend, %Code%, %CodeFile%
    Gui, Show

Return



;---------------------------------------------------------------------------
GuiClose:   ; {Alt-F4} pressed, [X] clicked
;---------------------------------------------------------------------------
ButtonExit: ; Exit button clicked
;---------------------------------------------------------------------------
    ExitApp

Return



;---------------------------------------------------------------------------
ButtonRunCode: ; run the code from the edit field
;---------------------------------------------------------------------------
    Gui, Submit, NoHide ; get code from GUI
    ;~ GuiControl, Disable, Code
    GuiControl, Disable, Output
    GuiControl, Disable, &Load
    GuiControl, Disable, &Save
    GuiControl, Disable, &Run
    Gosub, PadCode
    ;~ GuiControl, Enable, Code
    GuiControl, Enable, Output
    GuiControl, Enable, &Load
    GuiControl, Enable, &Save
    GuiControl, Enable, &Run

Return



;---------------------------------------------------------------------------
PadCode: ; pad code with spaces
;---------------------------------------------------------------------------
    mL := 0 ; max length
    Loop, Parse, Code, `n
        If (Len := StrLen(A_LoopField)) > mL
            mL := Len
    PaddedCode := ""
    Loop, Parse, Code, `n
        PaddedCode .= A_LoopField Repeat(" ", mL - StrLen(A_LoopField)) "`r`n"
    GuiControl,, Code, % Code := PaddedCode
    ml += 2

    ; find code entry point
    InstrPtr := (pos := InStr(Code, "$")) ? pos : 1

;---------------------------------------------------------------------------
Interpreter: ; this is the SNUSP Interpreter
;---------------------------------------------------------------------------
    ; setup SNUSP environment
    VarSetCapacity(SNUSP_MEM, 4096, 0)
    DataPtr := InstrDir := 0
    ControlFocus,, ahk_id %hCode%
    Loop {

        ; read next instruction
        getNext(InstrPtr)
        CMD := SubStr(Code, InstrPtr, 1)
        Data@Ptr := NumGet(SNUSP_MEM, DataPtr, "Char")
        If Watch {
            Highlight(InstrPtr)
            Sleep, 25
            ;~ ListVars
            ;~ Pause
        }

        If (CMD = "+") ; increment the data at DataPointer
            NumPut(Data@Ptr + 1, SNUSP_MEM, DataPtr, "Char")

        Else If (CMD = "-") ; decrement the data at DataPointer
            NumPut(Data@Ptr - 1, SNUSP_MEM, DataPtr, "Char")

        Else If (CMD = ">") ; increment the DataPointer
            DataPtr++

        Else If (CMD = "<") ; decrement the DataPointer
            DataPtr--

        Else If (CMD = ".") ; print the char at DataPointer
            Output(Chr(Data@Ptr))

        Else If (CMD = ",") ; get a char from stdin (keyboard)
            NumPut(GetChar(), SNUSP_MEM, DataPtr, "Char")

        Else If (CMD = "\") ; LURD
            LURD(InstrDir)

        Else If (CMD = "/") ; RULD
            RULD(InstrDir)

        Else If (CMD = "!") ; SKIP
            getNext(InstrPtr)

        Else If (CMD = "?") { ; SKIPZ
            If (Data@Ptr = 0)
                getNext(InstrPtr)

        } Else If (CMD = "@") { ; ENTER
            Push("Stack", InstrPtr)
            Push("Stack", InstrDir)

        } Else If (CMD = "#") { ; LEAVE
            InstrDir := Pop("Stack")
            InstrPtr := Pop("Stack")
            If (ErrorLevel = -1)
                Break
            Else
                getNext(InstrPtr)

        } Else If (CMD = "") ; this is the end of code
            Break
    }

Return



;---------------------------------------------------------------------------
getNext(ByRef InstrPtr) { ; get next instruction pointer
;---------------------------------------------------------------------------
    ;    instruction directions
    ;         Up = 3
    ;     Left = 2   0 = Right
    ;              1 = Down
    ;-----------------------------------------------------------------------
    global InstrDir, mL
    InstrPtr += (InstrDir > 1 ? -1 : 1) * (InstrDir & 1 ? mL : 1)
}



;---------------------------------------------------------------------------
LURD(ByRef InstrDir) { ; reflect the instruction direction "\"
;---------------------------------------------------------------------------
    ;~ case 0 -> 1
    ;~ case 1 -> 0
    ;~ case 2 -> 3
    ;~ case 3 -> 2
    ;-----------------------------------------------------------------------
    InstrDir += (InstrDir & 1 ? -1 : 1)
}



;---------------------------------------------------------------------------
RULD(ByRef InstrDir) { ; reflect the instruction direction "/"
;---------------------------------------------------------------------------
    ;~ case 0 -> 3
    ;~ case 1 -> 2
    ;~ case 2 -> 1
    ;~ case 3 -> 0
    ;-----------------------------------------------------------------------
    InstrDir := 3 - InstrDir
}



;---------------------------------------------------------------------------
Repeat(String,Count) {
;---------------------------------------------------------------------------
    Loop, %Count%
        Result .= String
    Return, Result
}



;---------------------------------------------------------------------------
GetChar() { ; retrieve a single char from StdIn (keyboard)
;---------------------------------------------------------------------------
    Progress, b2 w150 zh0 fs9, SNUSP Interpreter waits for input ...
    Input, InKey$, L1
    Progress, Off
    Return, Asc(InKey$)
}



;---------------------------------------------------------------------------
Push(Stack, x) { ; push x onto stack named "Stack"
;---------------------------------------------------------------------------
    local pos
    %Stack%_0 := pos := %Stack%_0 ? %Stack%_0 + 1 : 1
    %Stack%_%pos% := x
}



;---------------------------------------------------------------------------
Pop(Stack) { ; pop value from stack named "Stack"
;---------------------------------------------------------------------------
    local pos
    If (pos := %Stack%_0) < 1 ; already empty
        Return,, ErrorLevel := -1
    Return, %Stack%_%pos%, %Stack%_0--
}



;---------------------------------------------------------------------------
Highlight(InstrPtr) { ; select current command in code
;---------------------------------------------------------------------------
    global hCode
    SendMessage, 0xB1, InstrPtr-1, InstrPtr,, ahk_id %hCode% ; EM_SETSEL
}



;---------------------------------------------------------------------------
Output(Text) { ; append text to output
;---------------------------------------------------------------------------
    ; for non-interruptable output
    ;-----------------------------------------------------------------------
    global hOut
    SendMessage, 0xC2,, &Text,, ahk_id %hOut% ; EM_REPLACESEL
}



;---------- end of file ----------------------------------------------------