summaryrefslogtreecommitdiff
path: root/src/compiler/GF/Compile/GenerateBC.hs
blob: 488368887ec66c0e14dbc7cf5c42a5ae128f6389 (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
module GF.Compile.GenerateBC(generateByteCode) where

import GF.Grammar
import GF.Grammar.Lookup(lookupAbsDef)
import GF.Data.Operations
import PGF(CId,utf8CId)
import PGF.Internal(Instr(..))
import qualified Data.Map as Map
import Data.List(mapAccumL)

generateByteCode :: SourceGrammar -> Int -> [L Equation] -> [Instr]
generateByteCode gr arity eqs =
  compileEquations gr arity is (map (\(L _ (ps,t)) -> ([],ps,t)) eqs)
  where
    is = push_is (arity-1) arity []

compileEquations :: SourceGrammar -> Int -> [Int] -> [([(Ident,Int)],[Patt],Term)] -> [Instr]
compileEquations gr st _  []            = [FAIL]
compileEquations gr st [] ((vs,[],t):_) =
  let (heap,instrs) = compileBody gr st vs t 0 []
  in (if heap > 0 then (ALLOC heap :) else id)
     (instrs ++ [RET st])
compileEquations gr st (i:is) eqs       = whilePP eqs Map.empty
  where
    whilePP []                           cns     = mkCase cns []
    whilePP ((vs, PP c ps' : ps, t):eqs) cns     = whilePP eqs (Map.insertWith (++) (Q c,length ps') [(vs,ps'++ps,t)] cns)
    whilePP ((vs, PInt n   : ps, t):eqs) cns     = whilePP eqs (Map.insertWith (++) (EInt n,0) [(vs,ps,t)] cns)
    whilePP ((vs, PString s: ps, t):eqs) cns     = whilePP eqs (Map.insertWith (++) (K s,0) [(vs,ps,t)] cns)
    whilePP ((vs, PFloat d : ps, t):eqs) cns     = whilePP eqs (Map.insertWith (++) (EFloat d,0) [(vs,ps,t)] cns)
    whilePP eqs                          cns     = whilePV eqs cns []

    whilePV []                           cns vrs = mkCase cns (reverse vrs)
    whilePV ((vs, PV x     : ps, t):eqs) cns vrs = whilePV eqs cns (((x,i):vs,ps,t) : vrs)
    whilePV ((vs, PW       : ps, t):eqs) cns vrs = whilePV eqs cns ((      vs,ps,t) : vrs)
    whilePV eqs                          cns vrs = mkCase cns (reverse vrs) ++ compileEquations gr st (i:is) eqs

    mkCase cns vrs
      | Map.null cns = compileEquations gr st is vrs
      | otherwise    = EVAL (st-i-1) :
                       concat [compileBranch t n eqs | ((t,n),eqs) <- Map.toList cns] ++
                       compileEquations gr st is vrs

    compileBranch t n eqs =
      let case_instr = 
            case t of
              (Q (_,id)) -> CASE (i2i id)
              (EInt n)   -> CASE_INT n
              (K s)      -> CASE_STR s
              (EFloat d) -> CASE_FLT d
          instrs = compileEquations gr (st+n) (push_is st n is) eqs
      in case_instr (length instrs) : instrs

compileBody gr st vs (App e1 e2) h0 es = compileBody gr st vs e1 h0 (e2:es)
compileBody gr st vs (Q (m,id))  h0 es = case lookupAbsDef gr m id of
                                           Ok (Just _,Just _) 
                                              -> let ((h1,st1),iis) = mapAccumL (compileArg gr st vs) (h0,st) (reverse es)
                                                     (is1,is2,is3) = unzip3 iis
                                                 in (h1,concat is3 ++ is2 ++ [TAIL_CALL (i2i id)])
                                           _  -> let h1 = h0 + 2 + length es
                                                     ((h2,st1),iis)  = mapAccumL (compileArg gr st vs) (h1,st) es
                                                     (is1,is2,is3) = unzip3 iis
                                                 in (h2,PUT_CONSTR (i2i id) : concat (is1:is3))
compileBody gr st vs (QC qid)    h0 es = compileBody gr st vs (Q qid) h0 es
compileBody gr st vs (Vr x)      h0 es = case lookup x vs of
                                           Just i  -> let ((h1,st1),iis)  = mapAccumL (compileArg gr st vs) (h0,st) (reverse es)
                                                          (is1,is2,is3) = unzip3 iis
                                                      in (h1,concat is3 ++ is2 ++ [EVAL (st-i-1)])
                                           Nothing -> error "compileBody: unknown variable"
compileBody gr st vs (EInt n)    h0 _  = let h1 = h0 + 2
                                         in (h1,[PUT_INT n])
compileBody gr st vs (K s)       h0 _  = let h1 = h0 + 1 + (length s + 4) `div` 4
                                         in (h1,[PUT_STR s])
compileBody gr st vs (EFloat d)  h0 _  = let h1 = h0 + 3
                                         in (h1,[PUT_FLT d])

compileArg gr st vs (h0,st0) (Vr x) =
  case lookup x vs of
    Just i  -> ((h0,st0+1),(SET_VARIABLE (st-i-1),PUSH_VARIABLE (st0-i-1),[]))
    Nothing -> error "compileFunArg: unknown variable"
compileArg gr st vs (h0,st0) e =
  let (h1,is2) = compileBody gr st vs e h0 []
  in ((h1,st0+1),(SET_VALUE h0,PUSH_VALUE h0,is2))

i2i :: Ident -> CId
i2i = utf8CId . ident2utf8

push_is :: Int -> Int -> [Int] -> [Int]
push_is i 0 is = is
push_is i n is = i : push_is (i-1) (n-1) is