Created
January 22, 2019 12:31
-
-
Save jda0/16f302039dfa49ce214ee8d960195ca3 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module Proc where | |
import Prelude hiding (Num, and, lookup, map) | |
import Control.Applicative hiding (empty) | |
import Control.Arrow (second) | |
import Control.Monad | |
import Data.Functor.Identity (Identity) | |
import Data.Map.Lazy | |
import Data.Maybe | |
import Text.Megaparsec hiding (State, parse) | |
import qualified Text.Megaparsec.Lexer as L | |
import Text.Megaparsec.String | |
-- # Revisions | |
{- | |
- V1.2.1 01 May 2017 | |
- Added State call fallback to prevent errors when using undeclared | |
variables known to a previous State. Further improvements - | |
especially reducing the memory footprint of the semantic functions - | |
could be made by replacing `Store` with `State` entirely except in | |
`Block`s. [:323] | |
- `Block`-private `Scope`s are now empty to reduce memory footprint, | |
as only the keys are needed to make a comparison. Further memory | |
footprint reductions could be made by changing the structure of | |
private `Store`s to ([Var], [Pname]) [:369, :377] | |
- Changed the scoping of `proc`s to be performed after all `var`s | |
and `proc`s have been declared to allow recursion and look- | |
forward. [:378 - :372] | |
- Mixed-scope state applicator now falls back on the current state if | |
the child scope cannot be found due to recursion (`Store` is only | |
set in the top-most declaration) [:368, :414] | |
- Removed some unused symbols | |
- TODO: Replace usage of `map` with `fmap` in next version [:398] | |
- TODO: Improve documentation | |
-} | |
-- | |
-- # Grammar | |
{- | |
The grammar for Proc is as follows: | |
I ::= I Ib | Ia | |
Ia ::= a | b | ... | z | _ | |
Ib ::= Ia | 0 | 1 | ... | 9 | |
V ::= I | |
P ::= I | |
A ::= Aa + A | Aa - A | Aa | |
Aa ::= Ab * Aa | Ab | |
Ab ::= N | V | ( A ) | |
B ::= Ba & B | Ba | |
Ba ::= Neg Ba | Bb | |
Bb ::= TRUE | FALSE | ( B ) | A <= A | A = A | |
Dv ::= var V := A ; Dv | E | |
Dp ::= proc P := Sa ; Dp | E | |
Sb ::= if B then S else S | while B do S | begin Dv Dp S end | |
Ss ::= skip | call P | x := A | ( S ) | |
Sa ::= Sb | Ss | |
Sc ::= Sa ; S | |
S ::= Sb | Sc | Sa | |
-} | |
-- | |
-- # Parser | |
-- ## Typedefs | |
type Num = Integer | |
type Var = String | |
type Pname = String | |
type DecV = [(Var, Aexp)] | |
type DecP = [(Pname, Stm)] | |
data Aexp = N Num | V Var | Mult Aexp Aexp | Add Aexp Aexp | |
| Sub Aexp Aexp | |
deriving (Show) | |
data Bexp = TRUE | FALSE | Neg Bexp | And Bexp Bexp | |
| Le Aexp Aexp | Eq Aexp Aexp | |
deriving (Show) | |
data Stm = Skip | Ass Var Aexp | Comp Stm Stm | |
| If Bexp Stm Stm | While Bexp Stm | |
| Block DecV DecP Stm | Call Pname | |
deriving (Show) | |
-- ## Lexer setup | |
spaceConsumer :: ParsecT Dec String Identity () | |
spaceConsumer = L.space sp lc bc where | |
sp = void spaceChar | |
lc = L.skipLineComment "//" | |
bc = L.skipBlockComment "/*" "*/" | |
lexeme :: ParsecT Dec String Identity a | |
-> ParsecT Dec String Identity a | |
lexeme = L.lexeme spaceConsumer | |
symbol :: String -> ParsecT Dec String Identity String | |
symbol' :: String -> ParsecT Dec String Identity String | |
symbol = L.symbol spaceConsumer | |
symbol' = L.symbol' spaceConsumer | |
-- ## Lexeme Parsers | |
-- ### Parentheses | |
parens :: ParsecT Dec String Identity a | |
-> ParsecT Dec String Identity a | |
parens x = lexeme (between (symbol "(") (symbol ")") x) | |
<?> "parens" | |
-- ### Integers | |
num :: Parser Num | |
num = lexeme L.integer <?> "num" | |
-- ### Identity Names | |
-- Form of this function body inspired by | |
-- https://hackage.haskell.org/package/dotenv-0.3.0.3/src/src/Configuration/Dotenv/Parse.hs | |
ident :: ParsecT Dec String | |
Data.Functor.Identity.Identity String | |
ident = liftM2 (:) (letterChar <|> char '_') | |
(many $ try | |
(letterChar <|> char '_' <|> digitChar)) | |
<?> "ident" | |
var :: Parser Var | |
var = lexeme ident <?> "var" | |
pname :: Parser Pname | |
pname = lexeme ident <?> "pname" | |
-- ## Declaration Block Parsers | |
-- ### Variable Declaration Blocks | |
decv :: Parser DecV | |
decv = many (try decv') <?> "decv" where | |
decv' = (,) <$> var' <*> a' <* symbol ";" <?> "decvi" | |
var' = symbol' "var" *> var <* symbol' ":=" | |
<?> "decvv" | |
a' = aexp <?> "decva" | |
-- ### Procedure Declaration Blocks | |
decp :: Parser DecP | |
decp = many (try decp') <?> "decp" where | |
decp' = (,) <$> var' <*> s' <* symbol ";" <?> "decpi" | |
var' = symbol' "proc" *> pname <* symbol' "is" | |
<?> "decpv" | |
s' = stm' <?> "decps" | |
-- ## Expression Parsers | |
-- ### Arithmetic Expressions | |
aexp :: Parser Aexp | |
aexp = aexp0 <?> "aexp" where | |
-- Lowest precedence | |
aexp0 = try (Add <$> aexp1 <* symbol "+" <*> aexp0) | |
<|> try (Sub <$> aexp1 <* symbol "-" <*> aexp0) | |
<|> aexp1 | |
aexp1 = try (Mult <$> aexp2 <* symbol "*" <*> aexp1) | |
<|> aexp2 | |
-- Highest precedence & symbols | |
aexp2 = (N <$> num) | |
<|> (V <$> var) | |
<|> parens aexp | |
-- ### Boolean Expressions | |
bexp :: Parser Bexp | |
bexp = bexp0 <?> "bexp" where | |
-- Lowest precedence | |
bexp0 = try (And <$> bexp1 <* symbol "&" <*> bexp0) | |
<|> bexp1 | |
bexp1 = try (Neg <$ symbol "!" <*> bexp1) | |
<|> bexp2 | |
-- Highest precedence & symbols | |
bexp2 = (TRUE <$ symbol' "true") | |
<|> (FALSE <$ symbol' "false") | |
<|> parens bexp | |
<|> try (Le <$> aexp <* symbol "<=" <*> aexp) | |
<|> try (Eq <$> aexp <* symbol "=" <*> aexp) | |
-- ## Statement Parsers | |
-- ### Block Statements | |
stmb :: Parser Stm | |
stmb = (If <$ symbol' "if" <*> bexp | |
<* symbol' "then" <*> stm | |
<* symbol' "else" <*> stm | |
<?> "if") | |
<|> (While <$ symbol' "while" <*> bexp | |
<* symbol' "do" <*> stm | |
<?> "while") | |
<|> (Block <$ symbol' "begin" <*> decv | |
<*> decp <*> stm | |
<* symbol' "end" | |
<?> "block") | |
-- ### Inline Statements | |
stma :: Parser Stm | |
stma = (Skip <$ symbol' "skip" | |
<?> "skip") | |
<|> (Call <$ symbol' "call" <*> pname | |
<?> "call") | |
<|> (try (Ass <$> var <* symbol ":=" <*> aexp) | |
<?> "ass") | |
<|> parens stm <?> "parens'" | |
-- ### Single Statements | |
stm' :: Parser Stm | |
stm' = stmb <|> stma | |
-- ### Composite Statements | |
comp :: Parser Stm | |
comp = try (Comp <$> stm' <* symbol ";" | |
<*> stm) | |
<?> "comp" | |
-- ### General Statements | |
stm :: Parser Stm | |
stm = stmb <|> comp <|> stma | |
-- ### Outermost Statement Parser | |
prog :: Parser Stm | |
prog = spaceConsumer *> stm | |
-- ## Parser Applicators | |
parse :: String -> Stm | |
parse = fromJust . parseMaybe prog | |
parseDebug :: String -> IO () | |
parseDebug x = case runParser prog "" x of | |
Left err -> putStr (parseErrorPretty err) | |
Right f -> print f | |
-- | |
-- # Semantic Functions | |
-- ## Typedefs | |
{-# ANN module "HLint: ignore Use camelCase" #-} | |
type T = Bool | |
type Z = Integer | |
type State = Var -> Z | |
data Scope = Scope (Map Pname (Stm, Maybe Scope)) | |
(Map Var (Maybe Z)) State | |
-- ## Expression Evaluation | |
-- ### Arithmetic Expressions | |
aexpEval :: Aexp -> Scope -> Maybe Z | |
aexpEval = aee where | |
aee (N a) _ = Just a | |
aee (V a ) s = svLookup s a | |
aee (Mult a b) s = f' (*) a b s | |
aee (Add a b) s = f' (+) a b s | |
aee (Sub a b) s = f' (-) a b s | |
f' g a b s = pure g <*> aee a s <*> aee b s | |
-- ### Boolean Expressions | |
bexpEval :: Bexp -> Scope -> T | |
bexpEval = bee where | |
bee TRUE _ = True | |
bee FALSE _ = False | |
bee (And a b) s = bee a s && bee b s | |
bee (Neg a ) s = not $ bee a s | |
bee (Le a b) s = justAee a s <= justAee b s | |
bee (Eq a b) s = justAee a s == justAee b s | |
justAee a = fromJust . aexpEval a | |
-- ## Scope Manipulation | |
-- ### Scope Lookup | |
svLookup :: Scope -> Var -> Maybe Z | |
spLookup :: Scope -> Pname -> (Stm, Maybe Scope) | |
svLookup (Scope _ vs s) v = fromMaybe (Just $ s v) $ lookup v vs | |
spLookup (Scope ps _ _) = (!) ps | |
-- ### Insert Procedures | |
-- ### Insert Variables | |
-- ## Generic State Applicator | |
s_generic :: (Stm -> Scope -> Scope) | |
-> Stm -> State -> State | |
s_generic a f s v = fromJust $ svLookup (g' a f s v) v where | |
g' :: (Stm -> Scope -> Scope) | |
-> Stm -> State -> Var -> Scope | |
g' a' f' s' v' = a' f' (Scope empty | |
(singleton v' . Just . s' $ v') s') | |
-- ## Default Scope Applicator | |
siDefault :: (Stm -> Scope -> Scope) -> Stm -> Scope -> | |
Scope | |
siDefault _ Skip _ = Scope empty empty $ const 0 | |
siDefault _ (Ass v a) | |
(Scope ps vs s) = Scope ps | |
(insert v (aexpEval a (Scope ps vs s)) vs) s | |
siDefault i (Comp a b) s = i b (i a s) | |
siDefault i (If c f g) s = i (if bexpEval c s then f else g) s | |
siDefault i (While c f) s = if bexpEval c s | |
then i (While c f) (i f s) | |
else s | |
siDefault i (Block v p f) | |
s@(Scope ps vs sc) = | |
let addPs :: DecP -> (Scope, Scope) -> (Scope, Scope) | |
addPs [] ss = ss | |
addPs ((q, qf) : qs) | |
(Scope po vo so, | |
Scope pp vp sp) = addPs qs | |
(Scope (insert q (qf, Nothing) po) vo so, | |
Scope (insert q (Skip, Nothing) pp) vp sp) | |
addVs :: DecV -> (Scope, Scope) -> (Scope, Scope) | |
addVs [] ss = ss | |
addVs ((u, ua) : us) | |
(so@(Scope po vo to), | |
Scope pp vp tp) = addVs us (Scope po | |
(insert u (aexpEval ua so) vo) to, | |
Scope pp | |
(insert u Nothing vp) tp) | |
rescopePs :: (Scope, Scope) -> (Scope, Scope) | |
rescopePs | |
(so@(Scope po vo to), | |
sp) = (Scope (setScope so po) vo to, sp) | |
setScope ss = map (second (Just . fromMaybe ss)) | |
sa :: (Scope, Scope) | |
sa = rescopePs . | |
addPs p $ addVs v | |
(s, Scope empty empty $ const 0) | |
(Scope pps pvs _) = snd sa | |
(Scope fp fv _) = i f $ fst sa | |
in Scope (difference fp pps `union` ps) | |
(difference fv pvs `union` vs) sc | |
siDefault _ _ _ = undefined | |
-- ## Dynamic-scope State Applicator | |
s_dynamic :: Stm -> State -> State | |
s_dynamic = s_generic d' where | |
d' :: Stm -> Scope -> Scope | |
d' (Call p) s = let (p', _) = spLookup s p | |
in d' p' s | |
d' f s = siDefault d' f s | |
-- ## Mixed-scope State Applicator | |
s_mixed :: Stm -> State -> State | |
s_mixed = s_generic m' where | |
m' :: Stm -> Scope -> Scope | |
m' (Call p) | |
s@(Scope _ vs sc) = let (p', s') = spLookup s p | |
(Scope ps' _ _) = fromMaybe s s' | |
in m' p' (Scope ps' vs sc) | |
m' f s = siDefault m' f s | |
-- ## Static-scope State Applicator | |
s_static :: Stm -> State -> State | |
s_static = s_generic s' where | |
s' (Call _) s = s | |
s' f s = siDefault s' f s | |
-- |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment