-- ************************************************************* -- * -- * Trivial eXpression Language (TXL) Interpreter using Happy -- * Variation using Attribute Grammar (AG) formulation -- * Example for G53CMP, lecture 4, October 2018 -- * -- ************************************************************* { module Main where import Data.Char import System.Environment } ---------------------------------------------------------------- -- Parser ---------------------------------------------------------------- -- Happy Attribute Grammar for INTERPRETING TXL programs. -- Attributes defined by semantic attribute equations. %name interpreter %attribute value { Int } %attribute env { Env } %tokentype { Token } %token int { T_Int $$ } ident { T_Id $$ } '+' { T_Plus } '-' { T_Minus } '*' { T_Times } '/' { T_Divide } '(' { T_LeftPar } ')' { T_RightPar } '=' { T_Equal } let { T_Let } in { T_In } %right in %left '+' '-' %left '*' '/' %% txl_program : exp { $$ = $1; $1.env = emptyEnv } exp : exp '+' exp { $$ = $1 + $3; $1.env = $$.env; $3.env = $$.env } | exp '-' exp { $$ = $1 - $3; $1.env = $$.env; $3.env = $$.env } | exp '*' exp { $$ = $1 * $3; $1.env = $$.env; $3.env = $$.env } | exp '/' exp { $$ = $1 `div` $3; $1.env = $$.env; $3.env = $$.env } | int { $$ = $1 } | ident { $$ = $$.env $1 } | '(' exp ')' { $$ = $2; $2.env = $$.env } | let ident '=' exp in exp { $$ = $6; $4.env = $$.env; $6.env = extEnv $2 $4 $$.env } { -- Haskell code for defining token type, AST, scanner, top-level -- functions. More or less verbatim from the original TXL example -- (lecture 2). ---------------------------------------------------------------- -- Identifiers and environment ---------------------------------------------------------------- type Id = String type Env = Id -> Int emptyEnv :: Env emptyEnv = (\_ -> error "undefined variable") extEnv :: Id -> Int -> Env -> Env extEnv i v env = \i' -> if i' == i then v else env i' ---------------------------------------------------------------- -- Token type ---------------------------------------------------------------- data Token = T_Int Int | T_Id Id | T_Plus | T_Minus | T_Times | T_Divide | T_LeftPar | T_RightPar | T_Equal | T_Let | T_In deriving Show ---------------------------------------------------------------- -- Scanner ---------------------------------------------------------------- scanner :: [Char] -> [Token] -- End of input scanner [] = [] -- Drop white space and comments scanner (' ' : cs) = scanner cs scanner ('\n' : cs) = scanner cs scanner ('!' : cs) = scanner (dropWhile (/='\n') cs) -- Scan single character tokens scanner ('+' : cs) = T_Plus : scanner cs scanner ('-' : cs) = T_Minus : scanner cs scanner ('*' : cs) = T_Times : scanner cs scanner ('/' : cs) = T_Divide : scanner cs scanner ('(' : cs) = T_LeftPar : scanner cs scanner (')' : cs) = T_RightPar : scanner cs scanner ('=' : cs) = T_Equal : scanner cs -- Scan literal integers, identifiers, and keywords scanner (c : cs) | isDigit c = T_Int (read (c : takeWhile isDigit cs)) : scanner (dropWhile isDigit cs) | isAlpha c = mkIdOrKwd (c : takeWhile isAlphaNum cs) : scanner (dropWhile isAlphaNum cs) | otherwise = error "Illegal character!" where mkIdOrKwd "let" = T_Let mkIdOrKwd "in" = T_In mkIdOrKwd cs = T_Id cs ---------------------------------------------------------------- -- Utilities ---------------------------------------------------------------- happyError :: [Token] -> a happyError _ = error "Parse error" ---------------------------------------------------------------- -- Main ---------------------------------------------------------------- -- Usage: -- runtxl file.txl Interpret "file.txl" and write result -- to standard output. -- runtxl Interpret standard input and write -- result to standard output. main = do args <- getArgs input <- if null args then getContents else readFile (head args) print ((interpreter . scanner) input) }