git @ Cat's Eye Technologies Turmac / master src / Language / Turmac / Backend / Python.hs
master

Tree @master (Download .tar.gz)

Python.hs @masterraw · history · blame

-- SPDX-FileCopyrightText: Chris Pressey, the creator of this work, has dedicated it to the public domain.
-- For more information, please refer to <https://unlicense.org/>
-- SPDX-License-Identifier: Unlicense

module Language.Turmac.Backend.Python where

import Data.List (intercalate)

import Language.Turmac.Model
import Language.Turmac.IR
import Language.Turmac.IRCompiler

compileToPython :: [TMRule] -> String
compileToPython tmRules = let prog = buildProgram tmRules in unlines [
    "class Tape:",
    "    def __init__(self, initial_input=None):",
    "        self.left_cells = []",
    "        self.current_cell = '_'",
    "        self.right_cells = []",
    "        if initial_input is not None:",
    "            initial_input = [str(x) for x in initial_input]",
    "            self.current_cell = initial_input[0] if initial_input else '_'",
    "            self.right_cells = list(initial_input[1:]) if len(initial_input) > 1 else []",
    "",
    "    def move_left(self):",
    "        if self.left_cells:",
    "            self.right_cells.insert(0, self.current_cell)",
    "            self.current_cell = self.left_cells.pop()",
    "        else:",
    "            self.right_cells.insert(0, self.current_cell)",
    "            self.current_cell = '_'",
    "",
    "    def move_right(self):",
    "        if self.right_cells:",
    "            self.left_cells.append(self.current_cell)",
    "            self.current_cell = self.right_cells.pop(0)",
    "        else:",
    "            self.left_cells.append(self.current_cell)",
    "            self.current_cell = '_'",
    "",
    "    def write_symbol(self, symbol):",
    "        self.current_cell = str(symbol)",
    "",
    "    def read_symbol(self):",
    "        return self.current_cell",
    "",
    "    def get_tape_contents(self):",
    "        def strip_zeros(lst):",
    "            while lst and lst[-1] == '_':",
    "                lst.pop()",
    "            return lst",
    "",
    "        left = strip_zeros(self.left_cells[:])",
    "        right = strip_zeros(self.right_cells[:])",
    "        return left + [self.current_cell] + right",
    "",
    "class TuringMachine:",
    "    def __init__(self, initial_input=None):",
    "        self.tape = Tape(initial_input)",
    "        self.state = 'S0'",
    "        self.halted = False",
    "",
    "    def step(self):",
    indentPython 2 (compileStep prog),
    "",
    "    def run(self):",
    "        while not self.halted:",
    "            self.step()",
    "",
    "    def get_configuration(self):",
    "        return (self.state, self.tape.get_tape_contents(), self.halted)",
    "",
    "def main():",
    "    import sys",
    "    initial_input = sys.argv[1].split(',') if len(sys.argv) > 1 else None",
    "    tm = TuringMachine(initial_input)",
    "    tm.run()",
    "    state, tape, halted = tm.get_configuration()",
    "    tape_str = ','.join(tape)",
    "    print(f'State: {state}, Tape: [{tape_str}], Halted: {halted}')",
    "",
    "if __name__ == '__main__':",
    "    main()"
    ]

compileStep :: Prog -> String
compileStep (Program p) = compileStep p
compileStep (Seq ps) = intercalate "\n" (map compileStep ps)
compileStep (CondState branches) = unlines [
    "if self.halted:",
    "    return",
    compileBranches "self.state" branches
    ]
compileStep (CondSymbol branches) = 
    compileBranches "self.tape.read_symbol()" branches
compileStep (WriteMoveGoto sym dir state) = unlines [
    "self.tape.write_symbol(" ++ show sym ++ ")",
    if dir == -1 then "self.tape.move_left()" else "self.tape.move_right()",
    if state == "H" then "self.halted = True" else "self.state = " ++ show state
    ]

compileBranches :: String -> [(StateId, Prog)] -> String
compileBranches cond branches = unlines $
    [     "if " ++ cond ++ " == " ++ show val ++ ":" | (val, _) <- take 1 branches] ++
    [indentPython 1 (compileStep prog) | (_, prog) <- take 1 branches] ++
    concat [[
        "elif " ++ cond ++ " == " ++ show val ++ ":",
        indentPython 1 (compileStep prog)
    ] | (val, prog) <- drop 1 branches] ++
    ["else:",
     "    raise ValueError(f'No rules for " ++ cond ++ " = {" ++ cond ++ "}')"
    ]

indentPython :: Int -> String -> String
indentPython n code = unlines [
    replicate (n * 4) ' ' ++ line | line <- lines code
    ]