-- 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
]