10898
|
1 |
module Main where
|
|
2 |
|
|
3 |
import Text.PrettyPrint.HughesPJ
|
10904
|
4 |
import qualified Data.MultiMap as MM
|
|
5 |
import Data.Maybe
|
|
6 |
import Data.List
|
10927
|
7 |
import Data.Char
|
10898
|
8 |
|
|
9 |
data HWProtocol = Command String [CmdParam]
|
|
10 |
data CmdParam = Skip
|
|
11 |
| SS
|
|
12 |
| LS
|
|
13 |
| IntP
|
|
14 |
| Many [CmdParam]
|
|
15 |
data ClientStates = NotConnected
|
|
16 |
| JustConnected
|
|
17 |
| ServerAuth
|
|
18 |
| Lobby
|
|
19 |
|
10906
|
20 |
data ParseTree = PTPrefix String [ParseTree]
|
10927
|
21 |
| PTCommand String HWProtocol
|
10902
|
22 |
|
10898
|
23 |
cmd = Command
|
|
24 |
cmd1 s p = Command s [p]
|
|
25 |
cmd2 s p1 p2 = Command s [p1, p2]
|
|
26 |
|
10927
|
27 |
breakCmd (PTCommand (c:cs) params) = (c, PTCommand cs params)
|
10902
|
28 |
|
10898
|
29 |
commands = [
|
|
30 |
cmd "CONNECTED" [Skip, IntP]
|
|
31 |
, cmd1 "NICK" SS
|
|
32 |
, cmd1 "PROTO" IntP
|
|
33 |
, cmd1 "ASKPASSWORD" SS
|
|
34 |
, cmd1 "SERVER_AUTH" SS
|
10904
|
35 |
, cmd1 "JOINING" SS
|
10929
|
36 |
, cmd1 "TEAM_ACCEPTED" SS
|
|
37 |
, cmd1 "HH_NUM" $ Many [SS]
|
|
38 |
, cmd1 "TEAM_COLOR" $ Many [SS]
|
|
39 |
, cmd1 "TEAM_ACCEPTED" SS
|
10904
|
40 |
, cmd1 "BANLIST" $ Many [SS]
|
|
41 |
, cmd1 "JOINED" $ Many [SS]
|
10898
|
42 |
, cmd1 "LOBBY:JOINED" $ Many [SS]
|
10904
|
43 |
, cmd2 "LOBBY:LEFT" SS LS
|
|
44 |
, cmd2 "CLIENT_FLAGS" SS $ Many [SS]
|
|
45 |
, cmd2 "LEFT" SS $ Many [SS]
|
10902
|
46 |
, cmd1 "SERVER_MESSAGE" LS
|
10929
|
47 |
, cmd1 "ERROR" LS
|
|
48 |
, cmd1 "NOTICE" LS
|
|
49 |
, cmd1 "WARNING" LS
|
|
50 |
, cmd1 "JOINING" SS
|
10904
|
51 |
, cmd1 "EM" $ Many [LS]
|
|
52 |
, cmd1 "PING" $ Many [SS]
|
|
53 |
, cmd2 "CHAT" SS LS
|
|
54 |
, cmd2 "SERVER_VARS" SS LS
|
|
55 |
, cmd2 "BYE" SS LS
|
10908
|
56 |
, cmd1 "INFO" $ Many [SS]
|
10929
|
57 |
, cmd1 "ROOMS" $ Many [SS]
|
10904
|
58 |
, cmd "KICKED" []
|
10929
|
59 |
, cmd "RUN_GAME" []
|
|
60 |
, cmd "ROUND_FINISHED" []
|
10898
|
61 |
]
|
|
62 |
|
10929
|
63 |
unknowncmd = PTPrefix "$" [PTCommand "$" $ Command "__UNKNOWN__" [Many [SS]]]
|
|
64 |
|
10927
|
65 |
groupByFirstChar :: [ParseTree] -> [(Char, [ParseTree])]
|
10904
|
66 |
groupByFirstChar = MM.assocs . MM.fromList . map breakCmd
|
10902
|
67 |
|
10927
|
68 |
makePT cmd@(Command n p) = PTCommand n cmd
|
|
69 |
|
10929
|
70 |
buildParseTree cmds = [PTPrefix "!" $ (bpt $ map makePT cmds) ++ [unknowncmd]]
|
10925
|
71 |
bpt cmds = if isJust emptyNamed then cmdLeaf $ fromJust emptyNamed else subtree
|
10904
|
72 |
where
|
10927
|
73 |
emptyNamed = find (\(_, (PTCommand n _:_)) -> null n) assocs
|
10904
|
74 |
assocs = groupByFirstChar cmds
|
10906
|
75 |
subtree = map buildsub assocs
|
10925
|
76 |
buildsub (c, cmds) = let st = bpt cmds in if null $ drop 1 st then maybeMerge c st else PTPrefix [c] st
|
10927
|
77 |
maybeMerge c cmd@[PTCommand {}] = PTPrefix [c] cmd
|
10906
|
78 |
maybeMerge c cmd@[PTPrefix s ss] = PTPrefix (c:s) ss
|
10927
|
79 |
cmdLeaf (c, (hwc:_)) = [PTPrefix [c] [hwc]]
|
10906
|
80 |
|
|
81 |
dumpTree = vcat . map dt
|
|
82 |
where
|
|
83 |
dt (PTPrefix s st) = text s $$ (nest 1 $ vcat $ map dt st)
|
|
84 |
dt _ = empty
|
10904
|
85 |
|
10908
|
86 |
pas2 = buildSwitch $ buildParseTree commands
|
10906
|
87 |
where
|
|
88 |
buildSwitch cmds = text "case getNextChar of" $$ (nest 4 . vcat $ map buildCase cmds) $$ elsePart
|
10927
|
89 |
buildCase (PTCommand {}) = text "#10: <call cmd handler>;"
|
10906
|
90 |
buildCase (PTPrefix (s:ss) cmds) = quotes (char s) <> text ": " <> consumePrefix ss (buildSwitch cmds)
|
|
91 |
consumePrefix "" = id
|
|
92 |
consumePrefix str = (text "consume" <> (parens . quotes $ text str) <> semi $$)
|
|
93 |
zeroChar = text "#0: state:= pstDisconnected;"
|
|
94 |
elsePart = text "else <unknown cmd> end;"
|
10904
|
95 |
|
10929
|
96 |
renderArrays (letters, commands, handlers) = vcat $ punctuate (char '\n') [cmds, l, s, bodies, c]
|
10908
|
97 |
where
|
10925
|
98 |
maybeQuotes s = if null $ tail s then quotes $ text s else text s
|
|
99 |
l = text "const letters: array[0.." <> (int $ length letters - 1) <> text "] of char = "
|
|
100 |
<> parens (hsep . punctuate comma $ map maybeQuotes letters) <> semi
|
|
101 |
s = text "const commands: array[0.." <> (int $ length commands - 1) <> text "] of integer = "
|
|
102 |
<> parens (hsep . punctuate comma $ map text commands) <> semi
|
10929
|
103 |
c = text "const handlers: array[0.." <> (int $ length fixedNames - 1) <> text "] of PHandler = "
|
|
104 |
<> parens (hsep . punctuate comma $ map (text . (++) "@handler_") $ reverse fixedNames) <> semi
|
|
105 |
fixedNames = map fixName handlers
|
10927
|
106 |
fixName = map fixChar
|
|
107 |
fixChar c | isLetter c = c
|
|
108 |
| otherwise = '_'
|
10929
|
109 |
bodies = vcat $ punctuate (char '\n') $ map handlerBody fixedNames
|
|
110 |
handlerBody n = text "procedure handler_" <> text n <> semi
|
|
111 |
$+$ text "begin"
|
|
112 |
$+$ nest 4 (
|
|
113 |
text "state.cmd:= cmd_" <> text n <> semi
|
|
114 |
)
|
|
115 |
$+$ text "end" <> semi
|
|
116 |
cmds = text "type TCmdType = " <> parens (hsep $ punctuate comma $ map ((<>) (text "cmd_") . text) fixedNames) <> semi
|
10908
|
117 |
|
10927
|
118 |
pas = renderArrays $ buildTables $ buildParseTree commands
|
10925
|
119 |
where
|
10927
|
120 |
buildTables cmds = let (_, _, _, t1, t2, t3) = foldr walk (0, [0], -10, [], [], [[]]) cmds in (tail t1, tail t2, concat t3)
|
|
121 |
walk (PTCommand _ (Command n params)) (lc, s:sh, pc, tbl1, tbl2, (t3:tbl3)) =
|
10929
|
122 |
(lc, 2:sh, pc - 1, "#10":"#0":tbl1, "0":show pc:tbl2, (n:t3):tbl3)
|
10925
|
123 |
walk (PTPrefix prefix cmds) l = lvldown $ foldr fpf (foldr walk (lvlup l) cmds) prefix
|
10927
|
124 |
lvlup (lc, sh, pc, tbl1, tbl2, tbl3) = (lc, 0:sh, pc, tbl1, tbl2, []:tbl3)
|
|
125 |
lvldown (lc, s1:s2:sh, pc, tbl1, t:tbl2, t31:t32:tbl3) = (lc, s1+s2:sh, pc, tbl1, (if null t32 then "0" else show s1):tbl2, (t31 ++ t32):tbl3)
|
|
126 |
fpf c (lc, s:sh, pc, tbl1, tbl2, tbl3) = (lc + 1, s+1:sh, pc, [c]:tbl1, "0":tbl2, tbl3)
|
10925
|
127 |
|
|
128 |
main = putStrLn $ renderStyle style{lineLength = 80} pas
|