1
0

ParserGeneric.fs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419
  1. module Qsp.Parser.Generic
  2. open FParsec
  3. open FsharpMyExtension
  4. open FsharpMyExtension.Either
  5. open Qsp
  6. let fparsecPosToPos (pos:FParsec.Position) =
  7. Ast.positionCreate pos.StreamName pos.Index pos.Line pos.Column
  8. let runEither p str =
  9. match run p str with
  10. | Success(x, _, _) -> Right x
  11. | Failure(x, _, _) -> Left x
  12. let runStateEither p st str =
  13. match runParserOnString p st "" str with
  14. | Success(x, st, _) -> st, Right(x)
  15. | Failure(x, _, st) -> st, Left(x)
  16. let isIdentifierChar c = isLetter c || isDigit c || c = '_' || c = '.' || c = '$'
  17. let ident<'UserState> =
  18. skipChar '_' >>? many1Satisfy isIdentifierChar
  19. |>> fun ident -> "_" + ident
  20. <|> many1Satisfy2L (fun c -> isLetter c || c = '#') isIdentifierChar "identifier"
  21. : Parser<_, 'UserState>
  22. let ws<'UserState> =
  23. skipManySatisfy (fun c -> System.Char.IsWhiteSpace c && c <> '\n')
  24. : Parser<_, 'UserState>
  25. let ws1<'UserState> =
  26. skipMany1SatisfyL (fun c -> System.Char.IsWhiteSpace c && c <> '\n') "any white space except '\\n'"
  27. : Parser<_, 'UserState>
  28. let char_ws c = pchar c .>> ws
  29. let bet opened closed = between <| char_ws opened <| pchar closed
  30. let bet_ws opened closed p = bet opened closed p .>> ws
  31. let optList p = p <|>% []
  32. let nl<'UserState> = skipMany1 newline : Parser<unit, 'UserState>
  33. let stringLiteral<'UserState> =
  34. let normalChar c = satisfy (fun c' -> c' <> c)
  35. let p c = manyChars (normalChar c <|> attempt(pchar c >>. pchar c))
  36. let bet openedChar closedChar = between (pchar openedChar) (pchar closedChar)
  37. bet '"' '"' (p '"')
  38. <|> bet '\'' '\'' (p '\'')
  39. <|> bet '{' '}' (p '}') // TODO: забавно: проверил компилятор, и тот напрочь не воспринимает экранирование `}}`
  40. : Parser<_, 'UserState>
  41. /// Дело в том, что названия переменных могут начинаться с ключевых слов ("**if**SomethingTrue", например), а значит, чтобы это пресечь, можно воспользоваться именно этой функцией так:
  42. /// ```fsharp
  43. /// pstring "if" .>>? notFollowedVar
  44. /// ```
  45. let notFollowedVarCont<'UserState> =
  46. notFollowedBy (satisfy isIdentifierChar)
  47. : Parser<_, 'UserState>
  48. /// A document highlight kind.
  49. [<RequireQualifiedAccess>]
  50. type DocumentHighlightKind =
  51. /// A textual occurrence.
  52. | Text = 1
  53. /// Read-access of a symbol, like reading a variable.
  54. | Read = 2
  55. /// Write-access of a symbol, like writing to a variable.
  56. | Write = 3
  57. type VarHighlightKind =
  58. | ReadAccess
  59. | WriteAccess
  60. type VarHighlights =
  61. {
  62. VarScopeSystem: Scope.ScopeSystem<Ast.VarName, Tokens.InlineRange * VarHighlightKind>
  63. Ranges: (Tokens.InlineRange * Scope.VarId) list
  64. }
  65. let varHighlightsEmpty =
  66. {
  67. VarScopeSystem = Scope.scopeSystemEmpty
  68. Ranges = []
  69. }
  70. type LocHighlights =
  71. {
  72. Ma: Map<Ast.LocationName, (Tokens.InlineRange * VarHighlightKind) list>
  73. Ranges: (Tokens.InlineRange * Ast.LocationName) list
  74. }
  75. let locHighlightsEmpty =
  76. {
  77. Ma = Map.empty
  78. Ranges = []
  79. }
  80. type Highlights =
  81. {
  82. VarHighlights: VarHighlights
  83. LocHighlights: LocHighlights
  84. }
  85. let highlightsEmpty =
  86. {
  87. VarHighlights = varHighlightsEmpty
  88. LocHighlights = locHighlightsEmpty
  89. }
  90. type HoverDescription =
  91. | FuncDescription of Defines.PredefFunc
  92. // | VarDescription of Defines.
  93. | RawDescription of string
  94. type 'a Parser = Parser<'a, State>
  95. and State =
  96. {
  97. Tokens: Tokens.Token list
  98. /// Здесь ошибки только те, что могут определиться во время поверхностного семантического разбора, то есть это то, что не нуждается в нескольких проходах. Например, можно определить, что в коде пытаются переопределить встроенную функцию, и это будет ошибкой.
  99. ///
  100. /// А если хочется понять, что инструкция `gt 'some loc'` верна, то придется пройтись дважды, чтобы определить, существует ли вообще `'some loc'`. Если бы локации определялись последовательно, то есть нельзя было бы обратиться к той, что — ниже, тогда потребовался только один проход. Но в таком случае придется вводить что-то вроде `rec`, чтобы перейти на локацию, определенную ниже. Но всё это возвращает к той же задаче, потому ну его.
  101. SemanticErrors: (Tokens.InlineRange * string) list
  102. /// Информация обо всём и вся
  103. Hovers: (Tokens.InlineRange * HoverDescription) list
  104. Highlights: Highlights
  105. /// Нужен для `elseif` конструкции. Эх, если бы ее можно было как-то именно там оставить, но увы.
  106. IsEndOptional : bool
  107. LastSymbolPos : FParsec.Position
  108. /// Локации, которые неопределенны именно в этом документе, но переходы к ним есть
  109. NotDefinedLocs: Map<Ast.LocationName, Tokens.InlineRange list>
  110. // Я тут, это самое, оставлю. Никто не возражает?
  111. PStmts: Parser<Ast.PosStatement list>
  112. /// `&lt;a gt ''x''>`
  113. SingleQuotNestedCount: int
  114. DoubleQuotNestedCount: int
  115. HtmlAttDoubleNested: int
  116. }
  117. let emptyState =
  118. {
  119. Tokens = []
  120. SemanticErrors = []
  121. Hovers = []
  122. IsEndOptional = false
  123. LastSymbolPos = FParsec.Position("", 0L, 1L, 1L)
  124. Highlights = highlightsEmpty
  125. NotDefinedLocs = Map.empty
  126. PStmts = FParsec.Primitives.failFatally "PStmts not implemented"
  127. SingleQuotNestedCount = 0
  128. DoubleQuotNestedCount = 0
  129. HtmlAttDoubleNested = 0
  130. }
  131. let updateScope fn =
  132. updateUserState (fun x ->
  133. let ss = x.Highlights.VarHighlights.VarScopeSystem
  134. { x with
  135. Highlights =
  136. { x.Highlights with
  137. VarHighlights =
  138. { x.Highlights.VarHighlights with
  139. VarScopeSystem = fn ss
  140. }
  141. }
  142. })
  143. let pGetDefLocPos locName =
  144. getUserState
  145. |>> fun st ->
  146. match Map.tryFind locName st.Highlights.LocHighlights.Ma with
  147. | None ->
  148. None
  149. | Some(value) ->
  150. value
  151. |> List.tryPick (fun (r, kind) ->
  152. match kind with
  153. | WriteAccess -> Some r
  154. | _ -> None
  155. )
  156. let appendVarHighlight (r:Tokens.InlineRange) (var:Ast.Var) highlightKind isLocal =
  157. let var = mapSnd String.toLower var // for case-insensitively
  158. updateUserState (fun st ->
  159. { st with
  160. Highlights =
  161. {
  162. st.Highlights with
  163. VarHighlights =
  164. let varHighlights = st.Highlights.VarHighlights
  165. if not <| isLocal then
  166. let v = r, highlightKind
  167. let varId, ss = Scope.addAsRead (snd var, (fun xs -> v::xs)) varHighlights.VarScopeSystem
  168. {
  169. Ranges = (r, varId)::st.Highlights.VarHighlights.Ranges
  170. VarScopeSystem = ss
  171. }
  172. else
  173. let v = r, highlightKind
  174. let varId, ss = Scope.addAsWrite (snd var, fun xs -> v::xs) varHighlights.VarScopeSystem
  175. {
  176. Ranges = (r, varId)::st.Highlights.VarHighlights.Ranges
  177. VarScopeSystem = ss
  178. }
  179. }
  180. }
  181. )
  182. let appendLocHighlight (r:Tokens.InlineRange) (loc:string) highlightKind =
  183. let loc = String.toLower loc // без шуток, они тоже case-insensitively, хотя и представляют из себя string
  184. updateUserState (fun st ->
  185. { st with
  186. Highlights =
  187. {
  188. st.Highlights with
  189. LocHighlights =
  190. {
  191. Ranges = (r, loc)::st.Highlights.LocHighlights.Ranges
  192. Ma =
  193. let v = r, highlightKind
  194. st.Highlights.LocHighlights.Ma
  195. |> Map.addOrMod loc [v] (fun xs -> v::xs)
  196. }
  197. }
  198. }
  199. )
  200. let appendToken2 tokenType r =
  201. updateUserState (fun st ->
  202. let token =
  203. { Tokens.TokenType = tokenType
  204. Tokens.Range = r }
  205. { st with Tokens = token :: st.Tokens }
  206. )
  207. let toRange (p1:FParsec.Position) (p2:FParsec.Position) =
  208. {
  209. Tokens.InlineRange.Line = p1.Line // Должно выполняться условие `p1.Line = p2.Line`
  210. Tokens.InlineRange.Column1 = p1.Column
  211. Tokens.InlineRange.Column2 = p2.Column // Должно выполняться условие `p2.Column > p1.Column`
  212. }
  213. let appendToken tokenType p =
  214. getPosition .>>.? p .>>. getPosition
  215. >>= fun ((p1, p), p2) ->
  216. let r = toRange p1 p2
  217. appendToken2 tokenType r
  218. >>. preturn p
  219. let applyRange p =
  220. getPosition .>>.? p .>>. getPosition
  221. >>= fun ((p1, p), p2) ->
  222. let range = toRange p1 p2
  223. preturn (range, p)
  224. let appendHover2 msg range =
  225. updateUserState (fun st ->
  226. { st with Hovers = (range, msg) :: st.Hovers }
  227. )
  228. let appendSemanticError range msg =
  229. updateUserState (fun st ->
  230. { st with SemanticErrors =
  231. (range, msg) :: st.SemanticErrors })
  232. let appendHover msg p =
  233. (getPosition .>>.? p .>>. getPosition)
  234. >>= fun ((p1, p), p2) ->
  235. let r = toRange p1 p2
  236. appendHover2 msg r
  237. >>. preturn p
  238. let appendTokenHover tokenType msg p =
  239. (getPosition .>>.? p .>>. getPosition)
  240. >>= fun ((p1, p), p2) ->
  241. let r = toRange p1 p2
  242. appendToken2 tokenType r
  243. >>. appendHover2 msg r
  244. >>. preturn p
  245. let pSingleNested =
  246. updateUserState (fun st ->
  247. { st with
  248. SingleQuotNestedCount = st.SingleQuotNestedCount + 1
  249. })
  250. let pSingleUnnested =
  251. updateUserState (fun st ->
  252. { st with
  253. SingleQuotNestedCount = st.SingleQuotNestedCount - 1
  254. })
  255. let pGetSingleNested =
  256. getUserState |>> fun x -> x.SingleQuotNestedCount
  257. let pDoubleNested =
  258. updateUserState (fun st ->
  259. { st with
  260. DoubleQuotNestedCount = st.DoubleQuotNestedCount + 1
  261. })
  262. let pDoubleUnnested =
  263. updateUserState (fun st ->
  264. { st with
  265. DoubleQuotNestedCount = st.DoubleQuotNestedCount - 1
  266. })
  267. let pGetDoubleNested =
  268. getUserState |>> fun x -> x.DoubleQuotNestedCount
  269. let pHtmlAttDoubleNested =
  270. updateUserState (fun st ->
  271. { st with
  272. HtmlAttDoubleNested = st.HtmlAttDoubleNested + 1
  273. })
  274. let pHtmlAttDoubleUnnested =
  275. updateUserState (fun st ->
  276. { st with
  277. HtmlAttDoubleNested = st.HtmlAttDoubleNested - 1
  278. })
  279. let pGetHtmlAttDoubleNested =
  280. getUserState |>> fun x -> x.HtmlAttDoubleNested
  281. open Tokens
  282. let charsReplicate n (c:char) =
  283. System.String.Concat (Array.replicate n c)
  284. // Это такой фокус, чтобы напрочь во всем запутаться. А кто говорил, что это чисто функциональное программирование? Ну-ну.
  285. let pstmts : _ Parser =
  286. getUserState >>= fun st -> st.PStmts
  287. let stringLiteralWithToken pexpr : _ Parser =
  288. let bet tokenType openedChar closedChar pnested punnested pgetNested =
  289. let p nestedCount =
  290. many1Satisfy (fun c' -> not (c' = closedChar || c' = '\n' || c' = '<'))
  291. <|> (pstring (charsReplicate (pown 2 nestedCount) closedChar) // 1 2, 2 4
  292. >>% string closedChar)
  293. <|> (skipChar '<' >>? notFollowedBy (skipChar '<' <|> skipChar 'a' <|> skipString "/a>") >>% "<")
  294. let pattValBody nestedCount closedCharAtt =
  295. many1Satisfy (fun c' -> not (c' = closedChar || c' = '\n' || c' = '&' || c' = closedCharAtt))
  296. <|> (pstring (charsReplicate (pown 2 nestedCount) closedChar)
  297. >>% string closedChar)
  298. <|> (pchar '&'
  299. >>. ((pstring "quot" >>% "\"" <|> pstring "apos" >>% "'") .>> pchar ';'
  300. <|>% "&") )
  301. // <|> (skipChar '<' >>? notFollowedBy (skipChar '<' <|> skipChar 'a' <|> skipString "/a>") >>% "<")
  302. let plineKind nestedCount =
  303. let plineKind, plineKindRef = createParserForwardedToRef()
  304. let plineKinds =
  305. pipe2
  306. (many plineKind)
  307. (many
  308. (newline >>. many plineKind))
  309. (fun x xs -> x::xs)
  310. let pATag =
  311. // А вот здесь вообще начинается прелюбопытная штука:
  312. // 1. Все `"` экранируются в `&quot;`, а сам `&` — в `&amp;`
  313. // 2. Если нужно еще вложить, то используется `&quot;&quot;`
  314. pstring "<a href=\"exec:"
  315. >>. (attempt // TODO: Если в значении аттрибута нету подстановки, тогда нужно пытататься разобрать его статически. К черту производительность, главное, понятность
  316. (pHtmlAttDoubleNested
  317. >>. spaces >>. notEmpty pstmts
  318. .>> pHtmlAttDoubleUnnested
  319. |>> Ast.StaticStmts)
  320. <|> (appendToken tokenType (many1Strings (pattValBody nestedCount '"')) // TODO: здесь можно и нужно отобразить подстановки.
  321. |>> Ast.Raw))
  322. .>> pchar '"' .>> spaces .>> pchar '>' // что ж, не всё так просто. Дело в том, что во вложенном `pstmts` все `stringLiteral` заместо привычных `"` и `'` использует либо `&quot;` и `''`, либо `&apos;`. Да еще и `&` экранирует в `&amp;`. И всё это кучу раз вкладывается и перевкладывается. Честно сказать, голова пухнет от всех этих страстей. А еще на `if` жаловался, ну-ну.
  323. .>>. plineKinds .>> pstring "</a>" // вот надо были тебе эти дурацкие вложения? Еще скажи, что хотел полноценный HTML-parser сделать. Ой, точно, хочет! Ха-ха.
  324. |>> fun (stmts, line) -> Ast.HyperLinkKind(stmts, line) // Вот смотрю я на эти былины и диву даюсь, право слово. Это ж надо было до такого додуматься. Метаметамета...программирование какое-то
  325. plineKindRef :=
  326. appendToken tokenType (many1Strings (p nestedCount)) |>> Ast.StringKind
  327. <|> (appendToken TokenType.InterpolationBegin (pstring "<<")
  328. >>. (ws >>. pexpr |>> Ast.ExprKind) // это может *немного* запутать, но, эм, но есть какое-то "но", да... Никакого "но" нету — код безнадежно запутанный 😭. Так, здесь экранизация — внутри экранизации, поэтому порождает в два раза больше открывающих скобок. Я сделал всего два уровня и наивно надеюсь, что этого хватит. То есть сейчас он обрабатывает вот эту зверюгу: `'<<''<<''''x''''>>''>>'`. Страшно, правда? Но что-то мне подсказывает, что это так не работает. Проверил, работает, что еще больше ужасает. И `'<<''<<''''<<''''''''это чудовище''''''''>>''''>>''>>'` работает...
  329. .>> ws .>> appendToken TokenType.InterpolationEnd (pstring ">>"))
  330. <|> attempt pATag // TODO: тут бы предупреждение какое-нибудь не помешало: мол, не осилил
  331. plineKind <|> (pchar '<' >>% Ast.StringKind "<")
  332. pgetNested >>=? fun nestedCount ->
  333. let pOpened = pstring (charsReplicate (pown 2 nestedCount) openedChar)
  334. let pClosed = pstring (charsReplicate (pown 2 nestedCount) closedChar)
  335. let plineKind = plineKind (nestedCount + 1)
  336. appendToken tokenType (pOpened .>> pnested)
  337. >>. pipe2
  338. (many plineKind)
  339. (many
  340. (newline >>. many plineKind)
  341. .>> punnested
  342. .>> appendToken tokenType pClosed) // TODO: Здесь самое то использовать `PunctuationDefinitionStringEnd`
  343. (fun x xs -> (x:Ast.Line)::xs)
  344. bet TokenType.StringQuotedSingle '\'' '\'' pSingleNested pSingleUnnested pGetSingleNested
  345. <|> (pGetHtmlAttDoubleNested >>=? fun x ->
  346. if x > 0 then
  347. fail "not implemented HtmlAttDoubleNested"
  348. else
  349. bet TokenType.StringQuotedDouble '"' '"' pDoubleNested pDoubleUnnested pGetDoubleNested)
  350. let pbraces tokenType : _ Parser =
  351. let pbraces, pbracesRef = createParserForwardedToRef()
  352. let p = many1Satisfy (isNoneOf "{}\n")
  353. pbracesRef :=
  354. pipe2
  355. (appendToken tokenType
  356. (many1Satisfy2 ((=) '{') (isNoneOf "{}\n")) )
  357. (many
  358. (appendToken tokenType (many1Strings p)
  359. <|> newlineReturn "\n"
  360. <|> pbraces
  361. )
  362. .>>. appendToken tokenType (pchar '}'))
  363. (fun x (xs, closedChar) ->
  364. seq {
  365. yield x
  366. yield! xs
  367. yield string closedChar
  368. }
  369. |> System.String.Concat
  370. )
  371. pipe2
  372. (appendToken tokenType
  373. (pchar '{' >>. manyStrings p)
  374. .>>. opt (newlineReturn "\n"))
  375. (many
  376. (appendToken tokenType (many1Strings p)
  377. <|> newlineReturn "\n"
  378. <|> pbraces
  379. )
  380. .>> appendToken tokenType (pchar '}')) // TODO: Здесь самое то использовать `PunctuationDefinitionStringEnd`
  381. (fun (x, nl) xs ->
  382. match nl with
  383. | None ->
  384. x::xs |> System.String.Concat
  385. | Some nl ->
  386. x::nl::xs |> System.String.Concat)