summaryrefslogtreecommitdiff
path: root/2019/ic-asm.hs
blob: aec6e5144cd89d541bd5e711fa206274994100ec (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
module Main where

import qualified Data.Map.Strict as Map
import qualified Data.Set as Set

import qualified IntCode as IC


type Name = String

data Ref = Imm Int | Var Name
  deriving (Show)

data Stmt
    = Declare Name
    | Set Name Ref
    | Add Name Ref Ref
    | Mul Name Ref Ref
    | Input Name
    | Output Ref
    | Clt Name Ref Ref
    | Ceq Name Ref Ref
    | If Ref [Stmt] [Stmt]
    | While Ref [Stmt]
    | Halt
  deriving (Show)

assemble :: [Stmt] -> [Integer]
assemble stmts =
    IC.unparse (IC.Jnz (IC.Imm 1) (IC.mkImm (3 + nvars)))
    ++ replicate nvars 0
    ++ concatMap IC.unparse (snd (goL nvars stmts))
  where
    vars = Set.unions (map collectDeclares stmts)
    var2idx = Map.fromList (zip (Set.toList vars) [3..])
    nvars = Set.size vars

    goL :: Int -> [Stmt] -> (Int, [IC.IC])
    goL = (fmap concat .) . foldProduce go

    go :: Int -> Stmt -> ([IC.IC], Int)
    go off (Declare _) = ([], off)
    go off (Set n a) = go off (Add n a (Imm 0))
    go off (Add n a b) = ([IC.Add (ref a) (ref b) (ref (Var n))], off + 4)
    go off (Mul n a b) = ([IC.Mul (ref a) (ref b) (ref (Var n))], off + 4)
    go off (Input n) = ([IC.Inp (ref (Var n))], off + 2)
    go off (Output a) = ([IC.Out (ref a)], off + 2)
    go off (Clt n a b) = ([IC.Clt (ref a) (ref b) (ref (Var n))], off + 4)
    go off (Ceq n a b) = ([IC.Clt (ref a) (ref b) (ref (Var n))], off + 4)
    go off (If a [] s2) =
        let (off', ics2) = goL (off + 3) s2
        in (IC.Jnz (ref a) (IC.mkImm off') : ics2
           , off')
    go off (If a s1 s2) =
        let (offAfterThen, ics2) = goL (off + 3) s2
            (offAfterElse, ics1) = goL (offAfterThen + 3) s1
        in ([IC.Jez (ref a) (IC.mkImm (offAfterThen + 3))]
            ++ ics2
            ++ [IC.Jnz (IC.Imm 1) (IC.mkImm offAfterElse)]
            ++ ics1
           , offAfterElse)
    go off (While a s) =
        let (offAfterBody, ics) = goL (off + 3) s
        in ([IC.Jez (ref a) (IC.mkImm (offAfterBody + 3))]
            ++ ics
            ++ [IC.Jnz (ref a) (IC.mkImm (off + 3))]
           , offAfterBody + 3)
    go off Halt = ([IC.Hlt], off + 1)

    ref (Imm n) = IC.mkImm n
    ref (Var n) = case Map.lookup n var2idx of
                      Just idx -> IC.Addr idx
                      Nothing  -> error $ "Undeclared variable '" ++ n ++ "'"

collectDeclares :: Stmt -> Set.Set Name
collectDeclares (Declare n) = Set.singleton n
collectDeclares (If _ a b) = Set.unions (map collectDeclares (a ++ b))
collectDeclares (While _ a) = Set.unions (map collectDeclares a)
collectDeclares _ = Set.empty

foldProduce :: (s -> a -> (b, s)) -> s -> [a] -> (s, [b])
foldProduce _ s [] = (s, [])
foldProduce f s (x:xs) = let (y, s') = f s x in fmap (y :) (foldProduce f s' xs)


main :: IO ()
main = do
    let prog = [Declare "a"
               ,Declare "c"
               ,Declare "out"
               ,Set "out" (Imm 1)
               ,Input "a"
               ,Clt "c" (Imm 0) (Var "a")
               ,While (Var "c")
                    [Mul "out" (Var "out") (Var "a")
                    ,Add "a" (Var "a") (Imm (-1))
                    ,Clt "c" (Imm 0) (Var "a")
                    ]
               ,Output (Var "out")
               ,Halt
               ]
    let res = assemble prog
    print res
    print (snd (IC.run res [10]))