Show.fs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. module Qsp.Show
  2. open FsharpMyExtension
  3. open FsharpMyExtension.ShowList
  4. open Qsp.Ast
  5. type FormatConfig =
  6. {
  7. IsSplitStringPl: bool
  8. TrimWhitespaceWhenSplit: bool
  9. }
  10. static member Default =
  11. {
  12. IsSplitStringPl = false
  13. TrimWhitespaceWhenSplit = false
  14. }
  15. let showVarType = function
  16. | StringType -> showChar '$'
  17. | NumericType -> empty
  18. let showVar (typ:VarType, varName:string) =
  19. showVarType typ << showString varName
  20. let rec showStringLines showExpr showStmtsInline (lines:list<Line>) =
  21. lines
  22. |> List.map (
  23. List.collect (
  24. function
  25. | StringKind x ->
  26. showString (x.Replace("'", "''"))
  27. |> List.singleton
  28. | ExprKind x ->
  29. showExpr x
  30. |> show
  31. |> fun x -> x.Replace("'", "''") // TODO: стоит ли говорить, что все эти былины с `.Replace("'", "''")` нужно превратить в нормальный код?
  32. |> showString
  33. |> bet "<<" ">>"
  34. |> List.singleton
  35. | HyperLinkKind(x, body) ->
  36. let attValue =
  37. match x with
  38. | Raw x ->
  39. x.Replace("'", "''")
  40. |> showString
  41. | StaticStmts(x) ->
  42. showStmtsInline x
  43. |> show
  44. |> fun x -> x.Replace("'", "''")
  45. |> showString
  46. let header =
  47. showString "<a href=\"exec: "
  48. << attValue
  49. << showString "\">"
  50. match showStringLines showExpr showStmtsInline body with
  51. | [] ->
  52. header
  53. << showString "</a>"
  54. |> List.singleton
  55. | [x] ->
  56. header
  57. << x
  58. << showString "</a>"
  59. |> List.singleton
  60. | xs ->
  61. xs
  62. |> List.mapStartMidEnd
  63. (fun x -> header << x)
  64. id
  65. (fun x -> x << showString "</a>")
  66. |> fun x -> x // TODO: и все строки позже соединятся воедино, даже пробелов не удостоятся, ага.
  67. ) >> joinsEmpty empty
  68. )
  69. let showValue showExpr showStmtsInline = function
  70. | Int x -> shows x
  71. | String lines ->
  72. showStringLines showExpr showStmtsInline lines
  73. |> joinsEmpty (showString "\n")
  74. |> bet "'" "'"
  75. let ops = Op.toString >> showString
  76. let unar = function No -> "no" | Obj -> "obj" | Neg -> "-" | Loc -> "loc"
  77. let showFuncName = function
  78. | PredefUndef.Predef name ->
  79. match Map.tryFind name Qsp.Defines.functionBySymbolic with
  80. | Some x ->
  81. let _, returnedType = x.Signature
  82. let returnedType =
  83. match returnedType with
  84. | Defines.Numeric -> id
  85. | Defines.String -> showChar '$'
  86. | Defines.Any -> id // TODO: defines by argument type
  87. let nameStr = (string name).ToLower()
  88. returnedType << showString nameStr
  89. | None -> failwithf "%A not found in `functionBySymbolic`" name
  90. | PredefUndef.Undef name ->
  91. showString name
  92. let rec simpleShowExpr showStmtsInline expr : ShowS =
  93. let rec f = function
  94. | Val v -> showValue (simpleShowExpr showStmtsInline) showStmtsInline v
  95. | Var v -> showVar v
  96. | Func(name, args) ->
  97. let args =
  98. if List.isEmpty args then
  99. empty
  100. else
  101. showParen true (List.map f args |> join ", ")
  102. showFuncName name << args
  103. | UnarExpr(op, e) ->
  104. let space = function Obj | No | Loc -> showSpace | Neg -> id
  105. let x =
  106. match e with
  107. | Expr(_, _, _) ->
  108. showParen true (f e)
  109. | Arr(_, _) // `-(arr[idx])` лучше выглядит, чем `-arr[idx]`?
  110. | Func(_, _) // `-(func(idx))` лучше выглядит, чем `-(arr(idx))`?
  111. | UnarExpr _
  112. | Val _
  113. | Var _ ->
  114. space op << f e
  115. showString (unar op) << x
  116. | Expr(op, e1, e2) ->
  117. let f body =
  118. match body with
  119. | Val(_)
  120. | Var _ -> f body
  121. | UnarExpr(_, _)
  122. | Expr(_, _, _) ->
  123. showParen true (f body)
  124. | Func(_, _)
  125. | Arr(_, _) ->
  126. f body
  127. f e1 << showSpace
  128. << ops op << showSpace
  129. << f e2
  130. | Arr(var, es) ->
  131. showVar var << bet "[" "]" (List.map f es |> join ", ")
  132. f expr
  133. let rec showExpr showStmtsInline = function
  134. | Val v -> showValue (showExpr showStmtsInline) showStmtsInline v
  135. | Var v -> showVar v
  136. | Func(name, args) ->
  137. let args =
  138. if List.isEmpty args then
  139. empty
  140. else
  141. showParen true
  142. (List.map (showExpr showStmtsInline) args |> join ", ")
  143. showFuncName name << args
  144. | UnarExpr(op, e) ->
  145. let space = function Obj | No | Loc -> showSpace | Neg -> id
  146. showString (unar op) << space op << showExpr showStmtsInline e
  147. | Expr(op, e1, e2) ->
  148. let prec = Precedences.OpB >> Precedences.prec
  149. let f = function
  150. | Expr(op', _, _) -> showParen (prec op > prec op')
  151. | UnarExpr _ -> showParen true | _ -> id
  152. let f x = f x (showExpr showStmtsInline x)
  153. f e1 << showSpace << ops op << showSpace << f e2
  154. | Arr(var, es) -> showVar var << bet "[" "]" (List.map (showExpr showStmtsInline) es |> join ", ")
  155. let showAssign showStmtsInline = function
  156. | AssignWhat.AssignArr(var, key) -> showVar var << bet "[" "]" (showExpr showStmtsInline key)
  157. | AssignWhat.AssignVar var -> showVar var
  158. | AssignWhat.AssignArrAppend var -> showVar var << showString "[]"
  159. let (|OneStmt|_|) = function
  160. | [pos, x] ->
  161. match x with
  162. // | StarPl(Val (String _)) -> None
  163. | Proc(name, _) when name.ToLower() = "*pl" -> None // Как правило, строки очень длинные, потому пусть лучше будет так
  164. | Assign _ | Proc _ | Comment _ -> Some (pos, x)
  165. | AssignCode _ -> None // спорно
  166. | Act _ | If _ -> None
  167. | Label _ -> None // эту нечисть нужно как можно более нагляднее подчеркнуть. Да странно будет, если она окажется одна в списке инструкций.
  168. | Exit -> None // ¯\_(ツ)_/¯
  169. | _ -> None
  170. let (|AssingName|) = function AssignArr(x, _) -> x | AssignVar x -> x | AssignArrAppend x -> x
  171. type IndentsOption =
  172. | UsingSpaces of int
  173. | UsingTabs
  174. let spaceBetween (s:ShowS) : ShowS =
  175. showSpace << s << showSpace
  176. let showStmt indentsOption (formatConfig:FormatConfig) =
  177. let tabs =
  178. match indentsOption with
  179. | UsingTabs ->
  180. showChar '\t'
  181. | UsingSpaces spacesCount ->
  182. replicate spacesCount ' '
  183. let rec f' (pos, stmt) =
  184. let showStmtsInline xs : ShowS =
  185. List.collect f' xs // TODO
  186. |> join "&"
  187. let showAssign = showAssign showStmtsInline
  188. let showExpr = showExpr showStmtsInline
  189. let showStringLines = showStringLines showExpr showStmtsInline
  190. match stmt with
  191. | Proc(name, [e]) when name.ToLower() = "*pl" ->
  192. if formatConfig.IsSplitStringPl then
  193. match e with
  194. | Val(String str) ->
  195. let str =
  196. if formatConfig.TrimWhitespaceWhenSplit then
  197. str
  198. |> List.map (
  199. List.map (function
  200. | StringKind x -> StringKind (x.Trim())
  201. | x -> x)
  202. )
  203. else
  204. str
  205. showStringLines str
  206. |> List.map (bet "'" "'")
  207. | _ ->
  208. [ showExpr e ]
  209. else
  210. [ showExpr e ]
  211. | Proc(name, e) ->
  212. let args =
  213. if List.isEmpty e then
  214. empty
  215. else
  216. showSpace << (List.map showExpr e |> join ", ")
  217. [ showString name << args ]
  218. | Label s -> [showChar ':' << showString s]
  219. | If(e, thenBody, elseBody) ->
  220. let ifHeader e = showString "if" << showSpace << showExpr e << showChar ':'
  221. [
  222. match thenBody, elseBody with
  223. | OneStmt x, OneStmt y ->
  224. yield ifHeader e
  225. << showSpace << showStmtsInline [x]
  226. << spaceBetween (showString "else")
  227. << showStmtsInline [y]
  228. | OneStmt x, [] ->
  229. yield ifHeader e
  230. << showSpace << showStmtsInline [x]
  231. | _ ->
  232. let rec body : _ -> ShowS list = function
  233. | [pos, If(e, thenBody, elseBody)] ->
  234. [
  235. yield showString "elseif" << showSpace << showExpr e << showChar ':'
  236. yield! thenBody
  237. |> List.collect
  238. (f' >> List.map ((<<) tabs))
  239. yield! body elseBody
  240. ]
  241. | [] -> []
  242. | xs ->
  243. [
  244. yield showString "else"
  245. yield!
  246. xs
  247. |> List.collect
  248. (f' >> List.map ((<<) tabs))
  249. ]
  250. yield ifHeader e
  251. yield! thenBody
  252. |> List.collect
  253. (f' >> List.map ((<<) tabs))
  254. yield! body elseBody
  255. yield showString "end"
  256. ]
  257. | Act(es, body) ->
  258. let header = showString "act" << showSpace << join ", " (List.map showExpr es) << showChar ':'
  259. [
  260. match body with
  261. | OneStmt x ->
  262. yield header << showSpace << showStmtsInline [x]
  263. | _ ->
  264. yield header
  265. yield!
  266. body
  267. |> List.collect
  268. (f' >> List.map ((<<) tabs))
  269. yield showString "end"
  270. ]
  271. | Comment s -> [showChar '!' << showString s]
  272. | AssignCode(ass, stmts) ->
  273. let header = showAssign ass << spaceBetween (showChar '=') << showChar '{'
  274. [
  275. if List.isEmpty stmts then
  276. yield header << showChar '}'
  277. else
  278. yield header
  279. yield!
  280. stmts
  281. |> List.collect
  282. (f' >> List.map ((<<) tabs))
  283. yield showChar '}'
  284. ]
  285. | Exit -> [showString "exit"]
  286. f'
  287. let showLoc indentsOption isSplitStringPl (Location(name, statements)) : ShowS list =
  288. [
  289. yield showString "# " << showString name
  290. yield! List.collect (showStmt indentsOption isSplitStringPl) statements
  291. yield showString (sprintf "--- %s ----------" name)
  292. ]
  293. let printLocs indentsOption isSplitStringPl xs =
  294. List.map (lines << showLoc indentsOption isSplitStringPl) xs
  295. |> joinEmpty "\n\n"
  296. |> show