123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457 |
- module Qsp.Parser.Main
- open FParsec
- open FsharpMyExtension
- open FsharpMyExtension.Either
- open Qsp
- open Qsp.Ast
- open Qsp.Parser.Generic
- open Qsp.Parser.Expr
- let ppunctuationTerminator : _ Parser =
- appendToken Tokens.TokenType.PunctuationTerminatorStatement (pchar '&')
- let pImplicitVarWhenAssign p =
- applyRange p
- >>= fun (range, (name:string)) ->
- let nameLower = name.ToLower()
- Defines.vars
- |> Map.tryFind nameLower
- |> function
- | Some dscr ->
- appendHover2 dscr range
- | None ->
- if Map.containsKey nameLower Defines.procs then
- appendSemanticError range "Нельзя переопределять процедуру"
- elif Map.containsKey nameLower Defines.functions then
- appendSemanticError range "Нельзя переопределять функцию"
- else
- let dscr = "Пользовательская глобальная переменная числового типа"
- appendHover2 dscr range
- >>. appendToken2 Tokens.TokenType.Variable range
- >>. appendVarHighlight range (ImplicitNumericType, name) VarHighlightKind.WriteAccess
- >>. preturn name
- let pAssign stmts =
- let assdef name ass =
- let asscode =
- between (pchar '{' >>. spaces) (spaces >>. char_ws '}') stmts
- |>> fun stmts -> AssignCode(ass, stmts)
- let call =
- ident >>=?
- fun name ->
- followedBy (
- ident
- <|> (puint32 >>% "")
- <|> stringLiteral)
- >>. sepBy1 pexpr (char_ws ',')
- |>> fun args -> Assign(ass, Func(name, args)) // То есть `a = min x, y` можно, что ли? Хм...
- let assexpr = call <|> (pexpr |>> fun defExpr -> Assign(ass, defExpr))
- let str_ws s =
- appendToken Tokens.TokenType.OperatorAssignment
- (pstring s)
- .>> ws
- choice [
- str_ws "-=" >>. pexpr |>> fun defExpr -> Assign(ass, Expr.Expr(Minus, Var name, defExpr))
- str_ws "=-" >>. pexpr |>> fun defExpr -> Assign(ass, Expr.Expr(Minus, defExpr, Var name))
- (str_ws "+=" <|> str_ws "=+") >>. pexpr |>> fun defExpr -> Assign(ass, Expr.Expr(Plus, Var name, defExpr))
- str_ws "=" >>. (asscode <|> assexpr)
- ]
- let assign name =
- let arr =
- bet_ws '[' ']' (opt pexpr)
- |>> fun braketExpr ->
- match braketExpr with
- | Some braketExpr ->
- AssignArr(name, braketExpr)
- | None -> AssignArrAppend name
- arr <|>% AssignVar name >>=? assdef name
- let pExplicitAssign =
- let p =
- appendToken
- Tokens.TokenType.Type
- ((pstringCI "set" <|> pstringCI "let") .>>? notFollowedVarCont)
- .>> ws
- >>. (pexplicitVar VarHighlightKind.WriteAccess <|> (pImplicitVarWhenAssign ident |>> fun name -> ImplicitNumericType, name))
- p <|> pexplicitVar VarHighlightKind.WriteAccess .>>? ws
- >>=? assign
- let pImlicitAssign =
- pImplicitVarWhenAssign notFollowedByBinOpIdent .>>? ws
- >>=? fun name ->
- assign (ImplicitNumericType, name)
- pExplicitAssign <|> pImlicitAssign
- let pcallProc =
- let f defines p =
- applyRange p
- >>= fun (range, name) ->
- let p =
- defines
- |> Map.tryFind (String.toLower name)
- |> function
- | Some (dscr, sign) ->
- appendHover2 dscr range
- >>% Some sign
- | None ->
- [
- "Такой процедуры нет, а если есть, то напишите мне, автору расширения, пожалуйста, и я непременно добавлю."
- "Когда-нибудь добавлю: 'Возможно, вы имели ввиду: ...'"
- ]
- |> String.concat "\n"
- |> appendSemanticError range
- >>% None
- appendToken2 Tokens.TokenType.Procedure range
- >>. p
- |>> fun sign -> name, range, sign
- let pProcWithAsterix: _ Parser =
- let p =
- pchar '*' >>. many1Satisfy isIdentifierChar
- |>> sprintf "*%s" // да, так и хочется использоваться `many1Satisfy2`, но она довольствуется лишь первым символом, то есть '*', потому не подходит
- f Defines.proceduresWithAsterix p
- let procHoverAndToken =
- f Defines.procs notFollowedByBinOpIdent
- let pDefProc : _ Parser =
- Defines.procs
- |> Seq.sortByDescending (fun (KeyValue(name, _)) -> name) // для жадности
- |> Seq.map (fun (KeyValue(name, (dscr, sign))) ->
- applyRange (pstringCI name .>>? notFollowedVarCont)
- >>= fun (range, name) ->
- appendToken2 Tokens.TokenType.Procedure range
- >>. appendHover2 dscr range
- >>% (name, range, sign)
- )
- |> List.ofSeq
- |> choice
- /// Особый случай, который ломает к чертям весь заявленный синтаксис
- let adhoc =
- let createIdent name =
- pstringCI name .>>? notFollowedVarCont
- let p name name2 =
- createIdent name .>>? ws1 .>>.? createIdent name2
- applyRange
- ((p "add" "obj"
- <|> (createIdent "del" .>>? ws1 .>>.? (createIdent "obj" <|> createIdent "act"))
- |>> fun (name1, name2) -> name1 + name2)
- <|> (p "close" "all" |>> fun (name1, name2) -> sprintf "%s %s" name1 name2))
- >>= fun (range, name) ->
- match Map.tryFind (String.toLower name) Defines.procs with
- | Some (dscr, sign) ->
- appendToken2 Tokens.TokenType.Procedure range
- >>. appendHover2 dscr range
- >>% (name, range, sign)
- | None -> failwithf "'%s' not found in predef procs" name
- pProcWithAsterix
- .>> ws .>>. sepBy (applyRange pexpr) (char_ws ',') // Кстати, `,` — "punctuation.separator.parameter.js"
- <|> (adhoc <|> pDefProc .>> ws
- .>>. (followedBy (skipNewline <|> skipChar '&' <|> eof) >>% []
- <|> bet_ws '(' ')' (sepBy (applyRange pexpr) (pchar ',' >>. ws))
- <|> sepBy1 (applyRange pexpr) (char_ws ','))
- |>> fun ((name, range, sign), args) -> ((name, range, Some sign), args))
- <|> (procHoverAndToken
- .>>? (ws1 <|> followedBy (satisfy (isAnyOf "'\"")))
- .>>.? sepBy1 (applyRange pexpr) (char_ws ','))
- >>= fun ((name, range, sign), args) ->
- match sign with
- | None ->
- preturn (CallSt(name, List.map snd args))
- | Some x ->
- let procNameLower = String.toLower name
- let pLoc =
- if Set.contains procNameLower Defines.transferOperatorsSet then
- match args with
- | (r, Val (String [[StringKind locName]]))::_ ->
- getUserState
- >>= fun (x:State) ->
- let nested =
- if x.SingleQuotNestedCount > x.DoubleQuotNestedCount then // TODO: ничего хорошего из этого не получится
- x.SingleQuotNestedCount
- else
- x.DoubleQuotNestedCount
- |> (+) 1
- let r =
- { r with
- Column1 = r.Column1 + int64 nested // чтобы `'` или `"` пропустить
- Column2 = r.Column2 - int64 nested
- }
- let locNameLower = String.toLower locName
- appendLocHighlight r locNameLower VarHighlightKind.ReadAccess
- >>. pGetDefLocPos locNameLower
- >>= function
- | None ->
- updateUserState (fun st ->
- { st with
- NotDefinedLocs =
- st.NotDefinedLocs
- |> Map.addOrMod locNameLower [r] (fun xs -> r::xs)
- }
- )
- | Some _ -> preturn ()
- | _ -> preturn ()
- else
- preturn ()
- args
- |> Array.ofList
- |> Defines.getFuncByOverloadType x
- |> function
- | None ->
- let msg =
- Defines.Show.printSignature name x
- |> sprintf "Ожидается одна из перегрузок:\n%s"
- appendSemanticError range msg
- | Some () ->
- preturn ()
- >>. pLoc
- >>% CallSt(name, List.map snd args)
- let pcomment : _ Parser =
- let stringLiteralWithToken : _ Parser =
- let bet tokenType openedChar closedChar =
- let p =
- many1Satisfy (fun c' -> not (c' = closedChar || c' = '\n'))
- <|> (attempt(skipChar closedChar >>. skipChar closedChar)
- >>% string closedChar + string closedChar)
- pipe2
- (appendToken tokenType (pchar openedChar)
- >>. appendToken tokenType (manyStrings p))
- (many
- (newline >>. appendToken tokenType (manyStrings p))
- .>> appendToken tokenType (pchar closedChar)) // TODO: Здесь самое то использовать `PunctuationDefinitionStringEnd`
- (fun x xs ->
- x::xs |> String.concat "\n"
- |> fun x -> sprintf "%c%s%c" openedChar x closedChar
- )
- bet Tokens.TokenType.Comment '\'' '\''
- <|> bet Tokens.TokenType.Comment '"' '"'
- let p =
- appendToken Tokens.TokenType.Comment
- (many1Satisfy (fun c -> c <> '\n' && c <> ''' && c <> '"' && c <> '{'))
- <|> stringLiteralWithToken
- <|> (pbraces Tokens.TokenType.Comment |>> sprintf "{%s}")
- appendToken Tokens.TokenType.Comment (pchar '!')
- >>. manyStrings p |>> Statement.Comment
- let psign =
- appendToken Tokens.TokenType.LabelColon
- (pchar ':')
- >>. ws
- >>. appendToken Tokens.TokenType.NameLabel
- (many1SatisfyL ((<>) '\n') "labelName") // TODO: literal? Trim trailing spaces
- |>> Label
- let genKeywordParser tokenType keyword =
- let dscr =
- Defines.keywords
- |> List.tryPick (fun (name, dscr) ->
- if name = keyword then Some dscr
- else None)
- |> Option.defaultWith (fun () -> failwithf "not found %s" keyword)
- appendTokenHover tokenType dscr
- (pstringCI keyword .>>? notFollowedVarCont)
- let pexit : _ Parser =
- genKeywordParser Tokens.TokenType.Exit "exit"
- >>% Exit
- let pendKeyword : _ Parser =
- genKeywordParser Tokens.TokenType.End "end"
- let pstmts' pstmt =
- many
- (pstmt .>> spaces
- .>> (skipMany (ppunctuationTerminator .>> spaces)))
- let pstmts1' pstmt =
- many1
- (pstmt .>> spaces
- .>> (skipMany (ppunctuationTerminator .>> spaces)))
- let pstmt =
- let pstmt, pstmtRef = createParserForwardedToRef<Statement, _>()
- let pInlineStmts =
- many (pstmt .>> ws .>> skipMany (ppunctuationTerminator .>> ws))
- let pInlineStmts1 =
- many1 (pstmt .>> ws .>> skipMany (ppunctuationTerminator .>> ws))
- let pstmts = pstmts' pstmt
- let pcolonKeyword : _ Parser =
- appendToken Tokens.TokenType.Colon (pchar ':')
- let pAct =
- let pactKeyword : _ Parser =
- genKeywordParser Tokens.TokenType.Act "act"
- let pactHeader = pactKeyword .>> ws >>. sepBy1 pexpr (char_ws ',') .>> pcolonKeyword
- pipe2
- pactHeader
- ((ws >>? skipNewline >>. spaces >>. pstmts .>> pendKeyword)
- <|> (spaces >>. pInlineStmts .>> optional pendKeyword))
- (fun expr body ->
- Act(expr, body))
- let pFor =
- let pForHeader =
- genKeywordParser Tokens.TokenType.For "for" >>. ws
- >>. (pexplicitVar VarHighlightKind.WriteAccess
- <|> (pImplicitVarWhenAssign ident |>> fun name -> ImplicitNumericType, name))
- .>> ws .>> appendToken Tokens.TokenType.OperatorAssignment (pchar '=')
- .>> ws .>>. pexpr
- .>> genKeywordParser Tokens.TokenType.To "to"
- .>> ws .>>. pexpr
- .>>. opt (genKeywordParser Tokens.TokenType.Step "step"
- .>> ws >>. pexpr)
- .>> pcolonKeyword
- pipe2
- pForHeader
- ((ws >>? skipNewline >>. spaces >>. pstmts .>> pendKeyword)
- <|> (spaces >>. pInlineStmts .>> optional pendKeyword))
- (fun (((var, fromExpr), toExpr), stepExpr) body ->
- For(var, fromExpr, toExpr, stepExpr, body))
- let pIf =
- let pifKeyword : _ Parser =
- genKeywordParser Tokens.TokenType.If "if"
- let pelseifKeyword : _ Parser =
- genKeywordParser Tokens.TokenType.ElseIf "elseif"
- let pelseKeyword : _ Parser =
- genKeywordParser Tokens.TokenType.Else "else"
- let pifHeader = pifKeyword .>> ws >>. pexpr .>> pcolonKeyword
- let pelseifHeader = pelseifKeyword .>> ws >>. pexpr .>> pcolonKeyword
- let setIsEndOptionalTo boolean =
- updateUserState (fun x -> { x with IsEndOptional = boolean })
- let p =
- ws .>>? skipNewline >>. spaces >>. pstmts .>> setIsEndOptionalTo false
- <|> (spaces >>. pInlineStmts .>> setIsEndOptionalTo true)
- let pElse1 =
- pelseKeyword >>. ws
- >>. (pInlineStmts1 .>> opt pendKeyword
- <|> (spaces >>. pstmts .>> pendKeyword))
- let pend =
- getUserState
- >>= fun x ->
- if x.IsEndOptional then
- optional pendKeyword
- else
- pendKeyword >>% ()
- let pelseIf =
- many1 (pelseifHeader .>>. p)
- .>>. (pElse1 <|> (pend >>% []))
- |>> fun (elifs, elseBody) ->
- let rec f = function
- | (expr, thenBody)::xs ->
- [If(expr, thenBody, f xs)]
- | [] -> elseBody
- f elifs
- // `end` нужен, чтобы инструкции, определенные ниже, не ушли в тело `if`
- // ```qsps
- // if expr:
- // stmt1
- // end & ! без него `stmt2` станет принадлежать телу `if`
- // stmt2
- // ...
- // ```
- // `if expr: stmt1 & stmt2 & ...` — такому выражению `end` не нужен, поскольку эту роль выполняет конец строки.
- // Также работает и с `elif expr: stmt1 & stmt2 & ...`, и с `else expr: stmt1 & stmt2 & ...`.
- pipe2
- (pifHeader .>> ws)
- ((pInlineStmts1 .>>. (pelseIf <|> pElse1 <|> (opt pendKeyword >>% []))
- <|> (spaces >>. pstmts .>>. (pelseIf <|> pElse1 <|> (pendKeyword >>% [])))))
- (fun expr (thenBody, elseBody) ->
- If(expr, thenBody, elseBody))
- pstmtRef :=
- choice [
- pcomment
- pexit
- psign
- pIf
- pAct
- pFor
- pAssign pstmts
- pcallProc
- notFollowedBy (pchar '-' >>. ws >>. (skipNewline <|> skipChar '-' <|> eof)) // `-` завершает локацию
- >>. (pexpr |>> StarPl)
- ]
- pstmt
- let pstmts = pstmts' pstmt
- let pstmts1 = pstmts1' pstmt
- let psharpKeyword : _ Parser =
- appendToken Tokens.TokenType.SharpBeginLoc (pchar '#')
- let pminusKeyword : _ Parser =
- appendToken Tokens.TokenType.MinusEndLoc (pchar '-') // хотя здесь больше подошел бы обычный `end`
- let ploc =
- let pendKeyword =
- applyRange (pstringCI "end" .>>? notFollowedVarCont)
- >>= fun (range, _) ->
- appendToken2 Tokens.TokenType.End range
- >>. appendSemanticError range "Лишний `end`"
- pipe2
- (psharpKeyword .>> ws
- >>. (applyRange
- (many1Strings
- (many1Satisfy (isNoneOf " \t\n")
- <|> (many1Satisfy (isAnyOf " \t") .>>? notFollowedByNewline))
- <?> "location name")
- >>= fun (r, name) ->
- let pCheckLocExists r2 locName =
- pGetDefLocPos locName
- >>= function
- | Some r ->
- sprintf "Локация уже определена в\n%A" r
- |> appendSemanticError r2
- | None -> preturn ()
- let locNameLower = String.toLower name
- pCheckLocExists r locNameLower
- >>. updateUserState (fun st ->
- { st with
- NotDefinedLocs =
- Map.remove locNameLower st.NotDefinedLocs // ну да, к чему проверки? И так удалит
- }
- )
- >>. appendLocHighlight r locNameLower VarHighlightKind.WriteAccess // и все равно добавить, даже в случае семантической ошибки? Хм, ¯\_(ツ)_/¯
- >>. appendToken2 Tokens.TokenType.StringQuotedSingle r
- >>. preturn name
- )
- .>> spaces)
- (many (pstmts1 .>> many (pendKeyword .>> spaces)) |>> List.concat
- .>> (pminusKeyword .>> ws
- .>> appendToken Tokens.TokenType.Comment
- (skipManySatisfy ((<>) '\n'))))
- (fun name body -> Location(name, body))
- let pAfterAll =
- preturn ()
- let start str =
- let emptyState =
- { emptyState with PStmts = pstmts }
- let p =
- spaces >>. many (ploc .>> spaces)
- .>> (getPosition >>= fun p ->
- updateUserState (fun st ->
- { st with LastSymbolPos = p}))
- runParserOnString (p .>> pAfterAll .>> eof)
- emptyState
- ""
- str
- let startOnFile enc path =
- let emptyState =
- { emptyState with PStmts = pstmts }
- let p =
- spaces >>. many (ploc .>> spaces)
- .>> (getPosition >>= fun p ->
- updateUserState (fun st ->
- { st with LastSymbolPos = p}))
- runParserOnFile (p .>> pAfterAll .>> eof)
- emptyState
- path
- enc
|