?=      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ GThe data type 6 is used to represent the minimal length of a parser. \ Care should be taken in order to not evaluate the right hand side of the binary function  `nat-add` more than necesssary. The data type A is the core data type around which the parsers are constructed. ~ It describes a tree structure of streams containing (in an interleaved way) both the online result of the parsing process, a and progress information. Recognising an input token should correspond to a certain amount of , : which tells how much of the input state was consumed.  The R is used to implement the breadth-first search process, in which alternatives are K examined in a more-or-less synchronised way. The meaning of the various  constructors is as follows:  C A token was succesfully recognised, and as a result the input was advanced by the distance   & The type of value represented by the - changes by applying the function parameter.   A correcting step has to be made to the input; the first parameter contains information about what was expected in the input, b and the second parameter describes the various corrected alternatives, each with an associated   Y A small cost is inserted in the sequence, which is used to disambiguate. Use with care! The last two alternatives play a role in recognising ambigous non-terminals. For a full description see the technical report referred to from   Text.ParserCombinators.UU.README. The data type F contains three components, all being some form of primitive parser. 6 These components are used in various combinations, K depending on whether you are in the right and side operand of a monad, I whether you are interested in a result (if not, we use recognisers),  and whether you want to have the results in an online way (future parsers), or just prefer to be a bit faster (history parsers) c retrieves the correcting steps made since the last time the function was called. The result can, t by using it as the left hand side of a monadic bind, be used to control how to proceed with the parsing process.  The class  is used by the function & which retrieves the generated 6 correction steps since the last time it was called. c retrieves the correcting steps made since the last time the function was called. The result can, X by using it in a monad, be used to control how to proceed with the parsing process. YThe input state may maintain a location which can be used in generating error messages. 6 Since we do not want to fix our input to be just a String we provide an interface | which can be used to advance this location by passing information about the part recognised. This function is typically  called in the  splitState functions. The current position /The part which has been removed from the input  The class  contains a function I which is used to check whether we have reached the end of the input and  deletAtEnd I should discard any unconsumed input at the end of a successful parse.  is the greedy version of #. If its left hand side parser can N make any progress then it commits to that alternative. Can be used to make M parsers faster, and even get a complete Parsec equivalent behaviour, with ) all its (dis)advantages. Intended use p <<|> q <<|> r <|> x <|> y <?> "string". Use with care! MThe parsers build a list of symbols which are expected at a specific point. ' This list is used to report errors. R Quite often it is more informative to get e.g. the name of the non-terminal .  The E combinator replaces this list of symbols by the string argument. P makes a parser opaque for abstract interpretation; used when permuting parsers - where we do not want to compare lengths. $ checks whether its second argument P is a parser which can recognise the empty input. If so, an error message is J given using the String parameter. If not, then the third argument is T returned. This is useful in testing for illogical combinations. For its use see  the module !Text.ParserCombinators.UU.Derived.  is similar to , but can be O used in situations where we recognise a sequence of elements separated by P other elements. This does not make sense if both parsers can recognise the 9 empty string. Your grammar is then highly ambiguous. If p( can be recognized, the return value of p is used. Otherwise,  the value v is used. Note that * by default is greedy. If you do not want  this use ...<|> pure v instead. Furthermore, p should not L recognise the empty string, since this would make the parser ambiguous!!  In the class  j we assemble the basic properties we expect parsers to have. The class itself does not have any methods. 5 Most properties come directly from the standard  Control.Applicative module. The class < contains some extra methods we expect our parsers to have. !!0 retrieves the non-zero part from a descriptor. ""6 retrieves the possibly empty part from a descriptor. v combines the non-empty descriptor part and the empty descriptor part into a descriptor tupled with the parser triple #@The basic recognisers are written elsewhere (e.g. in our module )Text.ParserCombinataors.UU.BasicInstances;  they (i.e. the parameter  splitState) are lifted to our descriptors by the function # which also takes = the minimal number of tokens recognised by the parameter  spliState and an Maybe, value describing the possibly empty value. $$ inserts a I step into the sequence representing the progress the parser is making;  for its use see `(Text.ParserCombinators.UU.Demos.Examples` %#For the precise functioning of the %( combinators see the paper cited in the  Text.ParserCombinators.UU.README; a it converts an ambiguous parser into a parser which returns a list of possible recognitions, &&E returns the error messages that were generated since its last call. ''% returns the current input position. ! returns the current input state ( The function (v should be called at the end of the parsing process. It deletes any unconsumed input, turning it into error messages. ))p takes the current state and modifies it to a different type of state to which its argument parser is applied.  The second component of the result is a function which converts the remaining state of this parser back into a value of the original type.  For the second argument to )/ (say split) we expect the following to hold: $ let (n,f) = split st in f n == st * The function *3 shows the prototypical way of running a parser on  some specific input. K By default we use the future parser, since this gives us access to partal ; result; future parsers are expected to run in less space. + The function + behaves like * but using the history I parser. This parser does not give online results, but might run faster. ,,e removes the progress information from a sequence of steps, and constructs the value embedded in it.  If you are really desparate to see how your parsers are making progress (e.g. when you have written an ambiguous parser, and you cannot find the cause of a the exponential blow-up of your parsing process), you may switch on the trace in the function ,2 (you will need to edit the library source code). J makes sure that the head of the seqeunce contains progress information. @ It does so by pushing information about the result (i.e. the   steps) backwards.  The function best compares two streams  getlength8 retrieves the length of the non-empty part of a parser l compares two minmal length and returns the shorter length. The second component indicates whether the left - operand is the smaller one; we cannot use Either5 since the first component may already be inspected 2 before we know which operand is finally chosen s      !"#$%&'()*+,-./0123456  !"#$%&'()*+,-  $%&'()#*+"!,-  !"#$%&'()*+,$--& is defined for upwards compatibility ..; is defined for upwards compatibility, and is the unit for  | //* greedily recognises its argument. If not Nothing is returned. 00) recognises either one of its arguments. 11 is the version of $ which flips the function argument 22S parses an optional postfix element and applies its result to its left hand result 33 is equivalent to the  from Control.Applicative=. We want however all our parsers to start with a lower case p. 44 is equivalent to the  from Control.Applicative=. We want however all our parsers to start with a lower case p. 55` surrounds its third parser with the first and the second one, returning only the middle result 6789:;<=>?@ABCDEFGHIJKK+ recognises a specified number of elements LMNOCount the number of times p has succeeded PGBuild a parser for each element in the argument list and try them all. $-./0123456789:;<=>?@ABCDEFGHIJKLMNOP$-./0123456789:;<=>?@ABCDEFGHIJKLMNOP$-./0123456789:;<=>?@ABCDEFGHIJKLMNOP QRSToSince we want to get access to the individual parsers which recognise a consecutive piece of the input text we  define a new data type, which lifts the underlying parsers to the grammatical level, so they can be transformed, manipulated, and run in a piecewise way.  UTg is defined in such a way that we can always access the first parsers to be ran from such a structure.  We require that all the QDs do not recognise the empty string. These should be covered by the 7 in the UT constructor. UV The function VM splits a simple parser into the possibly empty part and the non-empty part. B The non-empty part recognises a consecutive part of the input.  Here we use the functions ! and "2 which are provided in the uu-parsinglib package, < but they could easily be provided by other packages too. W The function W is the merging equivalent of 7. Instead of running its two arguments consecutively, } the input is split into parts which serve as input for the left operand and parts which are served to the right operand. X The function X is a special version of W^, which only starts a new instance of its right operand when the left operand cannot proceed.  This is used in the function [S, where we want to merge as many instances of its argument, but no more than that. YY converts a UT6mar back into a parser, which can subsequenly be run. ZZ is like Yr, with the additional feature that we allow separators between the components. Only useful in the permuting case. [Run a sufficient number of p'4s in a merged fashion, but no more than necessary!! 8\The left hand side operand is gradually transformed so we get access to its first component 9&We define instances for the data type UT for , ,   and  QRSTUVWXYZ[ TUQSRVWXYZ[ QSRRSTUUVWXYZ[!\the String' describes what is being inserted, the a5 parameter the value which is to be inserted and the cost the prices to be paid. ]^_`abA b a b maps a c a onto a c b. cA c( is a parser that is prepared to accept  Data.Listlike input; hence we can deal with String's,  ByteString's, etc. dThe data type edX holds the input data to be parsed, the current location, the error messages generated J and whether it is ok to delete elements from the input. Since an insert/delete action is  the same as a delete/.insert action we try to avoid the first one. # So: no deletes after an insert. ef!the unconsumed part of the input gthe accumulated error messages hthe current input position i,we want to avoid deletions after insertions jThe data type j^ describes the various kinds of errors which can be generated by the instances in this module k-the unconsumed part of the input was deleted lfor future use mString was deleted at pos-ition, where we expected Strings nString was inserted at pos-ition, where we expected Strings oppm initialises the input stream with the input data and the initial position. There are no error messages yet. qq} describes and elementary parsing step. Its first parameter check whether the head element of the input can be recognised, e and the second parameter how to proceed in case an element recognised by this parser is absent, \ and parsing may proceed by pretending such an element was present in the input anayway. rri recognises an element between a lower and an upper bound. Furthermore it can be specified what element N is to be inserted in case such an element is not at the head of the input. ss5 uses the information from the bounds to compute the ]\ information. ttN recognises a specific element. Furthermore it can be specified what element N is to be inserted in case such an element is not at the head of the input. uu` recognises a specific element. Furthermore it can be specified what element. Information about ]\ is derived from the parameter. N is to be inserted in case such an element is not at the head of the input. :: recognises the longest prefix of the input for which the passed predicate holds. The message parameter is used when tracing has been switched on. vv recognises the longest prefix of the input for which the passed predicate holds. The message parameer is used when tracing has been switched on. ww6 succeeds if its parameter is a prefix of the input. x;<=\]^_`abcdefghijklmnopqrstuvwxjnmlkdefghi\]`a^_cbpoqrstuxwv\]]^__`aabcdefghiefghijnmlkklmnopqrstuvwx<>?@ABCDEFGHIJyz{|}~MLexeme Parsers skip trailing whitespace (this terminology comes from Parsec) KNB - At present this is not a lexeme parser, hence we don't  support - 7.0, - 7, + 7.0 etc.  It'4s also currently private - ie local to this module. L eg [1,2,3]  eg (1,2,3) ,Converts a UU Parser into a read-style one. 9This is intended to facilitate migration from read-style  parsers to UU-based ones. 0The lower-level interface. Returns all errors. #The higher-level interface. (Calls M with a simplified error). S Runs the parser; if the complete input is accepted without problems return the 5 result else fail with reporting unconsumed tokens -yz{|}~-yz{|}~-yz{|}~       !"#$%&'()*+,-./0123456  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[Running the function # should give the following output:  run pa "a" Result: "a" run pa "" Result: "a" Correcting steps: ; Inserted 'a' at position LineColPos 0 0 0 expecting 'a' run pa "b" Result: "a" Correcting steps: ; Deleted 'b' at position LineColPos 0 0 0 expecting 'a'; Inserted 'a' at position LineColPos 0 1 1 expecting 'a' run ((++) <$> pa <*> pa) "bbab" Result: "aa" Correcting steps: ; Deleted 'b' at position LineColPos 0 0 0 expecting 'a'; Deleted 'b' at position LineColPos 0 1 1 expecting 'a'; Deleted 'b' at position LineColPos 0 3 3 expecting 'a'; Inserted 'a' at position LineColPos 0 4 4 expecting 'a' run pa "ba" Result: "a" Correcting steps: ; Deleted 'b' at position LineColPos 0 0 0 expecting 'a' run pa "aa" Result: "a" Correcting steps: 9 The token 'a' was not consumed by the parsing process.$run (pCount pa :: Parser Int) "aaa" Result: 35run (do {l <- pCount pa; pExact l pb}) "aaacabbbbb" Result: ["b","b","b","b"] Correcting steps: I Deleted 'c' at position LineColPos 0 3 3 expecting one of ['b', 'a']9 The token 'b' was not consumed by the parsing process.Crun (amb ( (++) <$> pa2 <*> pa3 <|> (++) <$> pa3 <*> pa2)) "aaaaa" Result: ["aaaaa","aaaaa"]run (pList pLower) "doaitse" Result: "doaitse"run paz "abc2ez" Result: "abcez" Correcting steps: @ Deleted '2' at position LineColPos 0 3 3 expecting 'a'..'z'Drun (max <$> pParens ((+1) <$> wfp) <*> wfp `opt` 0) "((()))()(())" Result: 3%run (pa <|> pb <?> justamessage) "c" Result: "b" Correcting steps: D Deleted 'c' at position LineColPos 0 0 0 expecting justamessage; Inserted 'b' at position LineColPos 0 1 1 expecting 'b'>run (amb (pEither parseIntString pIntList)) "(123;456;789)"7 Result: [Left ["123","456","789"],Right [123,456,789]] The fuction r runs the parser and shows both the result, and the correcting steps which were taken during the parsing process. :Our first two parsers are simple; one recognises a single a& character and the other one a single b#. Since we will use them later we ) convert the recognised character into N! so they can be easily combined. VThe applicative style makes it very easy to merge recogition and computing a result. Q As an example we parse a sequence of nested well formed parentheses pairs and * compute the maximum nesting depth with : \It is very easy to recognise infix expressions with any number of priorities and operators:  L operators = [[('+', (+)), ('-', (-))], [('*' , (*))], [('^', (^))]] 9 same_prio ops = msum [ op <$ pSym c | (c, op) <- ops] \ expr = foldr pChainl ( pNatural <|> pParens expr) (map same_prio operators) -- which we can call:   run expr "15-3*5+2^5"   Result: 32 -Note that also here correction takes place:   run expr "2 + + 3 5"  Result: 37  Correcting steps: O Deleted ' ' at position 1 expecting one of ['0'..'9', '^', '*', '-', '+'] @ Deleted ' ' at position 3 expecting one of ['(', '0'..'9'] 2 Inserted '0' at position 4 expecting '0'..'9' @ Deleted ' ' at position 5 expecting one of ['(', '0'..'9'] O Deleted ' ' at position 7 expecting one of ['0'..'9', '^', '*', '-', '+'] UA common case where ambiguity arises is when we e.g. want to recognise identifiers, + but only those which are not keywords.  The combinator $3 inserts steps with a specfied cost in the result 4 of the parser which can be used to disambiguate:    ident :: Parser String ^ ident = ((:) <$> pSym ('a','z') <*> pMunch (\x -> 'a' <= x && x <= 'z') `micro` 2) <* spaces  idents = pList1 ident - pKey keyw = pToken keyw `micro` 1 <* spaces  spaces :: Parser String  spaces = pMunch (==' ') # takes_second_alt = pList ident G \<|> (\ c t e -> ["IfThenElse"] ++ c ++ t ++ e) 8 \<$ pKey "if" <*> pList_ng ident 7 \<* pKey "then" <*> pList_ng ident 9 \<* pKey "else" <*> pList_ng ident &A keyword is followed by a small cost 1, which makes sure that ] identifiers which have a keyword as a prefix win over the keyword. Identifiers are however  followed by a cost 26, with as result that in this case the keyword wins. g Note that a limitation of this approach is that keywords are only recognised as such when expected!  5 test13 = run takes_second_alt "if a then if else c" 6 test14 = run takes_second_alt "ifx a then if else c" with results for test13 and test14: % Result: ["IfThenElse","a","if","c"] - Result: ["ifx","a","then","if", "else","c"] {A mistake which is made quite often is to construct a parser which can recognise a sequence of elements using one of the  derived combinators (say ;B), but where the argument parser can recognise the empty string. r The derived combinators check whether this is the case and terminate the parsing process with an error message:   run (pList spaces) "" - Result: *** Exception: The combinator pList @ requires that it's argument cannot recognise the empty string  run (pMaybe spaces) " " . Result: *** Exception: The combinator pMaybe ? requires that it's argument cannot recognise the empty string $OPQRSTUVWXYZ[\]^_`The  is to be pronounced as stop  The function  is to be pronounced as start gThe idea of the Idiom concept is that sequential composition operators can be inferred from the type  of the various operands =run (iI (+) '(' pNatural "plus" pNatural ')' Ii) "(2 plus 3" Result: 5 Correcting steps: \ Inserted ')' at position LineColPos 0 4 4 expecting one of [')', Whitespace, '0'..'9'] By running the function 6 you will get a demonstration of the merging parsers. Grun ((,,) <$> two pA <||> three pB <||> pBetween 2 4 pC ) "cababbcccc"/ Result: ("aa",("b","b","b"),["c","c","c","c"]) Correcting steps: 9 The token 'c' was not consumed by the parsing process.\run (amb (mkParserM ((,) <$> pmMany ((,) <$> pA <*> pC) <||> pmMany pB))) "aabbcaabbccc" Result: [([("a","c"),("a","c"),("a","c"),("a","c")],["b","b","b","b"]),([("a","c"),("a","c"),("a","c"),("a","c")],["b","b","b","b"]), ([("a","c"),("a","c"),("a","c"),("a","c")],["b","b","b","b"]),([("a","c"),("a","c"),("a","c"),("a","c")],["b","b","b","b"]), ([("a","c"),("a","c"),("a","c"),("a","c")],["b","b","b","b"]),([("a","c"),("a","c"),("a","c"),("a","c")],["b","b","b","b"]), ([("a","c"),("a","c"),("a","c"),("a","c")],["b","b","b","b"]),([("a","c"),("a","c"),("a","c"),("a","c")],["b","b","b","b"]), ([("a","c"),("a","c"),("a","c"),("a","c")],["b","b","b","b"]),([("a","c"),("a","c"),("a","c"),("a","c")],["b","b","b","b"]), ([("a","c"),("a","c"),("a","c"),("a","c")],["b","b","b","b"]),([("a","c"),("a","c"),("a","c"),("a","c")],["b","b","b","b"])]brun (pmMany(pABC)) "a2a1b1b2c2a3b3c1c3" Result: ["2a","1a","3a"]Trun ((,) <$> pBetween 2 3 pA <||> pBetween 1 2 pB) "abba" Result: (["a","a"],["b","b"])Srun ((,) <$> pBetween 2 3 pA <||> pBetween 1 2 pB) "bba" Result: (["a","a"],["b","b"]) Correcting steps: ; Inserted 'a' at position LineColPos 0 3 3 expecting 'a'Trun (amb (mkParserM( ((,) <$> pBetween 2 3 pA <||> pBetween 1 2 pA)))) "aaa"@ Result: [(["a","a"],["a"]),(["a","a"],["a"]),(["a","a"],["a"])]The a/ at the right hand side can b any of the three a-s in the input: Vrun ((,) <$> pAtLeast 3 pA <||> pAtMost 3 pB) "aabbbb"& Result: (["a","a","a"],["b","b","b"]) Correcting steps: ; Deleted 'b' at position LineColPos 0 5 5 expecting 'a'; Inserted 'a' at position LineColPos 0 6 6 expecting 'a'Trun ((,) <$> pSome pA <||> pMany pB) "abba" Result: (["a","a"],["b","b"])Trun ((,) <$> pSome pA <||> pMany pB) "abba" Result: (["a","a"],["b","b"])Prun ((,) <$> pSome pA <||> pMany pB) "" Result: (["a"],[]) Correcting steps: I Inserted 'a' at position LineColPos 0 0 0 expecting one of ['a', 'b']Trun ((,) <$> pMany pB <||> pSome pC) "bcbc" Result: (["b","b"],["c","c"])Trun ((,) <$> pSome pB <||> pMany pC) "bcbc" Result: (["b","b"],["c","c"])Vrun ((,,,) <$> pSome pA <||> pMany pB <||> pC <||> (pNat `opt` 5) ) "bcab45"! Result: (["a"],["b","b"],"c",45)Zrun ((,) <$> pMany (pA <|> pB) <||> pSome pNat) "1ab12aab14"* Result: (["a","b","a","a","b"],[1,12,14])Wrun ( (,) <$> ((++) <$> pMany pA <||> pMany pB) <||> pC) "abcaaab"( Result: (["a","a","a","a","b","b"],"c")Wrun (pc `mkParserS` ((,) <$> pMany pA <||> pMany pB)) "acbcacb" Result: (["a","a"],["b","b"])< recognises two instance of p as part of the input sequence Y recognises two instance of p as part of the input sequence and concatenates the results % minimcs a series of events (here an a, a b and a c*), which belong to the same transaction. W The transaction is identified by a digit: hence a full transaction is a string like "a5b5c5". $ The third element in the body of D below shows how the different transactions can be recovered from h a log-file which contains all events generated by a collection of concurrently running transactions. a b   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_``abcdefgghhiijkllmnopqrstuvwxyz{|}~k        !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`auu-parsinglib-2.7.3.1Text.ParserCombinators.UU.Core!Text.ParserCombinators.UU.Derived)Text.ParserCombinators.UU.MergeAndPermute(Text.ParserCombinators.UU.BasicInstancesText.ParserCombinators.UU.Utils'Text.ParserCombinators.UU.Demo.Examples Text.ParserCombinators.UU.Idioms.Text.ParserCombinators.UU.Demo.MergeAndPermute Text.ParserCombinators.UU.README#Text.ParserCombinators.UU.CHANGELOGText.ParserCombinators.UUNat UnspecifiedInfiniteSuccZeroStringsProgressCostStepsEnd_fEnd_hMicroFailApplyStepP HasPositiongetPos StoresErrors getErrorsIsLocationUpdatedByadvanceEofeof deleteAtEndExtAlternative<<|>doNotInterpretmust_be_non_emptymust_be_non_emptiesoptIsParsergetOnePgetZeroPpSymExtmicroambpErrorspPospEndpSwitchparseparse_hevalpReturnpFailpMaybepEither<$$>pManypSomepPackedpFoldr pFoldr_ngpFoldr1 pFoldr1_nglist_algpListpList_ngpList1 pList1_ng pFoldrSep pFoldrSep_ng pFoldr1Sep pFoldr1Sep_ngpListSep pListSep_ng pList1Sep pList1Sep_ngpChainr pChainr_ngpChainl pChainl_ngpExactpBetweenpAtLeastpAtMostpCountpAnyAltBindSeqGrammkGram<||><<||> mkParserM mkParserSpmMany Insertion LineColPosLineCol ParserTrafoParserStrinputmsgsposdeleteOkError DeletedAtEndReplacedDeletedInsertedshow_expecting createStrpSatisfy pRangeInsertpRange pSymInsertpSympMunch pTokenCostpTokenpCRpLFpLowerpUpperpLetterpAsciipDigit pDigitAsNumpAnySympSpaceslexemepDotpCommapDQuotepLParenpRParen pLBracket pRBracketpLBracepRBracepSymbol pNaturalRaw pIntegerRaw pDoubleRaw pDoubleStrpNaturalpIntegerpDoublepPercentpEnumRawpEnum pEnumStrspParenspBraces pBrackets listParser tupleParserpTuple pDayMonthYearpDaypParentheticalString pQuotedStringparserReadsPrec execParser runParser show_demosrunpapbpc<++>pazwfpexprtest16spaces pManyTillstring pAnyTokenpIntListparseIntStringdemo Idiomatic idiomaticIiString'fromStrORFIELSETHENIFiIpNatGrammarpApBpCtwothreepABCpABC'dummyTbaseControl.Applicative<|>mkParsercombinepStateapplypush apply2fst succeedAlways failAlwaysnoAlts has_successnorm applyFailbestbest' getCheapesttraverseshow' removeEnd_h removeEnd_f getLengthnat_minnat_addtrace'GHC.Basefail>>=>>fmapreturn Control.MonadguardliftMMonadFunctor MonadPlus ApplicativeoptionalliftA3liftA2liftA<**><**><*>puremanysomeempty AlternativegetConstConst unwrapMonad WrapMonad WrappedMonad unwrapArrow WrapArrow WrappedArrow getZipListZipList Data.Functor<$>mfilterapliftM5liftM4liftM3liftM2unlesswhen replicateM_ replicateMfoldM_foldM zipWithM_zipWithM mapAndUnzipMjoinvoidforever<=<>=>msumforM_forMfilterMmapM_mapM sequence_sequence=<<mplusmzero<$ Data.MaybeMaybe$fApplicativeGram $fFunctorGrampMunchL show_tokens show_munch show_symbolMonthDecNovOctSepAugJulJunMayAprMarFebJanpSign pPercentRawGHC.ErrerrorString justamessagerun'liftpa2pa3test11 operators same_prioidentidentspKeytakes_second_alttest13test14 simpleCommentpVarIdpConIdpIdCharpDigit'