import Data.Char import Debug.Trace data Natural = Zero | Succ Natural deriving (Show, Eq) unu = Succ Zero doi = Succ unu --- important: vezi utilizare constructori de date în pattern-matching addNat Zero n = n addNat (Succ m) n = Succ $ addNat m n {- rulați addNat (Succ (Succ doi)) (Succ (Succ (Succ Zero))) addNat unu doi addNat doi Zero == doi :t addNat -} data Triple a b c = T a b c deriving (Show, Eq) first (T x _ _) = x second (T _ x _) = x third (T _ _ x) = x type TripleInt = Triple Int Int Int ---type Parser = [Char] -> (ParsRes Expr, [Char]) --- i - tipul inputului --- r - tipul rezultatului parsării --- Un parser prelucrează o parte din input, rezultând în rezultatul r, --- și lasă restul inputului neprelucrat type Parser i r = [i] -> (ParsRes r, [i]) data ParsRes r = Parsed r -- dacă parsarea a reușit | Failed -- dacă parsarea a eșuat (nu avem un rezultat și -- nu a fost parsat nimic din input) deriving (Show, Eq) fromRes (Parsed res) = res fromRes Failed = undefined -- o expresie aritmetică este fie un număr, fie o operație între alte 2 expresii data Expr = Number Integer | Op Expr Char Expr deriving (Show, Eq) --- construiește un parser care parsează un element din input, dacă acesta respectă funcția dată makeTokenParserF :: (i -> Bool) -> Parser i i --makeTokenParserF f = \(h:t) -> if f h then (Parsed h, t] else (Failed, (h:t)) --sau: makeTokenParserF _ [] = (Failed, []) makeTokenParserF f input@(h:t) = if f h then (Parsed h, t) else (Failed, input) --- este un parser pentru caracterul c tokenParser c = makeTokenParserF (==c) -- tokenParser '(' "(5+3)" --- este un parser care la o aplicare parsează o cifră digitParser = makeTokenParserF isDigit -- :t digitParser --- este un parser pentru operatori --operatorParser = makeTokenParserF (\c -> elem c ['+', '*']) --- sau: operatorParser = makeTokenParserF (flip elem $ ['+', '*']) --sau: operatorParser = tokenParser '*' >|> tokenParser '+' --folosind operatorul definit mai târziu -- :t operatorParser {-- vedeți cum se poate folosi iterate împreună cu parserul pentru cifre take 10 $ iterate (digitParser . snd) $ digitParser "2345" take 20 $ map fst $ iterate (digitParser . snd) $ digitParser $ concatMap show [1..] -} --- produce un nou parser care este concatenarea (compunerea) a două parsere cu rezultate de același tip --- ce rămâne de parsat de la primul parser va fi dat spre parsare celui de-al doilea parser --- rezultatele parsării de la cele două parsere vor fi fuzionate folosind funcția dată (>.>) :: Parser i rA -> Parser i rB -> Parser i (rA, rB) (>.>) p1 p2 = (\(res1, rem1) -> case res1 of -- ne uităm la rezultatul parsării cu p1 Failed -> (Failed, rem1) -- dacă p1 a eșuat, eșuează Parsed r1 -> case p2 rem1 of -- altfel parsăm ce rămâne după parsarea cu p1 (Parsed r2, rem2) -> -- și, dacă reușește, punem rezultatele într-o pereche (Parsed (r1, r2), rem2) (Failed, _) -> (Failed, rem1) -- dacă p2 a eșuat, eșuează ) . p1 {-- vedeți ce efect are concatenarea parserelor: :t digitParser >.> digitParser digitParser >.> digitParser $ "2345" digitParser >.> digitParser >.> digitParser $ "2345" digitParser >.> operatorParser $ "2+5" digitParser >.> operatorParser >.> digitParser $ "2+5" -} --- produce un nou parser, care avansează în input atâta timp cât poate aplica perserul dat --- rezultatele parsărilor individuale sunt adunate într-o listă --- la sfârșit, asamblează lista folosind funcția process (>*>) :: Eq b => Parser a b -> ([b] -> c) -> Parser a c (>*>) parser process = (\results -> --- aplicăm process peste lista rezultatelor, închidem în Parsed, (Parsed $ process $ map (fromRes . fst) $ results, --- și lăsăm ca neprelucrat ce a rămas de la ultima parsare snd $ last results)) . --- vezi și rularea cu iterate de mai sus --- luăm rezultatele atâta timp cât parsarea reușește takeWhile ((/= Failed) . fst) . iterate (parser . snd) . parser {- :t (>*>) digitParser -- folosim read pentru a citi numărul dintr-un șir de caractere care conține un număr -- folosim fromInteger pentru a indica lui read că trebuie să citim un întreg digitParser >*> (fromInteger . read) $ "2345+6" -} --- produce un nou parser unde rezultatul vine din procesarea rezultatului parserului dat (>=>) :: Parser a b -> (b -> c) -> Parser a c (>=>) parser process = (\(res, rem) -> case res of --- dacă parsarea s-a realizat cu succes, Parsed r -> (Parsed $ process r, rem) --- procesăm rezultatul acesteia; otherwise -> (Failed, rem) --- altfel, eșec ) . parser --- parsează un număr și îl închide într-o valoare de tip Expr numberParser = digitParser >*> (fromInteger . read) >=> Number --- produce un nou parser care ia parsarea cu succes dintre rezultatele --- celor două parsere date, pe același input (>|>) :: Parser a b -> Parser a b -> Parser a b (>|>) p1 p2 input = case (res1, res2) of (Failed, Failed) -> (Failed, input) (Parsed _, _) -> r1 _ -> r2 where r1@(res1, _) = p1 input r2@(res2, _) = p2 input --- parsează un operand, care este un număr sau o expresie între paranteze; --- rezultatul parsării este o expresie operandParser = --- este paranteză deschisă, urmată de expresie, urmată de paranteză închisă --- rezultatul parsării va fi (('(', expr), ')'), din care ne interesează doar expresia ((tokenParser '(' >.> exprParser >.> tokenParser ')') >=> (\((_, e), _) -> e)) >|> --- sau este un număr numberParser --- parsează o expresie care este un număr sau o operație între doi operanzi --- rezultatul parsării este o expresie exprParser = ( --- este operand, operator, operand --- rezultatul parsării este de exemplu ((expr, '+'), expr), din care producem --- o unică expresie (operandParser >.> operatorParser >.> operandParser) >=> (\((e1, op), e2)-> Op e1 op e2 )) >|> --- sau este un număr numberParser --- calculează valoarea unei expresii reprezentate ca o valoare de tip Expr compute (Number x) = x compute (Op e1 '+' e2) = compute e1 + compute e2 compute (Op e1 '*' e2) = compute e1 * compute e2 -- exprParser $ "123+((25+3)*5)" -- compute . fromRes . fst . exprParser $ "123+((25+3)*5)"