author | unc0rr |
Mon, 14 Mar 2016 22:08:27 +0300 | |
branch | qmlfrontend |
changeset 11606 | 99966b4a6e1e |
parent 11454 | 3c5d99013baf |
permissions | -rw-r--r-- |
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 |
11047 | 8 |
import qualified Data.Set as Set |
10898 | 9 |
|
10 |
data HWProtocol = Command String [CmdParam] |
|
11417 | 11 |
deriving Show |
11048 | 12 |
|
13 |
instance Ord HWProtocol where |
|
14 |
(Command a _) `compare` (Command b _) = a `compare` b |
|
15 |
instance Eq HWProtocol where |
|
16 |
(Command a _) == (Command b _) = a == b |
|
17 |
||
10898 | 18 |
data CmdParam = Skip |
19 |
| SS |
|
20 |
| LS |
|
21 |
| IntP |
|
22 |
| Many [CmdParam] |
|
11417 | 23 |
deriving Show |
10898 | 24 |
|
10906 | 25 |
data ParseTree = PTPrefix String [ParseTree] |
10927 | 26 |
| PTCommand String HWProtocol |
11417 | 27 |
deriving Show |
10902 | 28 |
|
10898 | 29 |
cmd = Command |
30 |
cmd1 s p = Command s [p] |
|
31 |
cmd2 s p1 p2 = Command s [p1, p2] |
|
32 |
||
11076 | 33 |
cmdName (Command n _) = n |
34 |
||
11047 | 35 |
cmdParams2str (Command _ p) = "TCmdParam" ++ concatMap f p |
36 |
where |
|
37 |
f Skip = "" |
|
38 |
f SS = "S" |
|
39 |
f LS = "L" |
|
40 |
f IntP = "i" |
|
41 |
f (Many p) = "" |
|
11048 | 42 |
|
43 |
cmdParams2handlerType (Command _ p) = "handler_" ++ concatMap f p |
|
44 |
where |
|
45 |
f Skip = "_" |
|
46 |
f SS = "S" |
|
47 |
f LS = "L" |
|
48 |
f IntP = "i" |
|
49 |
f (Many p) = 'M' : concatMap f p |
|
11050 | 50 |
|
11047 | 51 |
cmdParams2record cmd@(Command _ p) = renderStyle style{lineLength = 80} $ |
52 |
text "type " <> text (cmdParams2str cmd) |
|
53 |
<> text " = record" $+$ nest 4 ( |
|
54 |
vcat (map (uncurry f) $ zip [1..] $ filter isRendered p) |
|
55 |
$+$ text "end;") |
|
56 |
where |
|
57 |
isRendered Skip = False |
|
58 |
isRendered (Many _) = False |
|
59 |
isRendered _ = True |
|
60 |
f n Skip = empty |
|
61 |
f n SS = text "str" <> int n <> text ": shortstring;" |
|
62 |
f n LS = text "str" <> int n <> text ": longstring;" |
|
63 |
f n IntP = text "param" <> int n <> text ": LongInt;" |
|
64 |
f _ (Many _) = empty |
|
65 |
||
66 |
commandsDescription = [ |
|
10898 | 67 |
cmd "CONNECTED" [Skip, IntP] |
68 |
, cmd1 "NICK" SS |
|
69 |
, cmd1 "PROTO" IntP |
|
70 |
, cmd1 "ASKPASSWORD" SS |
|
71 |
, cmd1 "SERVER_AUTH" SS |
|
10904 | 72 |
, cmd1 "JOINING" SS |
10929 | 73 |
, cmd1 "TEAM_ACCEPTED" SS |
11442 | 74 |
, cmd2 "HH_NUM" SS SS |
75 |
, cmd2 "TEAM_COLOR" SS SS |
|
10904 | 76 |
, cmd1 "BANLIST" $ Many [SS] |
77 |
, cmd1 "JOINED" $ Many [SS] |
|
10898 | 78 |
, cmd1 "LOBBY:JOINED" $ Many [SS] |
10904 | 79 |
, cmd2 "LOBBY:LEFT" SS LS |
80 |
, cmd2 "CLIENT_FLAGS" SS $ Many [SS] |
|
11441 | 81 |
, cmd2 "LEFT" SS LS |
10902 | 82 |
, cmd1 "SERVER_MESSAGE" LS |
11050 | 83 |
, cmd1 "ERROR" LS |
10929 | 84 |
, cmd1 "NOTICE" LS |
85 |
, cmd1 "WARNING" LS |
|
10904 | 86 |
, cmd1 "EM" $ Many [LS] |
87 |
, cmd1 "PING" $ Many [SS] |
|
88 |
, cmd2 "CHAT" SS LS |
|
89 |
, cmd2 "SERVER_VARS" SS LS |
|
90 |
, cmd2 "BYE" SS LS |
|
10908 | 91 |
, cmd1 "INFO" $ Many [SS] |
11425
2947f06e8533
Another approach to parsing two-lines protocol commands
unc0rr
parents:
11419
diff
changeset
|
92 |
, cmd1 "ROOM~ADD" $ Many [SS] |
2947f06e8533
Another approach to parsing two-lines protocol commands
unc0rr
parents:
11419
diff
changeset
|
93 |
, cmd1 "ROOM~UPD" $ Many [SS] |
2947f06e8533
Another approach to parsing two-lines protocol commands
unc0rr
parents:
11419
diff
changeset
|
94 |
, cmd1 "ROOM~DEL" SS |
10929 | 95 |
, cmd1 "ROOMS" $ Many [SS] |
10904 | 96 |
, cmd "KICKED" [] |
10929 | 97 |
, cmd "RUN_GAME" [] |
98 |
, cmd "ROUND_FINISHED" [] |
|
11427 | 99 |
, cmd1 "ADD_TEAM" $ Many [SS] |
100 |
, cmd1 "REMOVE_TEAM" SS |
|
101 |
, cmd1 "CFG~MAP" SS |
|
102 |
, cmd1 "CFG~SEED" SS |
|
11429
d96a37de1076
Apply generated code to .pas files, fix FULLMAPCONFIG handling
unc0rr
parents:
11428
diff
changeset
|
103 |
, cmd1 "CFG~SCHEME" $ Many [SS] |
11427 | 104 |
, cmd1 "CFG~THEME" SS |
105 |
, cmd1 "CFG~TEMPLATE" IntP |
|
106 |
, cmd1 "CFG~MAPGEN" IntP |
|
107 |
, cmd1 "CFG~FEATURE_SIZE" IntP |
|
108 |
, cmd1 "CFG~MAZE_SIZE" IntP |
|
109 |
, cmd1 "CFG~SCRIPT" SS |
|
110 |
, cmd1 "CFG~DRAWNMAP" LS |
|
111 |
, cmd2 "CFG~AMMO" SS LS |
|
11454 | 112 |
, cmd1 "CFG~FULLMAPCONFIG" $ Many [SS] |
10898 | 113 |
] |
114 |
||
11413
ffff8a0d1a76
Implement processing net commands in the main thread
unc0rr
parents:
11076
diff
changeset
|
115 |
hasMany = any isMany |
ffff8a0d1a76
Implement processing net commands in the main thread
unc0rr
parents:
11076
diff
changeset
|
116 |
isMany (Many _) = True |
ffff8a0d1a76
Implement processing net commands in the main thread
unc0rr
parents:
11076
diff
changeset
|
117 |
isMany _ = False |
ffff8a0d1a76
Implement processing net commands in the main thread
unc0rr
parents:
11076
diff
changeset
|
118 |
|
ffff8a0d1a76
Implement processing net commands in the main thread
unc0rr
parents:
11076
diff
changeset
|
119 |
unknown = Command "__UNKNOWN__" [Many [SS]] |
ffff8a0d1a76
Implement processing net commands in the main thread
unc0rr
parents:
11076
diff
changeset
|
120 |
unknowncmd = PTPrefix "$" [PTCommand "$" $ unknown] |
10929 | 121 |
|
11075 | 122 |
fixName = map fixChar |
123 |
fixChar c | isLetter c = c |
|
124 |
| otherwise = '_' |
|
125 |
||
10927 | 126 |
groupByFirstChar :: [ParseTree] -> [(Char, [ParseTree])] |
10904 | 127 |
groupByFirstChar = MM.assocs . MM.fromList . map breakCmd |
11050 | 128 |
where |
129 |
breakCmd (PTCommand (c:cs) params) = (c, PTCommand cs params) |
|
10902 | 130 |
|
10927 | 131 |
makePT cmd@(Command n p) = PTCommand n cmd |
132 |
||
10929 | 133 |
buildParseTree cmds = [PTPrefix "!" $ (bpt $ map makePT cmds) ++ [unknowncmd]] |
11428
cc12bba5b2a2
Support for protocol commands which are equal to suffix of another protocol command
unC0Rr
parents:
11427
diff
changeset
|
134 |
|
cc12bba5b2a2
Support for protocol commands which are equal to suffix of another protocol command
unC0Rr
parents:
11427
diff
changeset
|
135 |
bpt :: [ParseTree] -> [ParseTree] |
cc12bba5b2a2
Support for protocol commands which are equal to suffix of another protocol command
unC0Rr
parents:
11427
diff
changeset
|
136 |
bpt cmds = cmdLeaf emptyNamed |
10904 | 137 |
where |
11428
cc12bba5b2a2
Support for protocol commands which are equal to suffix of another protocol command
unC0Rr
parents:
11427
diff
changeset
|
138 |
emptyNamed = partition (\(_, (PTCommand n _:_)) -> null n) $ groupByFirstChar cmds |
cc12bba5b2a2
Support for protocol commands which are equal to suffix of another protocol command
unC0Rr
parents:
11427
diff
changeset
|
139 |
buildsub :: (Char, [ParseTree]) -> [ParseTree] -> ParseTree |
cc12bba5b2a2
Support for protocol commands which are equal to suffix of another protocol command
unC0Rr
parents:
11427
diff
changeset
|
140 |
buildsub (c, cmds) pc = let st = (bpt cmds) ++ pc in if null $ drop 1 st then maybeMerge c st else PTPrefix [c] st |
cc12bba5b2a2
Support for protocol commands which are equal to suffix of another protocol command
unC0Rr
parents:
11427
diff
changeset
|
141 |
buildsub' = flip buildsub [] |
cc12bba5b2a2
Support for protocol commands which are equal to suffix of another protocol command
unC0Rr
parents:
11427
diff
changeset
|
142 |
cmdLeaf ([], assocs) = map buildsub' assocs |
cc12bba5b2a2
Support for protocol commands which are equal to suffix of another protocol command
unC0Rr
parents:
11427
diff
changeset
|
143 |
cmdLeaf ([(c, hwc:assocs1)], assocs2) |
cc12bba5b2a2
Support for protocol commands which are equal to suffix of another protocol command
unC0Rr
parents:
11427
diff
changeset
|
144 |
| null assocs1 = PTPrefix [c] [hwc] : map buildsub' assocs2 |
cc12bba5b2a2
Support for protocol commands which are equal to suffix of another protocol command
unC0Rr
parents:
11427
diff
changeset
|
145 |
| otherwise = (buildsub (c, assocs1) [hwc]) : map buildsub' assocs2 |
cc12bba5b2a2
Support for protocol commands which are equal to suffix of another protocol command
unC0Rr
parents:
11427
diff
changeset
|
146 |
|
10927 | 147 |
maybeMerge c cmd@[PTCommand {}] = PTPrefix [c] cmd |
10906 | 148 |
maybeMerge c cmd@[PTPrefix s ss] = PTPrefix (c:s) ss |
11419 | 149 |
maybeMerge c [] = PTPrefix [c] [] |
11428
cc12bba5b2a2
Support for protocol commands which are equal to suffix of another protocol command
unC0Rr
parents:
11427
diff
changeset
|
150 |
|
10906 | 151 |
dumpTree = vcat . map dt |
152 |
where |
|
11428
cc12bba5b2a2
Support for protocol commands which are equal to suffix of another protocol command
unC0Rr
parents:
11427
diff
changeset
|
153 |
dt (PTPrefix s st) = text s $$ (nest (length s) $ vcat $ map dt st) |
cc12bba5b2a2
Support for protocol commands which are equal to suffix of another protocol command
unC0Rr
parents:
11427
diff
changeset
|
154 |
dt _ = char '$' |
10904 | 155 |
|
11429
d96a37de1076
Apply generated code to .pas files, fix FULLMAPCONFIG handling
unc0rr
parents:
11428
diff
changeset
|
156 |
renderArrays (letters, commands, handlers) = vcat $ punctuate (char '\n') [grr, l, s, c, bodies, structs, realHandlers, realHandlersArray, cmds] |
10908 | 157 |
where |
11048 | 158 |
maybeQuotes "$" = text "#0" |
11425
2947f06e8533
Another approach to parsing two-lines protocol commands
unc0rr
parents:
11419
diff
changeset
|
159 |
maybeQuotes "~" = text "#10" |
10925 | 160 |
maybeQuotes s = if null $ tail s then quotes $ text s else text s |
161 |
l = text "const letters: array[0.." <> (int $ length letters - 1) <> text "] of char = " |
|
162 |
<> parens (hsep . punctuate comma $ map maybeQuotes letters) <> semi |
|
163 |
s = text "const commands: array[0.." <> (int $ length commands - 1) <> text "] of integer = " |
|
164 |
<> parens (hsep . punctuate comma $ map text commands) <> semi |
|
10929 | 165 |
c = text "const handlers: array[0.." <> (int $ length fixedNames - 1) <> text "] of PHandler = " |
11048 | 166 |
<> parens (hsep . punctuate comma $ map (text . (:) '@') handlerTypes) <> semi |
11413
ffff8a0d1a76
Implement processing net commands in the main thread
unc0rr
parents:
11076
diff
changeset
|
167 |
grr = text "const net2cmd: array[0.." <> (int $ length fixedNames - 1) <> text "] of TCmdType = " |
ffff8a0d1a76
Implement processing net commands in the main thread
unc0rr
parents:
11076
diff
changeset
|
168 |
<> parens (hsep . punctuate comma $ map (text . (++) "cmd_") $ reverse fixedNames) <> semi |
11429
d96a37de1076
Apply generated code to .pas files, fix FULLMAPCONFIG handling
unc0rr
parents:
11428
diff
changeset
|
169 |
handlerTypes = "handler__UNKNOWN_" : (map cmdParams2handlerType $ reverse sortedCmdDescriptions) |
11413
ffff8a0d1a76
Implement processing net commands in the main thread
unc0rr
parents:
11076
diff
changeset
|
170 |
sortedCmdDescriptions = sort commandsDescription |
10929 | 171 |
fixedNames = map fixName handlers |
11413
ffff8a0d1a76
Implement processing net commands in the main thread
unc0rr
parents:
11076
diff
changeset
|
172 |
bodies = vcat $ punctuate (char '\n') $ map handlerBody $ nub $ sort handlerTypes |
ffff8a0d1a76
Implement processing net commands in the main thread
unc0rr
parents:
11076
diff
changeset
|
173 |
handlerBody n = text "procedure " <> text n <> semi |
11427 | 174 |
$+$ text "begin" |
10929 | 175 |
$+$ text "end" <> semi |
11413
ffff8a0d1a76
Implement processing net commands in the main thread
unc0rr
parents:
11076
diff
changeset
|
176 |
cmds = text "type TCmdType = " <> parens (hsep $ punctuate comma $ concatMap (rhentry "cmd_") $ sortedCmdDescriptions) <> semi |
11047 | 177 |
structs = vcat (map text . Set.toList . Set.fromList $ map cmdParams2record commandsDescription) |
11413
ffff8a0d1a76
Implement processing net commands in the main thread
unc0rr
parents:
11076
diff
changeset
|
178 |
realHandlers = vcat $ punctuate (char '\n') $ map rh $ sortedCmdDescriptions |
11076 | 179 |
realHandlersArray = text "const handlers: array[TCmdType] of PHandler = " |
11427 | 180 |
<> parens (hsep . punctuate comma . concatMap (map ((<>) (text "PHandler") . parens) . rhentry "@handler_") $ sortedCmdDescriptions) <> semi |
11075 | 181 |
|
182 |
rh cmd@(Command n p) = text "procedure handler_" <> text (fixName n) <> parens (text "var p: " <> text (cmdParams2str cmd)) <> semi |
|
11413
ffff8a0d1a76
Implement processing net commands in the main thread
unc0rr
parents:
11076
diff
changeset
|
183 |
$+$ emptyBody $+$ if hasMany p then vcat [space, text "procedure handler_" <> text (fixName n) <> text "_s" <> parens (text "var s: TCmdParamS") <> semi |
11075 | 184 |
, emptyBody] else empty |
185 |
where |
|
186 |
emptyBody = text "begin" $+$ text "end" <> semi |
|
10908 | 187 |
|
11427 | 188 |
rhentry prefix cmd@(Command n p) = (text . (++) prefix . fixName . cmdName $ cmd) |
11413
ffff8a0d1a76
Implement processing net commands in the main thread
unc0rr
parents:
11076
diff
changeset
|
189 |
: if hasMany p then [text . flip (++) "_s" . (++) prefix . fixName . cmdName $ cmd] else [] |
ffff8a0d1a76
Implement processing net commands in the main thread
unc0rr
parents:
11076
diff
changeset
|
190 |
|
11047 | 191 |
pas = renderArrays $ buildTables $ buildParseTree commandsDescription |
10925 | 192 |
where |
10927 | 193 |
buildTables cmds = let (_, _, _, t1, t2, t3) = foldr walk (0, [0], -10, [], [], [[]]) cmds in (tail t1, tail t2, concat t3) |
194 |
walk (PTCommand _ (Command n params)) (lc, s:sh, pc, tbl1, tbl2, (t3:tbl3)) = |
|
10931 | 195 |
(lc, 1:sh, pc - 1, "#10":tbl1, show pc:tbl2, (n:t3):tbl3) |
10925 | 196 |
walk (PTPrefix prefix cmds) l = lvldown $ foldr fpf (foldr walk (lvlup l) cmds) prefix |
10927 | 197 |
lvlup (lc, sh, pc, tbl1, tbl2, tbl3) = (lc, 0:sh, pc, tbl1, tbl2, []:tbl3) |
198 |
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) |
|
199 |
fpf c (lc, s:sh, pc, tbl1, tbl2, tbl3) = (lc + 1, s+1:sh, pc, [c]:tbl1, "0":tbl2, tbl3) |
|
10925 | 200 |
|
11428
cc12bba5b2a2
Support for protocol commands which are equal to suffix of another protocol command
unC0Rr
parents:
11427
diff
changeset
|
201 |
main = do |
cc12bba5b2a2
Support for protocol commands which are equal to suffix of another protocol command
unC0Rr
parents:
11427
diff
changeset
|
202 |
putStrLn $ renderStyle style{mode = ZigZagMode, lineLength = 80} $ pas |
11050 | 203 |
--putStrLn $ renderStyle style{lineLength = 80} $ dumpTree $ buildParseTree commandsDescription |