Parsec.fs 20 KB

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