Parsec.fs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455
  1. module Qsp.Parser.Main
  2. open FParsec
  3. open FsharpMyExtension
  4. open FsharpMyExtension.Either
  5. open Qsp
  6. open Qsp.Ast
  7. open Qsp.Parser.Generic
  8. open Qsp.Parser.Expr
  9. open Qsp.Tokens
  10. let ppunctuationTerminator : _ Parser =
  11. appendToken TokenType.KeywordControl (pchar '&')
  12. let pImplicitVarWhenAssign p =
  13. applyRange p
  14. >>= fun (range, (name:string)) ->
  15. let nameLower = name.ToLower()
  16. Defines.vars
  17. |> Map.tryFind nameLower
  18. |> function
  19. | Some dscr ->
  20. appendHover2 dscr range
  21. | None ->
  22. if Map.containsKey nameLower Defines.procs then
  23. appendSemanticError range "Нельзя переопределять процедуру"
  24. elif Map.containsKey nameLower Defines.functions then
  25. appendSemanticError range "Нельзя переопределять функцию"
  26. else
  27. let dscr = "Пользовательская глобальная переменная числового типа"
  28. appendHover2 dscr range
  29. >>. appendToken2 TokenType.Variable range
  30. >>. appendVarHighlight range (ImplicitNumericType, name) VarHighlightKind.WriteAccess
  31. >>. preturn name
  32. let pAssign stmts =
  33. let assdef name ass =
  34. let asscode =
  35. between (pchar '{' >>. spaces) (spaces >>. char_ws '}') stmts
  36. |>> fun stmts -> AssignCode(ass, stmts)
  37. let call =
  38. ident >>=?
  39. fun name ->
  40. followedBy (
  41. ident
  42. <|> (puint32 >>% "")
  43. <|> stringLiteral)
  44. >>. sepBy1 pexpr (char_ws ',')
  45. |>> fun args -> Assign(ass, Func(name, args)) // То есть `a = min x, y` можно, что ли? Хм...
  46. let assexpr = call <|> (pexpr |>> fun defExpr -> Assign(ass, defExpr))
  47. let str_ws s =
  48. appendToken TokenType.OperatorAssignment
  49. (pstring s)
  50. .>> ws
  51. choice [
  52. str_ws "-=" >>. pexpr |>> fun defExpr -> Assign(ass, Expr.Expr(Minus, Var name, defExpr))
  53. str_ws "=-" >>. pexpr |>> fun defExpr -> Assign(ass, Expr.Expr(Minus, defExpr, Var name))
  54. (str_ws "+=" <|> str_ws "=+") >>. pexpr |>> fun defExpr -> Assign(ass, Expr.Expr(Plus, Var name, defExpr))
  55. str_ws "=" >>. (asscode <|> assexpr)
  56. ]
  57. let assign name =
  58. let arr =
  59. bet_ws '[' ']' (opt pexpr)
  60. |>> fun braketExpr ->
  61. match braketExpr with
  62. | Some braketExpr ->
  63. AssignArr(name, braketExpr)
  64. | None -> AssignArrAppend name
  65. arr <|>% AssignVar name >>=? assdef name
  66. let pExplicitAssign =
  67. let p =
  68. appendToken
  69. TokenType.Type
  70. ((pstringCI "set" <|> pstringCI "let") .>>? notFollowedVarCont)
  71. .>> ws
  72. >>. (pexplicitVar VarHighlightKind.WriteAccess <|> (pImplicitVarWhenAssign ident |>> fun name -> ImplicitNumericType, name))
  73. p <|> pexplicitVar VarHighlightKind.WriteAccess .>>? ws
  74. >>=? assign
  75. let pImlicitAssign =
  76. pImplicitVarWhenAssign notFollowedByBinOpIdent .>>? ws
  77. >>=? fun name ->
  78. assign (ImplicitNumericType, name)
  79. pExplicitAssign <|> pImlicitAssign
  80. let pcallProc =
  81. let f defines p =
  82. applyRange p
  83. >>= fun (range, name) ->
  84. let p =
  85. defines
  86. |> Map.tryFind (String.toLower name)
  87. |> function
  88. | Some (dscr, sign) ->
  89. appendHover2 dscr range
  90. >>% Some sign
  91. | None ->
  92. [
  93. "Такой процедуры нет, а если есть, то напишите мне, автору расширения, пожалуйста, и я непременно добавлю."
  94. "Когда-нибудь добавлю: 'Возможно, вы имели ввиду: ...'"
  95. ]
  96. |> String.concat "\n"
  97. |> appendSemanticError range
  98. >>% None
  99. appendToken2 TokenType.Procedure range
  100. >>. p
  101. |>> fun sign -> name, range, sign
  102. let pProcWithAsterix: _ Parser =
  103. let p =
  104. pchar '*' >>. many1Satisfy isIdentifierChar
  105. |>> sprintf "*%s" // да, так и хочется использоваться `many1Satisfy2`, но она довольствуется лишь первым символом, то есть '*', потому не подходит
  106. f Defines.proceduresWithAsterix p
  107. let procHoverAndToken =
  108. f Defines.procs notFollowedByBinOpIdent
  109. let pDefProc : _ Parser =
  110. Defines.procs
  111. |> Seq.sortByDescending (fun (KeyValue(name, _)) -> name) // для жадности
  112. |> Seq.map (fun (KeyValue(name, (dscr, sign))) ->
  113. applyRange (pstringCI name .>>? notFollowedVarCont)
  114. >>= fun (range, name) ->
  115. appendToken2 TokenType.Procedure range
  116. >>. appendHover2 dscr range
  117. >>% (name, range, sign)
  118. )
  119. |> List.ofSeq
  120. |> choice
  121. /// Особый случай, который ломает к чертям весь заявленный синтаксис
  122. let adhoc =
  123. let createIdent name =
  124. pstringCI name .>>? notFollowedVarCont
  125. let p name name2 =
  126. createIdent name .>>? ws1 .>>.? createIdent name2
  127. applyRange
  128. ((p "add" "obj"
  129. <|> (createIdent "del" .>>? ws1 .>>.? (createIdent "obj" <|> createIdent "act"))
  130. |>> fun (name1, name2) -> name1 + name2)
  131. <|> (p "close" "all" |>> fun (name1, name2) -> sprintf "%s %s" name1 name2))
  132. >>= fun (range, name) ->
  133. match Map.tryFind (String.toLower name) Defines.procs with
  134. | Some (dscr, sign) ->
  135. appendToken2 TokenType.Procedure range
  136. >>. appendHover2 dscr range
  137. >>% (name, range, sign)
  138. | None -> failwithf "'%s' not found in predef procs" name
  139. pProcWithAsterix
  140. .>> ws .>>. sepBy (applyRange pexpr) (char_ws ',') // Кстати, `,` — "punctuation.separator.parameter.js"
  141. <|> (adhoc <|> pDefProc .>> ws
  142. .>>. (followedBy (skipNewline <|> skipChar '&' <|> eof) >>% []
  143. <|> bet_ws '(' ')' (sepBy (applyRange pexpr) (pchar ',' >>. ws))
  144. <|> sepBy1 (applyRange pexpr) (char_ws ','))
  145. |>> fun ((name, range, sign), args) -> ((name, range, Some sign), args))
  146. <|> (procHoverAndToken
  147. .>>? (ws1 <|> followedBy (satisfy (isAnyOf "'\"")))
  148. .>>.? sepBy1 (applyRange pexpr) (char_ws ','))
  149. >>= fun ((name, range, sign), args) ->
  150. match sign with
  151. | None ->
  152. preturn (CallSt(name, List.map snd args))
  153. | Some x ->
  154. let procNameLower = String.toLower name
  155. let pLoc =
  156. if Set.contains procNameLower Defines.transferOperatorsSet then
  157. match args with
  158. | (r, Val (String [[StringKind locName]]))::_ ->
  159. getUserState
  160. >>= fun (x:State) ->
  161. let nested =
  162. if x.SingleQuotNestedCount > x.DoubleQuotNestedCount then // TODO: ничего хорошего из этого не получится
  163. x.SingleQuotNestedCount
  164. else
  165. x.DoubleQuotNestedCount
  166. |> (+) 1
  167. let r =
  168. { r with
  169. Column1 = r.Column1 + int64 nested // чтобы `'` или `"` пропустить
  170. Column2 = r.Column2 - int64 nested
  171. }
  172. let locNameLower = String.toLower locName
  173. appendLocHighlight r locNameLower VarHighlightKind.ReadAccess
  174. >>. pGetDefLocPos locNameLower
  175. >>= function
  176. | None ->
  177. updateUserState (fun st ->
  178. { st with
  179. NotDefinedLocs =
  180. st.NotDefinedLocs
  181. |> Map.addOrMod locNameLower [r] (fun xs -> r::xs)
  182. }
  183. )
  184. | Some _ -> preturn ()
  185. | _ -> preturn ()
  186. else
  187. preturn ()
  188. args
  189. |> Array.ofList
  190. |> Defines.getFuncByOverloadType x
  191. |> function
  192. | None ->
  193. let msg =
  194. Defines.Show.printSignature name x
  195. |> sprintf "Ожидается одна из перегрузок:\n%s"
  196. appendSemanticError range msg
  197. | Some () ->
  198. preturn ()
  199. >>. pLoc
  200. >>% CallSt(name, List.map snd args)
  201. let pcomment : _ Parser =
  202. let stringLiteralWithToken : _ Parser =
  203. let bet tokenType openedChar closedChar =
  204. let p =
  205. many1Satisfy (fun c' -> not (c' = closedChar || c' = '\n'))
  206. <|> (attempt(skipChar closedChar >>. skipChar closedChar)
  207. >>% string closedChar + string closedChar)
  208. pipe2
  209. (appendToken tokenType (pchar openedChar)
  210. >>. appendToken tokenType (manyStrings p))
  211. (many
  212. (newline >>. appendToken tokenType (manyStrings p))
  213. .>> appendToken tokenType (pchar closedChar)) // TODO: Здесь самое то использовать `PunctuationDefinitionStringEnd`
  214. (fun x xs ->
  215. x::xs |> String.concat "\n"
  216. |> fun x -> sprintf "%c%s%c" openedChar x closedChar
  217. )
  218. bet TokenType.Comment '\'' '\''
  219. <|> bet TokenType.Comment '"' '"'
  220. let p =
  221. appendToken TokenType.Comment
  222. (many1Satisfy (fun c -> c <> '\n' && c <> ''' && c <> '"' && c <> '{'))
  223. <|> stringLiteralWithToken
  224. <|> (pbraces TokenType.Comment |>> sprintf "{%s}")
  225. appendToken TokenType.Comment (pchar '!')
  226. >>. manyStrings p |>> Statement.Comment
  227. let psign =
  228. appendToken TokenType.LabelColon
  229. (pchar ':')
  230. >>. ws
  231. >>. appendToken TokenType.NameLabel
  232. (many1SatisfyL ((<>) '\n') "labelName") // TODO: literal? Trim trailing spaces
  233. |>> Label
  234. let genKeywordParser keyword =
  235. let dscr =
  236. Qsp.Defines.keywords
  237. |> List.tryPick (fun (name, dscr) ->
  238. if name = keyword then Some dscr
  239. else None)
  240. |> Option.defaultWith (fun () -> failwithf "not found %s" keyword)
  241. appendTokenHover TokenType.KeywordControl dscr
  242. (pstringCI keyword .>>? notFollowedVarCont)
  243. let pexit : _ Parser =
  244. genKeywordParser "exit"
  245. >>% Exit
  246. let pendKeyword : _ Parser =
  247. genKeywordParser "end"
  248. let pstmts' pstmt =
  249. many
  250. (pstmt .>> spaces
  251. .>> (skipMany (ppunctuationTerminator .>> spaces)))
  252. let pstmts1' pstmt =
  253. many1
  254. (pstmt .>> spaces
  255. .>> (skipMany (ppunctuationTerminator .>> spaces)))
  256. let pstmt =
  257. let pstmt, pstmtRef = createParserForwardedToRef<Statement, _>()
  258. let pInlineStmts =
  259. many (pstmt .>> ws .>> skipMany (ppunctuationTerminator .>> ws))
  260. let pInlineStmts1 =
  261. many1 (pstmt .>> ws .>> skipMany (ppunctuationTerminator .>> ws))
  262. let pstmts = pstmts' pstmt
  263. let pcolonKeyword : _ Parser =
  264. appendToken TokenType.KeywordControl (pchar ':')
  265. let pAct =
  266. let pactKeyword : _ Parser =
  267. genKeywordParser "act"
  268. let pactHeader = pactKeyword .>> ws >>. sepBy1 pexpr (char_ws ',') .>> pcolonKeyword
  269. pipe2
  270. pactHeader
  271. ((ws >>? skipNewline >>. spaces >>. pstmts .>> pendKeyword)
  272. <|> (spaces >>. pInlineStmts .>> optional pendKeyword))
  273. (fun expr body ->
  274. Act(expr, body))
  275. let pFor =
  276. let pForHeader =
  277. genKeywordParser "for" >>. ws
  278. >>. (pexplicitVar VarHighlightKind.WriteAccess
  279. <|> (pImplicitVarWhenAssign ident |>> fun name -> ImplicitNumericType, name))
  280. .>> ws .>> appendToken TokenType.OperatorAssignment (pchar '=')
  281. .>> ws .>>. pexpr
  282. .>> genKeywordParser "to"
  283. .>> ws .>>. pexpr
  284. .>> pcolonKeyword
  285. pipe2
  286. pForHeader
  287. ((ws >>? skipNewline >>. spaces >>. pstmts .>> pendKeyword)
  288. <|> (spaces >>. pInlineStmts .>> optional pendKeyword))
  289. (fun ((var, fromExpr), toExpr) body ->
  290. For(var, fromExpr, toExpr, body))
  291. let pIf =
  292. let pifKeyword : _ Parser =
  293. genKeywordParser "if"
  294. let pelseifKeyword : _ Parser =
  295. genKeywordParser "elseif"
  296. let pelseKeyword : _ Parser =
  297. genKeywordParser "else"
  298. let pifHeader = pifKeyword .>> ws >>. pexpr .>> pcolonKeyword
  299. let pelseifHeader = pelseifKeyword .>> ws >>. pexpr .>> pcolonKeyword
  300. let setIsEndOptionalTo boolean =
  301. updateUserState (fun x -> { x with IsEndOptional = boolean })
  302. let p =
  303. ws .>>? skipNewline >>. spaces >>. pstmts .>> setIsEndOptionalTo false
  304. <|> (spaces >>. pInlineStmts .>> setIsEndOptionalTo true)
  305. let pElse1 =
  306. pelseKeyword >>. ws
  307. >>. (pInlineStmts1 .>> opt pendKeyword
  308. <|> (spaces >>. pstmts .>> pendKeyword))
  309. let pend =
  310. getUserState
  311. >>= fun x ->
  312. if x.IsEndOptional then
  313. optional pendKeyword
  314. else
  315. pendKeyword >>% ()
  316. let pelseIf =
  317. many1 (pelseifHeader .>>. p)
  318. .>>. (pElse1 <|> (pend >>% []))
  319. |>> fun (elifs, elseBody) ->
  320. let rec f = function
  321. | (expr, thenBody)::xs ->
  322. [If(expr, thenBody, f xs)]
  323. | [] -> elseBody
  324. f elifs
  325. // `end` нужен, чтобы инструкции, определенные ниже, не ушли в тело `if`
  326. // ```qsps
  327. // if expr:
  328. // stmt1
  329. // end & ! без него `stmt2` станет принадлежать телу `if`
  330. // stmt2
  331. // ...
  332. // ```
  333. // `if expr: stmt1 & stmt2 & ...` — такому выражению `end` не нужен, поскольку эту роль выполняет конец строки.
  334. // Также работает и с `elif expr: stmt1 & stmt2 & ...`, и с `else expr: stmt1 & stmt2 & ...`.
  335. pipe2
  336. (pifHeader .>> ws)
  337. ((pInlineStmts1 .>>. (pelseIf <|> pElse1 <|> (opt pendKeyword >>% []))
  338. <|> (spaces >>. pstmts .>>. (pelseIf <|> pElse1 <|> (pendKeyword >>% [])))))
  339. (fun expr (thenBody, elseBody) ->
  340. If(expr, thenBody, elseBody))
  341. pstmtRef :=
  342. choice [
  343. pcomment
  344. pexit
  345. psign
  346. pIf
  347. pAct
  348. pFor
  349. pAssign pstmts
  350. pcallProc
  351. notFollowedBy (pchar '-' >>. ws >>. (skipNewline <|> skipChar '-' <|> eof)) // `-` завершает локацию
  352. >>. (pexpr |>> StarPl)
  353. ]
  354. pstmt
  355. let pstmts = pstmts' pstmt
  356. let pstmts1 = pstmts1' pstmt
  357. let psharpKeyword : _ Parser =
  358. appendToken TokenType.KeywordControl (pchar '#')
  359. let pminusKeyword : _ Parser =
  360. appendToken TokenType.KeywordControl (pchar '-') // хотя здесь больше подошел бы обычный `end`
  361. let ploc =
  362. let pendKeyword =
  363. applyRange (pstringCI "end" .>>? notFollowedVarCont)
  364. >>= fun (range, _) ->
  365. appendToken2 TokenType.KeywordControl range
  366. >>. appendSemanticError range "Лишний `end`"
  367. pipe2
  368. (psharpKeyword .>> ws
  369. >>. (applyRange
  370. (many1Strings
  371. (many1Satisfy (isNoneOf " \t\n")
  372. <|> (many1Satisfy (isAnyOf " \t") .>>? notFollowedByNewline))
  373. <?> "location name")
  374. >>= fun (r, name) ->
  375. let pCheckLocExists r2 locName =
  376. pGetDefLocPos locName
  377. >>= function
  378. | Some r ->
  379. sprintf "Локация уже определена в\n%A" r
  380. |> appendSemanticError r2
  381. | None -> preturn ()
  382. let locNameLower = String.toLower name
  383. pCheckLocExists r locNameLower
  384. >>. updateUserState (fun st ->
  385. { st with
  386. NotDefinedLocs =
  387. Map.remove locNameLower st.NotDefinedLocs // ну да, к чему проверки? И так удалит
  388. }
  389. )
  390. >>. appendLocHighlight r locNameLower VarHighlightKind.WriteAccess // и все равно добавить, даже в случае семантической ошибки? Хм, ¯\_(ツ)_/¯
  391. >>. appendToken2 TokenType.StringQuotedSingle r
  392. >>. preturn name
  393. )
  394. .>> spaces)
  395. (many (pstmts1 .>> many (pendKeyword .>> spaces)) |>> List.concat
  396. .>> (pminusKeyword .>> ws
  397. .>> appendToken TokenType.Comment
  398. (skipManySatisfy ((<>) '\n'))))
  399. (fun name body -> Location(name, body))
  400. let pAfterAll =
  401. preturn ()
  402. let start str =
  403. let emptyState =
  404. { emptyState with PStmts = pstmts }
  405. let p =
  406. spaces >>. many (ploc .>> spaces)
  407. .>> (getPosition >>= fun p ->
  408. updateUserState (fun st ->
  409. { st with LastSymbolPos = p}))
  410. runParserOnString (p .>> pAfterAll .>> eof)
  411. emptyState
  412. ""
  413. str
  414. let startOnFile enc path =
  415. let emptyState =
  416. { emptyState with PStmts = pstmts }
  417. let p =
  418. spaces >>. many (ploc .>> spaces)
  419. .>> (getPosition >>= fun p ->
  420. updateUserState (fun st ->
  421. { st with LastSymbolPos = p}))
  422. runParserOnFile (p .>> pAfterAll .>> eof)
  423. emptyState
  424. path
  425. enc