Give tock its own repo -- i.e. remove everything else and move tock up

This commit is contained in:
Adam Sampson 2007-07-16 21:48:55 +00:00
parent bf1a0392d5
commit 50731d0b75
157 changed files with 0 additions and 5484 deletions

View File

View File

View File

View File

View File

@ -1,199 +0,0 @@
-- Data types for occam abstract syntax
-- This is intended to be imported qualified:
-- import qualified AST as O
module AST where
import Data.Generics
import Metadata
data Name = Name Meta String
deriving (Show, Eq, Typeable, Data)
data Tag = Tag Meta String
deriving (Show, Eq, Typeable, Data)
data Type =
Bool
| Byte
| Int | Int16 | Int32 | Int64
| Real32 | Real64
| Array Expression Type
| ArrayUnsized Type
| UserType Name
| Chan Type
| Counted Type Type
| Any
| Timer
| Port Type
| Val Type
| Infer -- for where the type is not given but can be worked out (e.g. "x IS y:")
deriving (Show, Eq, Typeable, Data)
data ConversionMode =
DefaultConversion
| Round
| Trunc
deriving (Show, Eq, Typeable, Data)
data Subscript =
Subscript Meta Expression
| SubscriptTag Meta Tag
| SubFromFor Meta Expression Expression
| SubFrom Meta Expression
| SubFor Meta Expression
deriving (Show, Eq, Typeable, Data)
data LiteralRepr =
RealLiteral Meta String
| IntLiteral Meta String
| HexLiteral Meta String
| ByteLiteral Meta String
| StringLiteral Meta String
| ArrayLiteral Meta [Expression]
deriving (Show, Eq, Typeable, Data)
data Literal =
Literal Meta Type LiteralRepr
| SubscriptedLiteral Meta Subscript Literal
deriving (Show, Eq, Typeable, Data)
data Variable =
Variable Meta Name
| SubscriptedVariable Meta Subscript Variable
deriving (Show, Eq, Typeable, Data)
data Expression =
Monadic Meta MonadicOp Expression
| Dyadic Meta DyadicOp Expression Expression
| MostPos Meta Type
| MostNeg Meta Type
| Size Meta Type
| Conversion Meta ConversionMode Type Expression
| ExprVariable Meta Variable
| ExprLiteral Meta Literal
| True Meta
| False Meta
| FunctionCall Meta Name [Expression]
| BytesInType Meta Type
| OffsetOf Meta Type Tag
deriving (Show, Eq, Typeable, Data)
data ExpressionList =
FunctionCallList Meta Name [Expression]
| ExpressionList Meta [Expression]
deriving (Show, Eq, Typeable, Data)
data MonadicOp =
MonadicBytesIn
| MonadicSubtr
| MonadicBitNot
| MonadicNot
| MonadicSize
deriving (Show, Eq, Typeable, Data)
data DyadicOp =
Add | Subtr | Mul | Div | Rem
| Plus | Minus | Times
| BitAnd | BitOr | BitXor
| And | Or
| Eq | NotEq | Less | More | LessEq | MoreEq
| After
deriving (Show, Eq, Typeable, Data)
data InputItem =
InCounted Meta Variable Variable
| InVariable Meta Variable
deriving (Show, Eq, Typeable, Data)
data OutputItem =
OutCounted Meta Expression Expression
| OutExpression Meta Expression
deriving (Show, Eq, Typeable, Data)
data Replicator = For Meta Name Expression Expression
deriving (Show, Eq, Typeable, Data)
data Choice = Choice Meta Expression Process
deriving (Show, Eq, Typeable, Data)
data Alternative =
Alternative Meta Variable InputMode Process
| AlternativeCond Meta Expression Variable InputMode Process
| AlternativeSkip Meta Expression Process
deriving (Show, Eq, Typeable, Data)
data Option =
Option Meta [Expression] Process
| Else Meta Process
deriving (Show, Eq, Typeable, Data)
data Variant = Variant Meta Tag [InputItem] Process
deriving (Show, Eq, Typeable, Data)
-- This represents something that can contain local replicators and specifications.
-- (This ought to be a parametric type, "Structured Variant" etc., but doing so
-- makes using generic functions across it hard.)
data Structured =
Rep Meta Replicator Structured
| Spec Meta Specification Structured
| OnlyV Meta Variant
| OnlyC Meta Choice
| OnlyO Meta Option
| OnlyP Meta Process
| OnlyA Meta Alternative
| Several Meta [Structured]
deriving (Show, Eq, Typeable, Data)
data InputMode =
InputSimple Meta [InputItem]
| InputCase Meta Structured
| InputAfter Meta Expression
deriving (Show, Eq, Typeable, Data)
type Formals = [(Type, Name)]
type Specification = (Name, SpecType)
data SpecType =
Place Meta Expression
| Declaration Meta Type
| Is Meta Type Variable
| ValIs Meta Type Expression
| DataTypeIs Meta Type
| DataTypeRecord Meta Bool [(Type, Tag)]
| ProtocolIs Meta [Type]
| ProtocolCase Meta [(Tag, [Type])]
| Proc Meta Formals Process
| Function Meta [Type] Formals ValueProcess
| Retypes Meta Type Variable
| Reshapes Meta Type Variable
| ValRetypes Meta Type Variable
| ValReshapes Meta Type Variable
deriving (Show, Eq, Typeable, Data)
data ValueProcess =
ValOfSpec Meta Specification ValueProcess
| ValOf Meta Process ExpressionList
deriving (Show, Eq, Typeable, Data)
data Process =
ProcSpec Meta Specification Process
| Assign Meta [Variable] ExpressionList
| Input Meta Variable InputMode
| Output Meta Variable [OutputItem]
| OutputCase Meta Variable Tag [OutputItem]
| Skip Meta
| Stop Meta
| Main Meta
| Seq Meta [Process]
| SeqRep Meta Replicator Process
| If Meta Structured
| Case Meta Expression Structured
| While Meta Expression Process
| Par Meta Bool [Process]
| ParRep Meta Bool Replicator Process
| PlacedPar Meta Structured
| Processor Meta Expression Process
| Alt Meta Bool Structured
| ProcCall Meta Name [Expression]
deriving (Show, Eq, Typeable, Data)

View File

@ -1,125 +0,0 @@
-- Passes across the AST
module ASTPasses (astPasses) where
import List
import Data.Generics
import Control.Monad.State
import Metadata
import qualified AST as A
{- FIXME: Passes to add:
calculate types
fix Infer types
resolve "c ! x; y" ambiguity (is x tag or variable?)
resolve "x[y]" ambiguity (is y expression or tag?)
check types
add reference markers where appropriate
turn inline VALOFs into regular FUNCTIONs
identify free variables
rewrite PROC/FUNCTION declarations and uses to take free variables as parameters
make Names unique
make Names C-styled
mark Tags with their associated types
extract type/PROC/FUNCTION declarations
check only Main is left
-}
astPasses =
[ ("Unique names", uniqueNamesPass)
, ("C-style names", cStyleNamesPass)
]
type UniqueState = (Int, [(String, String)])
type UniqueM t = State UniqueState t
uniqueNamesPass :: A.Process -> A.Process
uniqueNamesPass p = evalState (doAny p) (0, [])
where
doAny :: Data t => t -> UniqueM t
doAny = doGeneric `extM` doName `extM` doProcess `extM` doValueProcess `extM` doStructured
doGeneric :: Data t => t -> UniqueM t
doGeneric = gmapM doAny
withNames :: Data t => [A.Name] -> t -> UniqueM ([A.Name], t)
withNames ns b = do
(count, vars) <- get
let (ms, names) = unzip [(m, s) | A.Name m s <- ns]
let names' = [n ++ "." ++ show (count + i) | (n, i) <- zip names [0..]]
put (count + length ns, (zip names names') ++ vars)
b' <- doAny b
(count', _) <- get
put (count', vars)
return ([A.Name m n | (m, n) <- zip ms names'], b')
withName :: Data t => A.Name -> t -> UniqueM (A.Name, t)
withName n b = do
(n':[], b') <- withNames [n] b
return (n', b')
withFormals :: Data t => A.Formals -> t -> UniqueM (A.Formals, t)
withFormals fs b = do
(fns', b') <- withNames (map snd fs) b
ts' <- mapM doAny (map fst fs)
return (zip ts' fns', b')
withSpec :: Data t => A.Specification -> t -> UniqueM (A.Specification, t)
withSpec (n, st) b = do
st' <- case st of
A.Proc m fs pp -> do (fs', pp') <- withFormals fs pp
return $ A.Proc m fs' pp'
A.Function m rt fs pp -> do (fs', pp') <- withFormals fs pp
return $ A.Function m rt fs' pp'
otherwise -> doAny st
(n', b') <- withName n b
return ((n', st'), b')
withRep :: Data t => A.Replicator -> t -> UniqueM (A.Replicator, t)
withRep (A.For m n f1 f2) b = do
(n', b') <- withName n b
f1' <- doAny f1
f2' <- doAny f2
return $ (A.For m n' f1' f2', b')
doProcess :: A.Process -> UniqueM A.Process
doProcess p = case p of
A.ProcSpec m s b -> do (s', b') <- withSpec s b
return $ A.ProcSpec m s' b'
A.SeqRep m r b -> do (r', b') <- withRep r b
return $ A.SeqRep m r' b'
A.ParRep m pri r b -> do (r', b') <- withRep r b
return $ A.ParRep m pri r' b'
otherwise -> doGeneric p
doValueProcess :: A.ValueProcess -> UniqueM A.ValueProcess
doValueProcess p = case p of
A.ValOfSpec m s b -> do (s', b') <- withSpec s b
return $ A.ValOfSpec m s' b'
otherwise -> doGeneric p
doStructured :: A.Structured -> UniqueM A.Structured
doStructured p = case p of
A.Rep m r b -> do (r', b') <- withRep r b
return $ A.Rep m r' b'
A.Spec m s b -> do (s', b') <- withSpec s b
return $ A.Spec m s' b'
otherwise -> doGeneric p
doName :: A.Name -> UniqueM A.Name
doName (A.Name m s) = do
(_, vars) <- get
let s' = case lookup s vars of
Just n -> n
Nothing -> dieP m $ "Name " ++ s ++ " not declared before use"
return $ A.Name m s'
cStyleNamesPass :: A.Process -> A.Process
cStyleNamesPass = everywhere (mkT doName)
where
doName :: A.Name -> A.Name
doName (A.Name m s) = A.Name m [if c == '.' then '_' else c | c <- s]

View File

@ -1,131 +0,0 @@
-- Write C code
module COutput where
import List
import Data.Generics
import Metadata
import qualified AST as A
concatWith x l = concat $ intersperse x l
bracketed s = "(" ++ s ++ ")"
unimp :: Data a => a -> String
unimp = unimpG `extQ` unimpS `extQ` unimpM
where
unimpG :: Data a => a -> String
unimpG t = rep
where
ctr = showConstr $ toConstr t
items = gmapQ unimp t
rep = "(" ++ ctr ++ concat [' ' : s | s <- items] ++ ")"
unimpS :: String -> String
unimpS s = show s
unimpM :: Meta -> String
unimpM m = formatSourcePos m
writeC :: A.Process -> String
writeC p = header ++ doProcess p
where
header = "#include <stdint.h>\n"
doName :: A.Name -> String
doName (A.Name _ n) = n
doUserType :: A.Type -> String
doUserType (A.UserType (A.Name _ n)) = "usertype_" ++ n
doType :: A.Type -> String
doType (A.Val t) = "const " ++ (doType t)
doType A.Bool = "int8_t"
doType A.Byte = "uint8_t"
doType A.Int = "int32_t"
doType A.Int16 = "int16_t"
doType A.Int32 = "int32_t"
doType A.Int64 = "int64_t"
doType A.Real32 = "float"
doType A.Real64 = "double"
doType u@(A.UserType _) = doUserType u
doType t = unimp t
doVariable :: A.Variable -> String
doVariable (A.Variable _ n) = doName n
doLiteralRepr :: A.LiteralRepr -> String
doLiteralRepr r = case r of
A.IntLiteral _ s -> s
doLiteral :: A.Literal -> String
doLiteral (A.Literal _ t r) = doLiteralRepr r
doFunction :: A.ValueProcess -> String
doFunction (A.ValOfSpec _ s p) = doSpecification s ++ doFunction p
doFunction (A.ValOf _ p el) = doProcess p ++ "return " ++ doExpressionListOne el ++ ";\n"
-- FIXME handle multi-value return
makeDecl :: A.Type -> A.Name -> String
makeDecl t n = doType t ++ " " ++ doName n
makeFormals :: [(A.Type, A.Name)] -> String
makeFormals fs = "(" ++ concatWith ", " [makeDecl t n | (t, n) <- fs] ++ ")"
doSpecification :: A.Specification -> String
doSpecification s@(n, st) = case st of
A.Declaration _ t -> makeDecl t n ++ ";\n"
A.Proc _ fs p -> "void " ++ doName n ++ " " ++ makeFormals fs ++ " {\n" ++ doProcess p ++ "}\n"
A.Function _ [r] fs vp -> doType r ++ " " ++ doName n ++ " " ++ makeFormals fs ++ " {\n" ++ doFunction vp ++ "}\n"
_ -> unimp s
doProcSpec :: A.Process -> String
doProcSpec p = doP [] p
where
doP :: [A.Specification] -> A.Process -> String
doP ss (A.ProcSpec _ s p) = doP (ss ++ [s]) p
doP ss p = "{\n" ++ concat (map doSpecification ss) ++ doProcess p ++ "}\n"
doActuals :: [A.Expression] -> String
doActuals es = "(" ++ concatWith ", " (map doExpression es) ++ ")"
doFunctionCall :: A.Name -> [A.Expression] -> String
doFunctionCall n as = (doName n) ++ " " ++ doActuals as
doMonadic :: A.MonadicOp -> A.Expression -> String
doMonadic o a = case o of
A.MonadicSubtr -> "-" ++ doExpression a
doDyadic :: A.DyadicOp -> A.Expression -> A.Expression -> String
doDyadic o a b = bracketed $ case o of
-- FIXME Ops ought to be runtime-checked using inline functions
A.Add -> doExpression a ++ " + " ++ doExpression b
A.Subtr -> doExpression a ++ " - " ++ doExpression b
A.Mul -> doExpression a ++ " * " ++ doExpression b
A.Div -> doExpression a ++ " / " ++ doExpression b
doExpression :: A.Expression -> String
doExpression e = case e of
A.Monadic _ o a -> doMonadic o a
A.Dyadic _ o a b -> doDyadic o a b
A.ExprVariable _ v -> doVariable v
A.ExprLiteral _ l -> doLiteral l
doExpressionListOne :: A.ExpressionList -> String
doExpressionListOne e = case e of
A.FunctionCallList _ n as -> doFunctionCall n as
A.ExpressionList _ [e] -> doExpression e
doAssign :: A.Process -> String
doAssign a = case a of
A.Assign _ [v] el -> (doVariable v) ++ " = " ++ (doExpressionListOne el) ++ ";\n"
doProcess :: A.Process -> String
doProcess s@(A.ProcSpec _ _ _) = doProcSpec s
doProcess a@(A.Assign _ _ _) = doAssign a
doProcess (A.Skip _) = "/* SKIP */;\n"
doProcess (A.Stop _) = "SetErr ();\n"
doProcess (A.Main _) = "/* MAIN-PROCESS */\n";
doProcess (A.Seq _ ps) = concatWith "" (map doProcess ps)
doProcess (A.ProcCall _ n as) = doName n ++ " " ++ doActuals as ++ ";\n"
doProcess n = unimp n

View File

@ -1,87 +0,0 @@
-- Driver for FCO
module Main where
import List
import System
import System.Console.GetOpt
import System.IO
import PrettyShow
import Parse
import SExpression
import Pass
import PTPasses
import PTToAST
import ASTPasses
import COutput
data Flag = ParseOnly | SOccamOnly | Verbose
deriving (Eq, Show)
options :: [OptDescr Flag]
options =
[ Option [] ["parse-tree"] (NoArg ParseOnly) "parse input files and output S-expression parse tree"
, Option [] ["soccam"] (NoArg SOccamOnly) "parse input files and output soccam"
, Option ['v'] ["verbose"] (NoArg Verbose) "show more detail about what's going on"
]
getOpts :: [String] -> IO ([Flag], [String])
getOpts argv =
case getOpt RequireOrder options argv of
(o,n,[] ) -> return (o,n)
(_,_,errs) -> error (concat errs ++ usageInfo header options)
where header = "Usage: fco [OPTION...] SOURCEFILE"
numberedListing :: String -> String
numberedListing s = concat $ intersperse "\n" $ [(show n) ++ ": " ++ s | (n, s) <- zip [1..] (lines s)]
main :: IO ()
main = do
argv <- getArgs
(opts, args) <- getOpts argv
let fn = case args of
[fn] -> fn
_ -> error "Must specify a single input file"
let progress = if Verbose `elem` opts then hPutStrLn stderr else (\s -> return ())
progress $ "Options: " ++ (show opts)
progress $ "Compiling " ++ fn
progress ""
progress "{{{ Preprocessor"
preprocessed <- readSource fn
progress $ numberedListing preprocessed
progress "}}}"
progress "{{{ Parser"
let pt = parseSource preprocessed fn
progress $ pshow pt
progress "}}}"
if ParseOnly `elem` opts then do
putStrLn $ show (nodeToSExp pt)
else if SOccamOnly `elem` opts then do
putStrLn $ show (nodeToSOccam pt)
else do
progress "{{{ PT passes"
pt' <- runPasses ptPasses progress pt
progress "}}}"
progress "{{{ PT to AST"
let ast = ptToAST pt'
progress $ pshow ast
progress "}}}"
progress "{{{ AST passes"
ast' <- runPasses astPasses progress ast
progress "}}}"
progress "{{{ C output"
putStr $ writeC ast'
progress "}}}"
progress "Done"

View File

@ -1,38 +0,0 @@
targets = fco
all: $(targets)
sources = \
AST.hs \
ASTPasses.hs \
COutput.hs \
Main.hs \
Metadata.hs \
Parse.hs \
Pass.hs \
PrettyShow.hs \
PT.hs \
PTPasses.hs \
SExpression.hs \
PTToAST.hs
$(targets): $(sources)
ghc -fglasgow-exts -o fco --make Main
tests = $(wildcard testcases/*.occ)
test: $(targets) $(tests)
@set -e; for x in $(tests); do \
echo -n "$$x: " ; \
if ! ./fco --parse-tree $$x >$$x.log 2>&1 ; then \
echo "parse failed" ; \
elif ! ./fco -v $$x >$$x.log 2>&1 ; then \
echo "full failed" ; \
else \
echo "ok" ; \
fi ; \
done
clean:
rm -f $(targets) *.o *.hi

View File

@ -1,30 +0,0 @@
-- Metadata types
module Metadata where
import Data.Generics
import Data.List
type Meta = [Metadatum]
data Metadatum =
SourcePos String Int Int
deriving (Show, Eq, Typeable, Data)
findSourcePos :: Meta -> Maybe Metadatum
findSourcePos ms = find (\x -> case x of SourcePos _ _ _ -> True
otherwise -> False) ms
formatSourcePos :: Meta -> String
formatSourcePos m = case findSourcePos m of
Just (SourcePos f l c) -> "<@" ++ show l ++ ":" ++ show c ++ ">"
Nothing -> "<?>"
die :: Monad m => String -> m a
die s = error $ "error: " ++ s
dieP :: Monad m => Meta -> String -> m a
dieP m s = case findSourcePos m of
Just (SourcePos f l c) -> die $ f ++ ":" ++ (show l) ++ ":" ++ (show c) ++ ": " ++ s
Nothing -> die $ "unknown position: " ++ s

159
fco/PT.hs
View File

@ -1,159 +0,0 @@
-- occam parse tree
-- This is intended to be imported qualified:
-- import qualified PT as N
module PT where
import Data.Generics
import Metadata
data Node = Node Meta NodeType
deriving (Show, Eq, Typeable, Data)
data NodeType =
Decl Node Node
| Alt [Node]
| AltRep Node Node
| PriAlt [Node]
| PriAltRep Node Node
| In Node Node
| InSimple [Node]
-- e.g. In (Name "c") (InCase [Variant .., Variant ..])
| Variant Node Node
| InCase [Node]
| InTag Node
| InAfter Node
| Out Node [Node]
| OutCase Node Node [Node]
| ExpList [Node]
| Assign [Node] Node
| If [Node]
| IfRep Node Node
| While Node Node
| Par [Node]
| ParRep Node Node
| PriPar [Node]
| PriParRep Node Node
| PlacedPar [Node]
| PlacedParRep Node Node
| Processor Node Node
| Skip
| Stop
| Case Node [Node]
| Seq [Node]
| SeqRep Node Node
| ProcCall Node [Node]
| MainProcess
| Vars Node [Node]
| Is Node Node
| IsType Node Node Node
| ValIs Node Node
| ValIsType Node Node Node
| Place Node Node
| DataType Node Node
| Record [Node]
| PackedRecord [Node]
| Fields Node [Node]
| Protocol Node [Node]
| TaggedProtocol Node [Node]
| Tag Node [Node]
-- e.g. Proc (Name "out.string") [Formals Int [Name "x", Name "y"], Formal Bool [Name "z"]]
| Formals Node [Node]
| Proc Node [Node] Node
| Func Node [Node] [Node] Node
| FuncIs Node [Node] [Node] Node
| Retypes Node Node Node
| ValRetypes Node Node Node
| Reshapes Node Node Node
| ValReshapes Node Node Node
| ValOf Node Node
| Sub Node Node
| SubPlain Node
| SubFromFor Node Node
| SubFrom Node
| SubFor Node
| CaseExps [Node] Node
| Else Node
| For Node Node Node
| Conv Node Node
| Round Node Node
| Trunc Node Node
| DyadicOp Node Node Node
| Add
| Subtr
| Mul
| Div
| Rem
| Plus
| Minus
| Times
| BitAnd
| BitOr
| BitXor
| And
| Or
| Eq
| NEq
| Less
| More
| LessEq
| MoreEq
| After
| MonadicOp Node Node
| MonSub
| MonBitNot
| MonNot
| MonSize
| MostPos Node
| MostNeg Node
| Size Node
| Call Node [Node]
| BytesIn Node
| OffsetOf Node Node
| Guard Node Node
| CondGuard Node Node
| Choice Node Node
| Val Node
| ChanOf Node
| PortOf Node
| Timer
| Array Node Node
| ArrayUnsized Node
| Counted Node Node
| Bool
| Byte
| Int
| Int16
| Int32
| Int64
| Real32
| Real64
| Any
| TypedLit Node Node
| LitReal String
| LitInt String
| LitHex String
| LitByte String
| LitString String
| LitArray [Node]
| True
| False
| Name String
deriving (Show, Eq, Typeable, Data)

View File

@ -1,8 +0,0 @@
-- Passes across the parse tree
module PTPasses (ptPasses) where
import qualified PT as P
ptPasses = []

View File

@ -1,252 +0,0 @@
-- Convert the parse tree into the AST
module PTToAST (ptToAST) where
import Metadata
import qualified PT as N
import qualified AST as O
doName :: N.Node -> O.Name
doName (N.Node m (N.Name s)) = O.Name m s
doName n = error $ "Failed to translate to Name: " ++ (show n)
doTag :: N.Node -> O.Tag
doTag (N.Node m (N.Name s)) = O.Tag m s
doType :: N.Node -> O.Type
doType n@(N.Node _ nt) = case nt of
N.Bool -> O.Bool
N.Byte -> O.Byte
N.Int -> O.Int
N.Int16 -> O.Int16
N.Int32 -> O.Int32
N.Int64 -> O.Int64
N.Real32 -> O.Real32
N.Real64 -> O.Real64
N.Array e t -> O.Array (doExpression e) (doType t)
N.ArrayUnsized t -> O.ArrayUnsized (doType t)
N.Name _ -> O.UserType (doName n)
N.ChanOf t -> O.Chan (doType t)
N.Counted ct dt -> O.Counted (doType ct) (doType dt)
N.Any -> O.Any
N.Timer -> O.Timer
N.PortOf t -> O.Port (doType t)
N.Val t -> O.Val (doType t)
doMonadicOp :: N.Node -> O.MonadicOp
doMonadicOp n@(N.Node _ nt) = case nt of
N.MonSub -> O.MonadicSubtr
N.MonBitNot -> O.MonadicBitNot
N.MonNot -> O.MonadicNot
N.MonSize -> O.MonadicSize
doDyadicOp :: N.Node -> O.DyadicOp
doDyadicOp n@(N.Node _ nt) = case nt of
N.Add -> O.Add
N.Subtr -> O.Subtr
N.Mul -> O.Mul
N.Div -> O.Div
N.Rem -> O.Rem
N.Plus -> O.Plus
N.Minus -> O.Minus
N.Times -> O.Times
N.BitAnd -> O.BitAnd
N.BitOr -> O.BitOr
N.BitXor -> O.BitXor
N.And -> O.And
N.Or -> O.Or
N.Eq -> O.Eq
N.NEq -> O.NotEq
N.Less -> O.Less
N.More -> O.More
N.LessEq -> O.LessEq
N.MoreEq -> O.MoreEq
N.After -> O.After
doSubscript :: N.Node -> O.Subscript
doSubscript n@(N.Node m nt) = case nt of
N.SubPlain e -> O.Subscript m (doExpression e)
N.SubFromFor e f -> O.SubFromFor m (doExpression e) (doExpression f)
N.SubFrom e -> O.SubFrom m (doExpression e)
N.SubFor f -> O.SubFor m (doExpression f)
doLiteral :: N.Node -> O.Literal
doLiteral n@(N.Node m nt) = case nt of
N.TypedLit t l -> O.Literal m (doType t) rep where (O.Literal _ _ rep) = doLiteral l
N.LitReal s -> O.Literal m O.Real32 (O.RealLiteral m s)
N.LitInt s -> O.Literal m O.Int (O.IntLiteral m s)
N.LitHex s -> O.Literal m O.Int (O.HexLiteral m s)
N.LitByte s -> O.Literal m O.Byte (O.ByteLiteral m s)
N.LitString s -> O.Literal m (O.ArrayUnsized O.Byte) (O.StringLiteral m s)
N.LitArray ns -> O.Literal m O.Infer (O.ArrayLiteral m (map doExpression ns))
N.Sub s l -> O.SubscriptedLiteral m (doSubscript s) (doLiteral l)
doVariable :: N.Node -> O.Variable
doVariable n@(N.Node m nt) = case nt of
N.Name _ -> O.Variable m (doName n)
N.Sub s v -> O.SubscriptedVariable m (doSubscript s) (doVariable v)
_ -> error $ "Failed to translate to Variable: " ++ (show n)
doExpression :: N.Node -> O.Expression
doExpression n@(N.Node m nt) = case nt of
N.MonadicOp o a -> O.Monadic m (doMonadicOp o) (doExpression a)
N.DyadicOp o a b -> O.Dyadic m (doDyadicOp o) (doExpression a) (doExpression b)
N.MostPos t -> O.MostPos m (doType t)
N.MostNeg t -> O.MostNeg m (doType t)
N.Size t -> O.Size m (doType t)
N.Conv t e -> O.Conversion m O.DefaultConversion (doType t) (doExpression e)
N.Round t e -> O.Conversion m O.Round (doType t) (doExpression e)
N.Trunc t e -> O.Conversion m O.Trunc (doType t) (doExpression e)
N.TypedLit _ _ -> O.ExprLiteral m $ doLiteral n
N.LitReal _ -> O.ExprLiteral m $ doLiteral n
N.LitInt _ -> O.ExprLiteral m $ doLiteral n
N.LitHex _ -> O.ExprLiteral m $ doLiteral n
N.LitByte _ -> O.ExprLiteral m $ doLiteral n
N.LitString _ -> O.ExprLiteral m $ doLiteral n
N.LitArray _ -> O.ExprLiteral m $ doLiteral n
N.True -> O.True m
N.False -> O.False m
N.Call f es -> O.FunctionCall m (doName f) (map doExpression es)
N.BytesIn t -> O.BytesInType m (doType t)
N.OffsetOf t g -> O.OffsetOf m (doType t) (doTag g)
otherwise -> O.ExprVariable m (doVariable n)
doExpressionList :: N.Node -> O.ExpressionList
doExpressionList n@(N.Node m nt) = case nt of
N.Call f es -> O.FunctionCallList m (doName f) (map doExpression es)
N.ExpList es -> O.ExpressionList m (map doExpression es)
doReplicator :: N.Node -> O.Replicator
doReplicator n@(N.Node m nt) = case nt of
N.For v f t -> O.For m (doName v) (doExpression f) (doExpression t)
doFields :: [N.Node] -> [(O.Type, O.Tag)]
doFields ns = concat $ [[(doType t, doTag f) | f <- fs] | (N.Node _ (N.Fields t fs)) <- ns]
doFormals :: [N.Node] -> [(O.Type, O.Name)]
doFormals fs = concat $ [[(doType t, doName n) | n <- ns] | (N.Node _ (N.Formals t ns)) <- fs]
doVariant :: N.Node -> O.Structured
doVariant n@(N.Node m nt) = case nt of
N.Variant (N.Node _ (N.Tag t is)) p -> O.OnlyV m $ O.Variant m (doTag t) (map doInputItem is) (doProcess p)
N.Decl s v -> doSpecifications s O.Spec (doVariant v)
doChoice :: N.Node -> O.Structured
doChoice n@(N.Node m nt) = case nt of
N.If cs -> O.Several m $ map doChoice cs
N.IfRep r c -> O.Rep m (doReplicator r) (doChoice c)
N.Choice b p -> O.OnlyC m $ O.Choice m (doExpression b) (doProcess p)
N.Decl s c -> doSpecifications s O.Spec (doChoice c)
doOption :: N.Node -> O.Structured
doOption n@(N.Node m nt) = case nt of
N.CaseExps cs p -> O.OnlyO m $ O.Option m (map doExpression cs) (doProcess p)
N.Else p -> O.OnlyO m $ O.Else m (doProcess p)
N.Decl s o -> doSpecifications s O.Spec (doOption o)
doInputItem :: N.Node -> O.InputItem
doInputItem n@(N.Node m nt) = case nt of
N.Counted c d -> O.InCounted m (doVariable c) (doVariable d)
otherwise -> O.InVariable m (doVariable n)
doOutputItem :: N.Node -> O.OutputItem
doOutputItem n@(N.Node m nt) = case nt of
N.Counted c d -> O.OutCounted m (doExpression c) (doExpression d)
otherwise -> O.OutExpression m (doExpression n)
doInputMode :: N.Node -> O.InputMode
doInputMode n@(N.Node m nt) = case nt of
N.InSimple is -> O.InputSimple m (map doInputItem is)
N.InCase vs -> O.InputCase m (O.Several m $ map doVariant vs)
N.InTag (N.Node _ (N.Tag t is)) -> O.InputCase m (O.OnlyV m $ O.Variant m (doTag t) (map doInputItem is) (O.Skip m))
N.InAfter e -> O.InputAfter m (doExpression e)
doSimpleSpec :: N.Node -> O.Specification
doSimpleSpec n@(N.Node m nt) = case nt of
N.Is d v -> (doName d, O.Is m O.Infer (doVariable v))
N.IsType t d v -> (doName d, O.Is m (doType t) (doVariable v))
N.ValIs d e -> (doName d, O.ValIs m O.Infer (doExpression e))
N.ValIsType t d e -> (doName d, O.ValIs m (doType t) (doExpression e))
N.Place v e -> (doName v, O.Place m (doExpression e))
N.DataType n (N.Node _ (N.Record fs)) -> (doName n, O.DataTypeRecord m False (doFields fs))
N.DataType n (N.Node _ (N.PackedRecord fs)) -> (doName n, O.DataTypeRecord m True (doFields fs))
N.DataType n t -> (doName n, O.DataTypeIs m (doType t))
N.Protocol n is -> (doName n, O.ProtocolIs m (map doType is))
N.TaggedProtocol n ts -> (doName n, O.ProtocolCase m [(doTag tn, map doType tts) | (N.Node _ (N.Tag tn tts)) <- ts])
N.Proc n fs p -> (doName n, O.Proc m (doFormals fs) (doProcess p))
N.Func n rs fs vp -> (doName n, O.Function m (map doType rs) (doFormals fs) (doValueProcess vp))
N.FuncIs n rs fs el -> (doName n, O.Function m (map doType rs) (doFormals fs) (O.ValOf m (O.Skip m) (doExpressionList el)))
N.Retypes t d s -> (doName d, O.Retypes m (doType t) (doVariable s))
N.ValRetypes t d s -> (doName d, O.ValRetypes m (doType t) (doVariable s))
N.Reshapes t d s -> (doName d, O.Reshapes m (doType t) (doVariable s))
N.ValReshapes t d s -> (doName d, O.ValReshapes m (doType t) (doVariable s))
doSpecifications :: N.Node -> (Meta -> O.Specification -> a -> a) -> a -> a
doSpecifications n@(N.Node m nt) comb arg = case nt of
N.Vars t [] -> arg
N.Vars t (v:vs) -> comb m (doName v, O.Declaration m (doType t)) (doSpecifications (N.Node m (N.Vars t vs)) comb arg)
otherwise -> comb m (doSimpleSpec n) arg
doAlternative :: N.Node -> O.Alternative
doAlternative n@(N.Node m nt) = case nt of
N.Guard (N.Node _ (N.In c md)) p -> O.Alternative m (doVariable c) (doInputMode md) (doProcess p)
N.Guard (N.Node _ (N.CondGuard b (N.Node _ (N.In c md)))) p -> O.AlternativeCond m (doExpression b) (doVariable c) (doInputMode md) (doProcess p)
N.Guard (N.Node _ (N.CondGuard b (N.Node _ N.Skip))) p -> O.AlternativeSkip m (doExpression b) (doProcess p)
-- ALT over "? CASE": the O.Skip that gets inserted here doesn't correspond
-- to anything in real occam; it's just there to let us handle these the same
-- way as the regular ALT inputs.
N.In c md@(N.Node _ (N.InCase _)) -> O.Alternative m (doVariable c) (doInputMode md) (O.Skip m)
N.CondGuard b (N.Node _ (N.In c md@(N.Node _ (N.InCase _)))) -> O.AlternativeCond m (doExpression b) (doVariable c) (doInputMode md) (O.Skip m)
doAlt :: N.Node -> O.Structured
doAlt n@(N.Node m nt) = case nt of
N.Alt ns -> O.Several m $ map doAlt ns
N.PriAlt ns -> O.Several m $ map doAlt ns
N.AltRep r n -> O.Rep m (doReplicator r) (doAlt n)
N.PriAltRep r n -> O.Rep m (doReplicator r) (doAlt n)
N.Decl s n -> doSpecifications s O.Spec (doAlt n)
otherwise -> O.OnlyA m $ doAlternative n
doValueProcess :: N.Node -> O.ValueProcess
doValueProcess n@(N.Node m nt) = case nt of
N.Decl s n -> doSpecifications s O.ValOfSpec (doValueProcess n)
N.ValOf p el -> O.ValOf m (doProcess p) (doExpressionList el)
doPlacedPar :: N.Node -> O.Structured
doPlacedPar n@(N.Node m nt) = case nt of
N.PlacedPar ps -> O.Several m $ map doPlacedPar ps
N.PlacedParRep r p -> O.Rep m (doReplicator r) (doPlacedPar p)
N.Processor e p -> O.OnlyP m $ O.Processor m (doExpression e) (doProcess p)
N.Decl s p -> doSpecifications s O.Spec (doPlacedPar p)
doProcess :: N.Node -> O.Process
doProcess n@(N.Node m nt) = case nt of
N.Decl s p -> doSpecifications s O.ProcSpec (doProcess p)
N.Assign vs el -> O.Assign m (map doVariable vs) (doExpressionList el)
N.In c md -> O.Input m (doVariable c) (doInputMode md)
N.Out c os -> O.Output m (doVariable c) (map doOutputItem os)
N.OutCase c t os -> O.OutputCase m (doVariable c) (doTag t) (map doOutputItem os)
N.Skip -> O.Skip m
N.Stop -> O.Stop m
N.MainProcess -> O.Main m
N.Seq ps -> O.Seq m (map doProcess ps)
N.SeqRep r p -> O.SeqRep m (doReplicator r) (doProcess p)
N.If _ -> O.If m $ doChoice n
N.Case e os -> O.Case m (doExpression e) (O.Several m $ map doOption os)
N.While e p -> O.While m (doExpression e) (doProcess p)
N.Par ns -> O.Par m False (map doProcess ns)
N.PriPar ns -> O.Par m True (map doProcess ns)
N.ParRep r p -> O.ParRep m False (doReplicator r) (doProcess p)
N.PriParRep r p -> O.ParRep m True (doReplicator r) (doProcess p)
N.PlacedPar _ -> O.PlacedPar m $ doPlacedPar n
N.PlacedParRep _ _ -> O.PlacedPar m $ doPlacedPar n
N.Processor _ _ -> O.PlacedPar m $ doPlacedPar n
N.Alt _ -> O.Alt m False $ doAlt n
N.AltRep _ _ -> O.Alt m False $ doAlt n
N.PriAlt _ -> O.Alt m True $ doAlt n
N.PriAltRep _ _ -> O.Alt m True $ doAlt n
N.ProcCall p es -> O.ProcCall m (doName p) (map doExpression es)
ptToAST :: N.Node -> O.Process
ptToAST = doProcess

View File

@ -1,803 +0,0 @@
-- Parse occam code
module Parse (readSource, parseSource) where
import Data.List
import Text.ParserCombinators.Parsec
import qualified Text.ParserCombinators.Parsec.Token as P
import Text.ParserCombinators.Parsec.Language (emptyDef)
import qualified IO
import qualified PT as N
import Metadata
-- -------------------------------------------------------------
md :: Parser Meta
md = do
pos <- getPosition
return $ [SourcePos (sourceName pos) (sourceLine pos) (sourceColumn pos)]
nd :: Meta -> N.NodeType -> N.Node
nd = N.Node
-- -------------------------------------------------------------
mainMarker = "##MAGIC-FCO-MAIN-PROCESS##"
occamStyle
= emptyDef
{ P.commentLine = "--"
, P.nestedComments = False
, P.identStart = letter
, P.identLetter = alphaNum <|> char '.'
, P.opStart = oneOf "+-*/\\>=<~"
, P.opLetter = oneOf "/\\>=<"
, P.reservedOpNames= [
"+",
"-",
"*",
"/",
"\\",
"/\\",
"\\/",
"><",
"=",
"<>",
"<",
">",
">=",
"<=",
"-",
"~"
]
, P.reservedNames = [
"AFTER",
"ALT",
"AND",
"ANY",
"AT",
"BITAND",
"BITNOT",
"BITOR",
"BOOL",
"BYTE",
"BYTESIN",
"CASE",
"CHAN",
"DATA",
"ELSE",
"FALSE",
"FOR",
"FROM",
"FUNCTION",
"IF",
"INT",
"INT16",
"INT32",
"INT64",
"IS",
"MINUS",
"MOSTNEG",
"MOSTPOS",
"NOT",
"OF",
"OFFSETOF",
"OR",
"PACKED",
"PAR",
"PLACE",
"PLACED",
"PLUS",
"PORT",
"PRI",
"PROC",
"PROCESSOR",
"PROTOCOL",
"REAL32",
"REAL64",
"RECORD",
"REM",
"RESHAPES",
"RESULT",
"RETYPES",
"ROUND",
"SEQ",
"SIZE",
"SKIP",
"STOP",
"TIMER",
"TIMES",
"TRUE",
"TRUNC",
"TYPE",
"VAL",
"VALOF",
"WHILE",
mainMarker
]
, P.caseSensitive = True
}
lexer :: P.TokenParser ()
lexer = P.makeTokenParser occamStyle
-- XXX replace whitespace with something that doesn't eat \ns
whiteSpace = P.whiteSpace lexer
lexeme = P.lexeme lexer
symbol = P.symbol lexer
natural = P.natural lexer
parens = P.parens lexer
semi = P.semi lexer
identifier= P.identifier lexer
reserved = P.reserved lexer
reservedOp= P.reservedOp lexer
sLeft = try $ symbol "["
sRight = try $ symbol "]"
sLeftR = try $ symbol "("
sRightR = try $ symbol ")"
sAssign = try $ symbol ":="
sColon = try $ symbol ":"
sColons = try $ symbol "::"
sComma = try $ symbol ","
sSemi = try $ symbol ";"
sAmp = try $ symbol "&"
sQuest = try $ symbol "?"
sBang = try $ symbol "!"
sEq = try $ symbol "="
sAFTER = reserved "AFTER"
sALT = reserved "ALT"
sAND = reserved "AND"
sANY = reserved "ANY"
sAT = reserved "AT"
sBITAND = reserved "BITAND"
sBITNOT = reserved "BITNOT"
sBITOR = reserved "BITOR"
sBOOL = reserved "BOOL"
sBYTE = reserved "BYTE"
sBYTESIN = reserved "BYTESIN"
sCASE = reserved "CASE"
sCHAN = reserved "CHAN"
sDATA = reserved "DATA"
sELSE = reserved "ELSE"
sFALSE = reserved "FALSE"
sFOR = reserved "FOR"
sFROM = reserved "FROM"
sFUNCTION = reserved "FUNCTION"
sIF = reserved "IF"
sINT = reserved "INT"
sINT16 = reserved "INT16"
sINT32 = reserved "INT32"
sINT64 = reserved "INT64"
sIS = reserved "IS"
sMINUS = reserved "MINUS"
sMOSTNEG = reserved "MOSTNEG"
sMOSTPOS = reserved "MOSTPOS"
sNOT = reserved "NOT"
sOF = reserved "OF"
sOFFSETOF = reserved "OFFSETOF"
sOR = reserved "OR"
sPACKED = reserved "PACKED"
sPAR = reserved "PAR"
sPLACE = reserved "PLACE"
sPLACED = reserved "PLACED"
sPLUS = reserved "PLUS"
sPORT = reserved "PORT"
sPRI = reserved "PRI"
sPROC = reserved "PROC"
sPROCESSOR = reserved "PROCESSOR"
sPROTOCOL = reserved "PROTOCOL"
sREAL32 = reserved "REAL32"
sREAL64 = reserved "REAL64"
sRECORD = reserved "RECORD"
sREM = reserved "REM"
sRESHAPES = reserved "RESHAPES"
sRESULT = reserved "RESULT"
sRETYPES = reserved "RETYPES"
sROUND = reserved "ROUND"
sSEQ = reserved "SEQ"
sSIZE = reserved "SIZE"
sSKIP = reserved "SKIP"
sSTOP = reserved "STOP"
sTIMER = reserved "TIMER"
sTIMES = reserved "TIMES"
sTRUE = reserved "TRUE"
sTRUNC = reserved "TRUNC"
sTYPE = reserved "TYPE"
sVAL = reserved "VAL"
sVALOF = reserved "VALOF"
sWHILE = reserved "WHILE"
sMainMarker = reserved mainMarker
-- XXX could handle VALOF by translating each step to one { and matching multiple ones?
indent = symbol "{"
outdent = symbol "}"
eol = symbol "@"
-- -------------------------------------------------------------
-- These productions are based on the syntax in the occam2.1 manual.
-- The way productions should work is that each production should only consume input if it's sure that it's unambiguous.
abbreviation
= try (do { m <- md ; n <- name ; sIS ; v <- variable ; sColon ; eol ; return $ nd m $ N.Is n v })
<|> try (do { m <- md ; s <- specifier ; n <- name ; sIS ; v <- variable ; sColon ; eol ; return $ nd m $ N.IsType s n v })
<|> do { m <- md ; sVAL ;
try (do { n <- name ; sIS ; e <- expression ; sColon ; eol ; return $ nd m $ N.ValIs n e })
<|> do { s <- specifier ; n <- name ; sIS ; e <- expression ; sColon ; eol ; return $ nd m $ N.ValIsType s n e } }
<?> "abbreviation"
actual
= expression
<|> variable
<|> channel
<?> "actual"
allocation
= do { m <- md ; sPLACE ; n <- name ; sAT ; e <- expression ; sColon ; eol ; return $ nd m $ N.Place n e }
<?> "allocation"
alternation
= do { m <- md ; sALT ;
do { eol ; indent ; as <- many1 alternative ; outdent ; return $ nd m $ N.Alt as }
<|> do { r <- replicator ; eol ; indent ; a <- alternative ; outdent ; return $ nd m $ N.AltRep r a } }
<|> do { m <- md ; sPRI ; sALT ;
do { eol ; indent ; as <- many1 alternative ; outdent ; return $ nd m $ N.PriAlt as }
<|> do { r <- replicator ; eol ; indent ; a <- alternative ; outdent ; return $ nd m $ N.PriAltRep r a } }
<?> "alternation"
-- The reason the CASE guards end up here is because they have to be handled
-- specially: you can't tell until parsing the guts of the CASE what the processes
-- are.
alternative
= guardedAlternative
<|> alternation
<|> try (do { m <- md ; b <- boolean ; sAmp ; c <- channel ; sQuest ; sCASE ; eol ; indent ; vs <- many1 variant ; outdent ; return $ nd m $ N.CondGuard b (nd m $ N.In c (nd m $ N.InCase vs)) })
<|> try (do { m <- md ; c <- channel ; sQuest ; sCASE ; eol ; indent ; vs <- many1 variant ; outdent ; return $ nd m $ N.In c (nd m $ N.InCase vs) })
<|> do { m <- md ; s <- specification ; a <- alternative ; return $ nd m $ N.Decl s a }
<?> "alternative"
assignment
= do { m <- md ; vs <- variableList ; sAssign ; es <- expressionList ; eol ; return $ nd m $ N.Assign vs es }
<?> "assignment"
base
= expression
<?> "base"
boolean
= expression
<?> "boolean"
byte
= lexeme (do { m <- md ; char '\'' ; s <- character ; char '\'' ; return $ nd m $ N.LitByte s })
<?> "byte"
caseExpression
= expression
<?> "caseExpression"
caseInput
= do { m <- md ; c <- channel ; sQuest ; sCASE ; eol ; indent ; vs <- many1 variant ; outdent ; return $ nd m $ N.In c (nd m $ N.InCase vs) }
<?> "caseInput"
-- This is also used for timers and ports, since the syntax is identical (and
-- the parser really can't tell at this stage which is which).
channel
= do { m <- md ; v <- channel' ; es <- many (do { sLeft ; e <- expression ; sRight ; return e }) ; return $ foldl (\e s -> nd m $ N.Sub (nd m $ N.SubPlain s) e) v es }
<?> "channel"
channel'
= try name
<|> try (do { m <- md ; sLeft ; n <- channel ; sFROM ; e <- expression ; sFOR ; f <- expression ; sRight ; return $ nd m $ N.Sub (nd m $ N.SubFromFor e f) n })
<|> try (do { m <- md ; sLeft ; n <- channel ; sFROM ; e <- expression ; sRight ; return $ nd m $ N.Sub (nd m $ N.SubFrom e) n })
<|> do { m <- md ; sLeft ; n <- channel ; sFOR ; e <- expression ; sRight ; return $ nd m $ N.Sub (nd m $ N.SubFor e) n }
<?> "channel'"
-- FIXME should probably make CHAN INT work, since that'd be trivial...
channelType
= do { m <- md ; sCHAN ; sOF ; p <- protocol ; return $ nd m $ N.ChanOf p }
<|> try (do { m <- md ; sLeft ; s <- expression ; sRight ; t <- channelType ; return $ nd m $ N.Array s t })
<?> "channelType"
-- FIXME this isn't at all the right way to return the character!
character
= try (do { char '*' ;
do { char '#' ; a <- hexDigit ; b <- hexDigit ; return $ ['*', '#', a, b] }
<|> do { c <- anyChar ; return $ ['*', c] } })
<|> do { c <- anyChar ; return $ [c] }
<?> "character"
occamChoice
= guardedChoice
<|> conditional
<|> do { m <- md ; s <- try specification ; c <- occamChoice ; return $ nd m $ N.Decl s c }
<?> "choice"
conditional
= do { m <- md ; sIF ;
do { eol ; indent ; cs <- many1 occamChoice ; outdent ; return $ nd m $ N.If cs }
<|> do { r <- replicator ; eol ; indent ; c <- occamChoice ; outdent ; return $ nd m $ N.IfRep r c } }
<?> "conditional"
-- This uses operandNotTable to resolve the "x[y]" ambiguity.
conversion
= try (do { m <- md ; t <- dataType; do { sROUND ; o <- operand ; return $ nd m $ N.Round t o } <|> do { sTRUNC ; o <- operand ; return $ nd m $ N.Trunc t o } <|> do { o <- operandNotTable ; return $ nd m $ N.Conv t o } })
<?> "conversion"
occamCount
= expression
<?> "count"
dataType
= do { m <- md ; sBOOL ; return $ nd m $ N.Bool }
<|> do { m <- md ; sBYTE ; return $ nd m $ N.Byte }
<|> do { m <- md ; sINT ; return $ nd m $ N.Int }
<|> do { m <- md ; sINT16 ; return $ nd m $ N.Int16 }
<|> do { m <- md ; sINT32 ; return $ nd m $ N.Int32 }
<|> do { m <- md ; sINT64 ; return $ nd m $ N.Int64 }
<|> do { m <- md ; sREAL32 ; return $ nd m $ N.Real32 }
<|> do { m <- md ; sREAL64 ; return $ nd m $ N.Real64 }
<|> try (do { m <- md ; sLeft ; s <- expression ; sRight ; t <- dataType ; return $ nd m $ N.Array s t })
<|> name
<?> "data type"
declType
= dataType
<|> channelType
<|> timerType
<|> portType
-- FIXME this originally had four lines like this, one for each of the above;
-- it might be nicer to generate a different nd m $ N.Node for each type of declaration
declaration
= do { m <- md ; d <- declType ; ns <- sepBy1 name sComma ; sColon ; eol ; return $ nd m $ N.Vars d ns }
<?> "declaration"
definition
= do { m <- md ; sDATA ; sTYPE ; n <- name ;
do {sIS ; t <- dataType ; sColon ; eol ; return $ nd m $ N.DataType n t }
<|> do { eol ; indent ; t <- structuredType ; outdent ; sColon ; eol ; return $ nd m $ N.DataType n t } }
<|> do { m <- md ; sPROTOCOL ; n <- name ;
do { sIS ; p <- sequentialProtocol ; sColon ; eol ; return $ nd m $ N.Protocol n p }
<|> do { eol ; indent ; sCASE ; eol ; indent ; ps <- many1 taggedProtocol ; outdent ; outdent ; sColon ; eol ; return $ nd m $ N.TaggedProtocol n ps } }
<|> do { m <- md ; sPROC ; n <- name ; fs <- formalList ; eol ; indent ; p <- process ; outdent ; sColon ; eol ; return $ nd m $ N.Proc n fs p }
<|> try (do { m <- md ; rs <- sepBy1 dataType sComma ; (n, fs) <- functionHeader ;
do { sIS ; el <- expressionList ; sColon ; eol ; return $ nd m $ N.FuncIs n rs fs el }
<|> do { eol ; indent ; vp <- valueProcess ; outdent ; sColon ; eol ; return $ nd m $ N.Func n rs fs vp } })
<|> try (do { m <- md ; s <- specifier ; n <- name ;
do { sRETYPES ; v <- variable ; sColon ; eol ; return $ nd m $ N.Retypes s n v }
<|> do { try sRESHAPES ; v <- variable ; sColon ; eol ; return $ nd m $ N.Reshapes s n v } })
<|> do { m <- md ; sVAL ; s <- specifier ; n <- name ;
do { sRETYPES ; v <- variable ; sColon ; eol ; return $ nd m $ N.ValRetypes s n v }
<|> do { sRESHAPES ; v <- variable ; sColon ; eol ; return $ nd m $ N.ValReshapes s n v } }
<?> "definition"
digits
= many1 digit
<?> "digits"
dyadicOperator
= do { m <- md ; reservedOp "+" ; return $ nd m $ N.Add }
<|> do { m <- md ; reservedOp "-" ; return $ nd m $ N.Subtr }
<|> do { m <- md ; reservedOp "*" ; return $ nd m $ N.Mul }
<|> do { m <- md ; reservedOp "/" ; return $ nd m $ N.Div }
<|> do { m <- md ; reservedOp "\\" ; return $ nd m $ N.Rem }
<|> do { m <- md ; sREM ; return $ nd m $ N.Rem }
<|> do { m <- md ; sPLUS ; return $ nd m $ N.Plus }
<|> do { m <- md ; sMINUS ; return $ nd m $ N.Minus }
<|> do { m <- md ; sTIMES ; return $ nd m $ N.Times }
<|> do { m <- md ; reservedOp "/\\" ; return $ nd m $ N.BitAnd }
<|> do { m <- md ; reservedOp "\\/" ; return $ nd m $ N.BitOr }
<|> do { m <- md ; reservedOp "><" ; return $ nd m $ N.BitXor }
<|> do { m <- md ; sBITAND ; return $ nd m $ N.BitAnd }
<|> do { m <- md ; sBITOR ; return $ nd m $ N.BitOr }
<|> do { m <- md ; sAND ; return $ nd m $ N.And }
<|> do { m <- md ; sOR ; return $ nd m $ N.Or }
<|> do { m <- md ; reservedOp "=" ; return $ nd m $ N.Eq }
<|> do { m <- md ; reservedOp "<>" ; return $ nd m $ N.NEq }
<|> do { m <- md ; reservedOp "<" ; return $ nd m $ N.Less }
<|> do { m <- md ; reservedOp ">" ; return $ nd m $ N.More }
<|> do { m <- md ; reservedOp "<=" ; return $ nd m $ N.LessEq }
<|> do { m <- md ; reservedOp ">=" ; return $ nd m $ N.MoreEq }
<|> do { m <- md ; sAFTER ; return $ nd m $ N.After }
<?> "dyadicOperator"
occamExponent
= try (do { c <- oneOf "+-" ; d <- digits ; return $ c : d })
<?> "exponent"
expression
= try (do { m <- md ; o <- monadicOperator ; v <- operand ; return $ nd m $ N.MonadicOp o v })
<|> do { m <- md ; a <- sMOSTPOS ; t <- dataType ; return $ nd m $ N.MostPos t }
<|> do { m <- md ; a <- sMOSTNEG ; t <- dataType ; return $ nd m $ N.MostNeg t }
<|> do { m <- md ; a <- sSIZE ; t <- dataType ; return $ nd m $ N.Size t }
<|> try (do { m <- md ; a <- operand ; o <- dyadicOperator ; b <- operand ; return $ nd m $ N.DyadicOp o a b })
<|> try conversion
<|> operand
<?> "expression"
expressionList
= try (do { m <- md ; n <- name ; sLeftR ; as <- sepBy expression sComma ; sRightR ; return $ nd m $ N.Call n as })
<|> do { m <- md ; es <- sepBy1 expression sComma ; return $ nd m $ N.ExpList es }
-- XXX value process
<?> "expressionList"
fieldName
= name
<?> "fieldName"
-- This is rather different from the grammar, since I had some difficulty
-- getting Parsec to parse it as a list of lists of arguments.
formalList
= do { m <- md ; sLeftR ; fs <- sepBy formalArg sComma ; sRightR ; return $ markTypes m fs }
<?> "formalList"
where
formalArg :: Parser (Maybe N.Node, N.Node)
formalArg = try (do { m <- md ; sVAL ; s <- specifier ; n <- name ; return $ (Just (nd m $ N.Val s), n) })
<|> try (do { s <- specifier ; n <- name ; return $ (Just s, n) })
<|> try (do { n <- name ; return $ (Nothing, n) })
markTypes :: Meta -> [(Maybe N.Node, N.Node)] -> [N.Node]
markTypes _ [] = []
markTypes _ ((Nothing, _):_) = error "Formal list must start with a type"
markTypes m ((Just ft, fn):is) = markRest m ft [fn] is
markRest :: Meta -> N.Node -> [N.Node] -> [(Maybe N.Node, N.Node)] -> [N.Node]
markRest m lt ns [] = [nd m $ N.Formals lt ns]
markRest m lt ns ((Nothing, n):is) = markRest m lt (ns ++ [n]) is
markRest m lt ns ((Just t, n):is) = (markRest m lt ns []) ++ (markRest m t [n] is)
functionHeader
= do { sFUNCTION ; n <- name ; fs <- formalList ; return $ (n, fs) }
<?> "functionHeader"
guard
= try input
<|> try (do { m <- md ; b <- boolean ; sAmp ; i <- input ; return $ nd m $ N.CondGuard b i })
<|> try (do { m <- md ; b <- boolean ; sAmp ; sSKIP ; eol ; return $ nd m $ N.CondGuard b (nd m $ N.Skip) })
<?> "guard"
guardedAlternative
= do { m <- md ; g <- guard ; indent ; p <- process ; outdent ; return $ nd m $ N.Guard g p }
<?> "guardedAlternative"
guardedChoice
= do { m <- md ; b <- boolean ; eol ; indent ; p <- process ; outdent ; return $ nd m $ N.Choice b p }
<?> "guardedChoice"
hexDigits
= do { m <- md ; d <- many1 hexDigit ; return $ nd m $ N.LitHex d }
<?> "hexDigits"
input
= do m <- md
c <- channel
sQuest
(do { sCASE ; tl <- taggedList ; eol ; return $ nd m $ N.In c (nd m $ N.InTag tl) }
<|> do { sAFTER ; e <- expression ; eol ; return $ nd m $ N.In c (nd m $ N.InAfter e) }
<|> do { is <- sepBy1 inputItem sSemi ; eol ; return $ nd m $ N.In c (nd m $ N.InSimple is) })
<?> "input"
inputItem
= try (do { m <- md ; v <- variable ; sColons ; w <- variable ; return $ nd m $ N.Counted v w })
<|> variable
<?> "inputItem"
integer
= try (do { m <- md ; d <- lexeme digits ; return $ nd m $ N.LitInt d })
<|> do { char '#' ; d <- lexeme hexDigits ; return $ d }
<?> "integer"
literal
= try real
<|> try integer
<|> try byte
<|> try (do { m <- md ; v <- real ; sLeftR ; t <- dataType ; sRightR ; return $ nd m $ N.TypedLit t v })
<|> try (do { m <- md ; v <- integer ; sLeftR ; t <- dataType ; sRightR ; return $ nd m $ N.TypedLit t v })
<|> try (do { m <- md ; v <- byte ; sLeftR ; t <- dataType ; sRightR ; return $ nd m $ N.TypedLit t v })
<|> try (do { m <- md ; sTRUE ; return $ nd m $ N.True })
<|> do { m <- md ; sFALSE ; return $ nd m $ N.False }
<?> "literal"
loop
= do { m <- md ; sWHILE ; b <- boolean ; eol ; indent ; p <- process ; outdent ; return $ nd m $ N.While b p }
monadicOperator
= do { m <- md ; reservedOp "-" ; return $ nd m $ N.MonSub }
<|> do { m <- md ; sMINUS ; return $ nd m $ N.MonSub }
<|> do { m <- md ; reservedOp "~" ; return $ nd m $ N.MonBitNot }
<|> do { m <- md ; sBITNOT ; return $ nd m $ N.MonBitNot }
<|> do { m <- md ; sNOT ; return $ nd m $ N.MonNot }
<|> do { m <- md ; sSIZE ; return $ nd m $ N.MonSize }
<?> "monadicOperator"
name
= do { m <- md ; s <- identifier ; return $ nd m $ N.Name s }
<?> "name"
occamString
= lexeme (do { m <- md ; char '"' ; s <- many (noneOf "\"") ; char '"' ; return $ nd m $ N.LitString s })
<?> "string"
operandNotTable
= do { m <- md ; v <- operandNotTable' ; es <- many (do { sLeft ; e <- expression ; sRight ; return e }) ; return $ foldl (\e s -> nd m $ N.Sub (nd m $ N.SubPlain s) e) v es }
<?> "operandNotTable"
operandNotTable'
= try variable
<|> try literal
<|> try (do { sLeftR ; e <- expression ; sRightR ; return e })
-- XXX value process
<|> try (do { m <- md ; n <- name ; sLeftR ; as <- sepBy expression sComma ; sRightR ; return $ nd m $ N.Call n as })
<|> try (do { m <- md ; sBYTESIN ; sLeftR ; o <- operand ; sRightR ; return $ nd m $ N.BytesIn o })
<|> try (do { m <- md ; sBYTESIN ; sLeftR ; o <- dataType ; sRightR ; return $ nd m $ N.BytesIn o })
<|> try (do { m <- md ; sOFFSETOF ; sLeftR ; n <- name ; sComma ; f <- fieldName ; sRightR ; return $ nd m $ N.OffsetOf n f })
<?> "operandNotTable'"
operand
= do { m <- md ; v <- operand' ; es <- many (do { sLeft ; e <- expression ; sRight ; return e }) ; return $ foldl (\e s -> nd m $ N.Sub (nd m $ N.SubPlain s) e) v es }
<?> "operand"
operand'
= try table
<|> operandNotTable'
<?> "operand'"
occamOption
= try (do { m <- md ; ces <- sepBy caseExpression sComma ; eol ; indent ; p <- process ; outdent ; return $ nd m $ N.CaseExps ces p })
<|> try (do { m <- md ; sELSE ; eol ; indent ; p <- process ; outdent ; return $ nd m $ N.Else p })
<|> do { m <- md ; s <- specification ; o <- occamOption ; return $ nd m $ N.Decl s o }
<?> "option"
-- XXX This can't tell at parse time in "c ! x; y" whether x is a variable or a tag...
-- ... so this now wants "c ! CASE x" if it's a tag, to match input.
-- We can fix this with a pass later...
output
= do m <- md
c <- channel
sBang
(do { sCASE ; t <- tag ; sSemi ; os <- sepBy1 outputItem sSemi ; eol ; return $ nd m $ N.OutCase c t os }
<|> do { sCASE ; t <- tag ; eol ; return $ nd m $ N.OutCase c t [] }
<|> do { os <- sepBy1 outputItem sSemi ; eol ; return $ nd m $ N.Out c os })
<?> "output"
outputItem
= try (do { m <- md ; a <- expression ; sColons ; b <- expression ; return $ nd m $ N.Counted a b })
<|> expression
<?> "outputItem"
parallel
= do { m <- md ; sPAR ; do { eol ; indent ; ps <- many1 process ; outdent ; return $ nd m $ N.Par ps } <|> do { r <- replicator ; eol ; indent ; p <- process ; outdent ; return $ nd m $ N.ParRep r p } }
<|> do { m <- md ; sPRI ; sPAR ; do { eol ; indent ; ps <- many1 process ; outdent ; return $ nd m $ N.PriPar ps } <|> do { r <- replicator ; eol ; indent ; p <- process ; outdent ; return $ nd m $ N.PriParRep r p } }
<|> placedpar
<?> "parallel"
-- XXX PROCESSOR as a process isn't really legal, surely?
placedpar
= do { m <- md ; sPLACED ; sPAR ; do { eol ; indent ; ps <- many1 placedpar ; outdent ; return $ nd m $ N.PlacedPar ps } <|> do { r <- replicator ; eol ; indent ; p <- placedpar ; outdent ; return $ nd m $ N.PlacedParRep r p } }
<|> do { m <- md ; sPROCESSOR ; e <- expression ; eol ; indent ; p <- process ; outdent ; return $ nd m $ N.Processor e p }
<?> "placedpar"
portType
= do { m <- md ; sPORT ; sOF ; p <- protocol ; return $ nd m $ N.PortOf p }
<|> do { m <- md ; try sLeft ; s <- try expression ; try sRight ; t <- portType ; return $ nd m $ N.Array s t }
<?> "portType"
procInstance
= do { m <- md ; n <- name ; sLeftR ; as <- sepBy actual sComma ; sRightR ; eol ; return $ nd m $ N.ProcCall n as }
<?> "procInstance"
process
= try assignment
<|> try input
<|> try output
<|> do { m <- md ; sSKIP ; eol ; return $ nd m $ N.Skip }
<|> do { m <- md ; sSTOP ; eol ; return $ nd m $ N.Stop }
<|> occamSequence
<|> conditional
<|> selection
<|> loop
<|> try parallel
<|> alternation
<|> try caseInput
<|> try procInstance
<|> do { m <- md ; sMainMarker ; eol ; return $ nd m $ N.MainProcess }
<|> do { m <- md ; a <- allocation ; p <- process ; return $ nd m $ N.Decl a p }
<|> do { m <- md ; s <- specification ; p <- process ; return $ nd m $ N.Decl s p }
<?> "process"
protocol
= try name
<|> simpleProtocol
<?> "protocol"
real
= try (do { m <- md ; l <- digits ; char '.' ; r <- digits ; char 'e' ; e <- lexeme occamExponent ; return $ nd m $ N.LitReal (l ++ "." ++ r ++ "e" ++ e) })
<|> do { m <- md ; l <- digits ; char '.' ; r <- lexeme digits ; return $ nd m $ N.LitReal (l ++ "." ++ r) }
<?> "real"
replicator
= do { m <- md ; n <- name ; sEq ; b <- base ; sFOR ; c <- occamCount ; return $ nd m $ N.For n b c }
<?> "replicator"
selection
= do { m <- md ; sCASE ; s <- selector ; eol ; indent ; os <- many1 occamOption ; outdent ; return $ nd m $ N.Case s os }
<?> "selection"
selector
= expression
<?> "selector"
occamSequence
= do m <- md
sSEQ
(do { eol ; indent ; ps <- many1 process ; outdent ; return $ nd m $ N.Seq ps }
<|> do { r <- replicator ; eol ; indent ; p <- process ; outdent ; return $ nd m $ N.SeqRep r p })
<?> "sequence"
sequentialProtocol
= do { l <- try $ sepBy1 simpleProtocol sSemi ; return $ l }
<?> "sequentialProtocol"
simpleProtocol
= try (do { m <- md ; l <- dataType ; sColons ; sLeft ; sRight ; r <- dataType ; return $ nd m $ N.Counted l r })
<|> dataType
<|> do { m <- md ; sANY ; return $ nd m $ N.Any }
<?> "simpleProtocol"
specification
= try declaration
<|> try abbreviation
<|> definition
<?> "specification"
specifier
= try dataType
<|> try channelType
<|> try timerType
<|> try portType
<|> try (do { m <- md ; sLeft ; sRight ; s <- specifier ; return $ nd m $ N.ArrayUnsized s })
<|> do { m <- md ; sLeft ; e <- expression ; sRight ; s <- specifier ; return $ nd m $ N.Array e s }
<?> "specifier"
structuredType
= try (do { m <- md ; sRECORD ; eol ; indent ; fs <- many1 structuredTypeField ; outdent ; return $ nd m $ N.Record fs })
<|> do { m <- md ; sPACKED ; sRECORD ; eol ; indent ; fs <- many1 structuredTypeField ; outdent ; return $ nd m $ N.PackedRecord fs }
<?> "structuredType"
-- FIXME this should use the same type-folding code as proc/func definitions
structuredTypeField
= do { m <- md ; t <- dataType ; fs <- many1 fieldName ; sColon ; eol ; return $ nd m $ N.Fields t fs }
<?> "structuredTypeField"
-- i.e. array literal
table
= do { m <- md ; v <- table' ; es <- many (do { sLeft ; e <- expression ; sRight ; return e }) ; return $ foldl (\e s -> nd m $ N.Sub (nd m $ N.SubPlain s) e) v es }
<?> "table"
table'
= try occamString
<|> try (do { m <- md ; s <- occamString ; sLeftR ; n <- name ; sRightR ; return $ nd m $ N.TypedLit n s })
<|> do { sLeft ;
try (do { m <- md ; es <- sepBy1 expression sComma ; sRight ; return $ nd m $ N.LitArray es })
<|> try (do { m <- md ; n <- table ; sFROM ; e <- expression ; sFOR ; f <- expression ; sRight ; return $ nd m $ N.Sub (nd m $ N.SubFromFor e f) n })
<|> try (do { m <- md ; n <- table ; sFROM ; e <- expression ; sRight ; return $ nd m $ N.Sub (nd m $ N.SubFrom e) n })
<|> do { m <- md ; n <- table ; sFOR ; e <- expression ; sRight ; return $ nd m $ N.Sub (nd m $ N.SubFor e) n } }
<?> "table'"
tag
= name
<?> "tag"
taggedList
= try (do { m <- md ; t <- tag ; sSemi ; is <- sepBy1 inputItem sSemi ; return $ nd m $ N.Tag t is })
<|> do { m <- md ; t <- tag ; return $ nd m $ N.Tag t [] }
<?> "taggedList"
taggedProtocol
= try (do { m <- md ; t <- tag ; eol ; return $ nd m $ N.Tag t [] })
<|> try (do { m <- md ; t <- tag ; sSemi ; sp <- sequentialProtocol ; eol ; return $ nd m $ N.Tag t sp })
timerType
= do { m <- md ; sTIMER ; return $ nd m $ N.Timer }
<|> do { m <- md ; try sLeft ; s <- try expression ; try sRight ; t <- timerType ; return $ nd m $ N.Array s t }
<?> "timerType"
valueProcess
= try (do { m <- md ; sVALOF ; eol ; indent ; p <- process ; sRESULT ; el <- expressionList ; eol ; outdent ; return $ nd m $ N.ValOf p el })
<|> do { m <- md ; s <- specification ; v <- valueProcess ; return $ nd m $ N.Decl s v }
variable
= do { m <- md ; v <- variable' ; es <- many (do { sLeft ; e <- expression ; sRight ; return e }) ; return $ foldl (\e s -> nd m $ N.Sub (nd m $ N.SubPlain s) e) v es }
<?> "variable"
variable'
= try name
<|> try (do { m <- md ; sLeft ; n <- variable ; sFROM ; e <- expression ; sFOR ; f <- expression ; sRight ; return $ nd m $ N.Sub (nd m $ N.SubFromFor e f) n })
<|> try (do { m <- md ; sLeft ; n <- variable ; sFROM ; e <- expression ; sRight ; return $ nd m $ N.Sub (nd m $ N.SubFrom e) n })
<|> do { m <- md ; sLeft ; n <- variable ; sFOR ; e <- expression ; sRight ; return $ nd m $ N.Sub (nd m $ N.SubFor e) n }
<?> "variable'"
variableList
= do { vs <- sepBy1 variable sComma ; return $ vs }
<?> "variableList"
variant
= try (do { m <- md ; t <- taggedList ; eol ; indent ; p <- process ; outdent ; return $ nd m $ N.Variant t p })
<|> do { m <- md ; s <- specification ; v <- variant ; return $ nd m $ N.Decl s v }
<?> "variant"
-- -------------------------------------------------------------
-- This is only really true once we've tacked a process onto the bottom; a
-- source file is really a series of specifications, but the later ones need to
-- have the earlier ones in scope, so we can't parse them separately.
sourceFile = do { whiteSpace ; process }
-- -------------------------------------------------------------
-- XXX this doesn't handle multi-line strings
-- XXX or VALOF processes
countIndent :: String -> Int
countIndent (' ':' ':cs) = 1 + (countIndent cs)
countIndent (' ':cs) = error "Bad indentation"
countIndent _ = 0
stripIndent :: String -> String
stripIndent (' ':cs) = stripIndent cs
stripIndent cs = cs
stripComment :: String -> String
stripComment [] = []
stripComment ('-':'-':s) = []
stripComment ('"':s) = '"' : stripCommentInString s
stripComment (c:s) = c : stripComment s
stripCommentInString :: String -> String
stripCommentInString [] = error "In string at end of line"
stripCommentInString ('"':s) = '"' : stripComment s
stripCommentInString (c:s) = c : stripCommentInString s
flatten :: [String] -> String
flatten ls = concat $ intersperse "\n" $ flatten' ls 0
where
rep n i = take n (repeat i)
flatten' [] level = [rep level '}']
flatten' (s:ss) level
| stripped == "" = "" : flatten' ss level
| newLevel > level = (rep (newLevel - level) '{' ++ stripped) : rest
| newLevel < level = (rep (level - newLevel) '}' ++ stripped) : rest
| otherwise = stripped : rest
where newLevel = countIndent s
stripped' = stripComment s
stripped = if stripIndent stripped' == "" then "" else (stripped' ++ "@")
rest = flatten' ss newLevel
-- -------------------------------------------------------------
-- XXX Doesn't handle preprocessor instructions.
preprocess :: String -> String
preprocess d = flatten $ lines (d ++ "\n" ++ mainMarker)
readSource :: String -> IO String
readSource fn = do
f <- IO.openFile fn IO.ReadMode
d <- IO.hGetContents f
let prep = preprocess d
return prep
-- -------------------------------------------------------------
parseSource :: String -> String -> N.Node
parseSource prep sourceFileName
= case (parse sourceFile sourceFileName prep) of
Left err -> error ("Parsing error: " ++ (show err))
Right defs -> defs

View File

@ -1,23 +0,0 @@
-- Defining and running passes across some type of tree
module Pass where
import Control.Monad.State
import Data.Generics
import PrettyShow
type Progress = (String -> IO ())
type Pass t = t -> t
type PassList t = [(String, Pass t)]
runPasses :: Data t => PassList t -> Progress -> t -> IO t
runPasses [] _ d = return d
runPasses ((name, pass):ps) progress d = do
progress $ "{{{ Pass: " ++ name
let d' = pass d
progress $ pshow d'
progress $ "}}}"
runPasses ps progress d'

View File

@ -1,49 +0,0 @@
-- A generic show implementation that pretty-prints expressions
-- This ought to use a class (like show does), so that it can be extended
-- properly without me needing to have FCO-specific cases in here -- see the
-- appropriate SYB paper.
module PrettyShow (pshow) where
import Data.Generics
import Text.PrettyPrint.HughesPJ
import Metadata
-- This is ugly -- but it looks like you can't easily define a generic function
-- even for a single tuple type, since it has to parameterise over multiple Data
-- types...
isTupleCtr :: String -> Bool
isTupleCtr ('(':cs) = checkRest cs
where
checkRest ",)" = True
checkRest (',':cs) = checkRest cs
checkRest _ = False
isTupleCtr _ = False
doGeneral :: Data a => a -> Doc
doGeneral t =
if isTupleCtr cn then
parens $ sep $ punctuate (text ",") l
else case l of
[] -> con
otherwise -> parens $ sep (con : l)
where
cn = showConstr (toConstr t)
con = text $ cn
l = gmapQ doAny t
doList :: Data a => [a] -> Doc
doList t = brackets $ sep $ punctuate (text ",") (map doAny t)
doString :: String -> Doc
doString s = text $ show s
doMeta :: Meta -> Doc
doMeta m = text $ formatSourcePos m
doAny :: Data a => a -> Doc
doAny = doGeneral `ext1Q` doList `extQ` doString `extQ` doMeta
pshow :: Data a => a -> String
pshow x = render $ doAny x

View File

@ -1,274 +0,0 @@
-- Lisp-style s-expression support
module SExpression where
import List
import qualified PT as N
import Text.PrettyPrint.HughesPJ
data SExp = Item String | List [SExp]
sexpToDoc :: SExp -> Doc
sexpToDoc (Item s) = text s
sexpToDoc (List is) = parens $ sep (map sexpToDoc is)
instance Show SExp where
show s = render $ sexpToDoc s
dyadicName :: N.Node -> String
dyadicName (N.Node meta node) = case node of
N.Add -> "+"
N.Subtr -> "-"
N.Mul -> "*"
N.Div -> "/"
N.Rem -> "mod"
N.Plus -> "plus"
N.Minus -> "minus"
N.Times -> "times"
N.BitAnd -> "bitand"
N.BitOr -> "bitor"
N.BitXor -> "bitxor"
N.And -> "and"
N.Or -> "or"
N.Eq -> "="
N.NEq -> "<>"
N.Less -> "<"
N.More -> ">"
N.LessEq -> "<="
N.MoreEq -> ">="
N.After -> "after"
monadicName :: N.Node -> String
monadicName (N.Node meta node) = case node of
N.MonSub -> "-"
N.MonBitNot -> "bitnot"
N.MonNot -> "not"
N.MonSize -> "size"
nodeToSExp :: N.Node -> SExp
nodeToSExp (N.Node meta node)
= case node of
N.Decl a b -> wrap2 ":" (top a) (top b)
N.Alt a -> wrapl "alt" (map top a)
N.AltRep a b -> wrap2 "alt-rep" (top a) (top b)
N.PriAlt a -> wrapl "pri-alt" (map top a)
N.PriAltRep a b -> wrap2 "pri-alt-rep" (top a) (top b)
N.In a (N.Node _ (N.InSimple b)) -> wrapl1 "?" (top a) (map top b)
N.Variant a b -> wrap2 "variant" (top a) (top b)
N.In a (N.Node _ (N.InCase b)) -> wrapl1 "?case" (top a) (map top b)
N.In a (N.Node _ (N.InTag b)) -> wrap2 "?case-tag" (top a) (top b)
N.In a (N.Node _ (N.InAfter b)) -> wrap2 "?after" (top a) (top b)
N.Out a b -> wrapl1 "!" (top a) (map top b)
N.OutCase a b c -> wrapl2 "!case" (top a) (top b) (map top c)
N.ExpList a -> wrapl "exp-list" (map top a)
N.Assign a b -> wrap2 ":=" (List $ map top a) (top b)
N.If a -> wrapl "if" (map top a)
N.IfRep a b -> wrap2 "if-rep" (top a) (top b)
N.While a b -> wrap2 "while" (top a) (top b)
N.Par a -> wrapl "par" (map top a)
N.ParRep a b -> wrap2 "par-rep" (top a) (top b)
N.PriPar a -> wrapl "pri-par" (map top a)
N.PriParRep a b -> wrap2 "pri-par-rep" (top a) (top b)
N.PlacedPar a -> wrapl "placed-par" (map top a)
N.PlacedParRep a b -> wrap2 "placed-par-rep" (top a) (top b)
N.Processor a b -> wrap2 "processor" (top a) (top b)
N.Skip -> Item "skip"
N.Stop -> Item "stop"
N.Case a b -> wrapl1 "case" (top a) (map top b)
N.Seq a -> wrapl "seq" (map top a)
N.SeqRep a b -> wrap2 "seq-rep" (top a) (top b)
N.ProcCall a b -> wrapl1 "proc-call" (top a) (map top b)
N.MainProcess -> Item "main"
N.Vars a b -> wrapl1 "vars" (top a) (map top b)
N.Is a b -> wrap2 "is" (top a) (top b)
N.IsType a b c -> wrap3 "is-type" (top a) (top b) (top c)
N.ValIs a b -> wrap2 "val-is" (top a) (top b)
N.ValIsType a b c -> wrap3 "val-is-type" (top a) (top b) (top c)
N.Place a b -> wrap2 "place-at" (top a) (top b)
N.DataType a b -> wrap2 "data-type" (top a) (top b)
N.Record a -> wrapl "record" (map top a)
N.PackedRecord a -> wrapl "packed-record" (map top a)
N.Fields a b -> wrapl1 "fields" (top a) (map top b)
N.Protocol a b -> wrapl1 "protocol" (top a) (map top b)
N.TaggedProtocol a b -> wrapl1 "protocol-tagged" (top a) (map top b)
N.Tag a b -> wrapl1 "tag" (top a) (map top b)
N.Formals a b -> wrapl1 "formal" (top a) (map top b)
N.Proc a b c -> wrap3 "proc" (top a) (List $ map top b) (top c)
N.Func a b c d -> wrap4 "function" (top a) (List $ map top b) (List $ map top c) (top d)
N.FuncIs a b c d -> wrap4 "function-is" (top a) (List $ map top b) (List $ map top c) (top d)
N.Retypes a b c -> wrap3 "retypes" (top a) (top b) (top c)
N.ValRetypes a b c -> wrap3 "val-retypes" (top a) (top b) (top c)
N.Reshapes a b c -> wrap3 "reshapes" (top a) (top b) (top c)
N.ValReshapes a b c -> wrap3 "val-reshapes" (top a) (top b) (top c)
N.ValOf a b -> wrap2 "valof" (top a) (top b)
N.Sub (N.Node _ (N.SubPlain b)) a -> wrap2 "sub" (top a) (top b)
N.Sub (N.Node _ (N.SubFromFor b c)) a -> wrap3 "sub-from-for" (top a) (top b) (top c)
N.Sub (N.Node _ (N.SubFrom b)) a -> wrap2 "sub-from" (top a) (top b)
N.Sub (N.Node _ (N.SubFor b)) a -> wrap2 "sub-for" (top a) (top b)
N.CaseExps a b -> wrap2 "case-exps" (List $ map top a) (top b)
N.Else a -> wrap "else" (top a)
N.For a b c -> wrap3 "for" (top a) (top b) (top c)
N.Conv a b -> wrap2 "conv" (top a) (top b)
N.Round a b -> wrap2 "round" (top a) (top b)
N.Trunc a b -> wrap2 "trunc" (top a) (top b)
N.DyadicOp o a b -> wrap2 (dyadicName o) (top a) (top b)
N.MonadicOp o a -> wrap (monadicName o) (top a)
N.MostPos a -> wrap "mostpos" (top a)
N.MostNeg a -> wrap "mostneg" (top a)
N.Size a -> wrap "size" (top a)
N.Call a b -> wrapl1 "call" (top a) (map top b)
N.BytesIn a -> wrap "bytesin" (top a)
N.OffsetOf a b -> wrap2 "offsetof" (top a) (top b)
N.Guard a b -> wrap2 "guard" (top a) (top b)
N.CondGuard a b -> wrap2 "cond" (top a) (top b)
N.Choice a b -> wrap2 "choice" (top a) (top b)
N.Val a -> wrap "val" (top a)
N.ChanOf a -> wrap "chan" (top a)
N.PortOf a -> wrap "port" (top a)
N.Timer -> Item "timer"
N.Array a b -> wrap2 "array" (top a) (top b)
N.ArrayUnsized a -> wrap "array-unsized" (top a)
N.Counted a b -> wrap2 "::" (top a) (top b)
N.Bool -> Item "bool"
N.Byte -> Item "byte"
N.Int -> Item "int"
N.Int16 -> Item "int16"
N.Int32 -> Item "int32"
N.Int64 -> Item "int64"
N.Real32 -> Item "real32"
N.Real64 -> Item "real64"
N.Any -> Item "any"
N.TypedLit a b -> wrap2 "typed-literal" (top a) (top b)
N.LitReal a -> wrap "real-literal" (Item a)
N.LitInt a -> wrap "integer-literal" (Item a)
N.LitHex a -> wrap "hex-literal" (Item a)
N.LitByte a -> wrap "byte-literal" (Item ("'" ++ a ++ "'"))
N.LitString a -> wrap "string-literal" (Item ("\"" ++ a ++ "\""))
N.LitArray a -> wrapl "array-literal" (map top a)
N.True -> Item "true"
N.False -> Item "false"
N.Name a -> wrap "name" (Item a)
_ -> error $ "Unsupported node: " ++ (show node)
where top = nodeToSExp
wrap name arg = List [Item name, arg]
wrap2 name arg1 arg2 = List [Item name, arg1, arg2]
wrap3 name arg1 arg2 arg3 = List [Item name, arg1, arg2, arg3]
wrap4 name arg1 arg2 arg3 arg4 = List [Item name, arg1, arg2, arg3, arg4]
wrapl name args = List ((Item name) : args)
wrapl1 name arg1 args = List ((Item name) : arg1 : args)
wrapl2 name arg1 arg2 args = List ((Item name) : arg1 : arg2 : args)
nodeToSOccam :: N.Node -> SExp
nodeToSOccam (N.Node meta node)
= case node of
N.Decl a b -> wrap2 ":" (top a) (top b)
N.Alt a -> wrapl "alt" (map top a)
N.AltRep a b -> wrap2 "alt" (top a) (top b)
N.PriAlt a -> wrapl "pri-alt" (map top a)
N.PriAltRep a b -> wrap2 "pri-alt" (top a) (top b)
N.In a (N.Node _ (N.InSimple b)) -> wrapl1 "?" (top a) (map top b)
N.Variant a b -> wrap2 "variant" (top a) (top b)
N.In a (N.Node _ (N.InCase b)) -> wrapl1 "?case" (top a) (map top b)
N.In a (N.Node _ (N.InTag b)) -> wrap2 "?case-tag" (top a) (top b)
N.In a (N.Node _ (N.InAfter b)) -> wrap2 "?after" (top a) (top b)
N.Out a b -> wrapl1 "!" (top a) (map top b)
N.OutCase a b c -> wrapl2 "!case" (top a) (top b) (map top c)
N.ExpList a -> List (map top a)
N.Assign a b -> wrap2 ":=" (List $ map top a) (top b)
N.If a -> wrapl "if" (map top a)
N.IfRep a b -> wrap2 "if" (top a) (top b)
N.While a b -> wrap2 "while" (top a) (top b)
N.Par a -> wrapl "par" (map top a)
N.ParRep a b -> wrap2 "par" (top a) (top b)
N.PriPar a -> wrapl "pri-par" (map top a)
N.PriParRep a b -> wrap2 "pri-par" (top a) (top b)
N.PlacedPar a -> wrapl "placed-par" (map top a)
N.PlacedParRep a b -> wrap2 "placed-par" (top a) (top b)
N.Processor a b -> wrap2 "processor" (top a) (top b)
N.Skip -> Item "skip"
N.Stop -> Item "stop"
N.Case a b -> wrapl1 "case" (top a) (map top b)
N.Seq a -> wrapl "seq" (map top a)
N.SeqRep a b -> wrap2 "seq" (top a) (top b)
N.ProcCall a b -> List ((top a) : (map top b))
N.MainProcess -> Item "main"
N.Vars a b -> List ((top a) : (map top b))
N.Is a b -> wrap2 "is" (top a) (top b)
N.IsType a b c -> wrap3 "is" (top a) (top b) (top c)
N.ValIs a b -> wrap2 "val-is" (top a) (top b)
N.ValIsType a b c -> wrap3 "val-is" (top a) (top b) (top c)
N.Place a b -> wrap2 "place-at" (top a) (top b)
N.DataType a b -> wrap2 "data-type" (top a) (top b)
N.Record a -> wrapl "record" (map top a)
N.PackedRecord a -> wrapl "packed-record" (map top a)
N.Fields a b -> List ((top a) : (map top b))
N.Protocol a b -> wrapl1 "protocol" (top a) (map top b)
N.TaggedProtocol a b -> wrapl1 "protocol" (top a) (map top b)
N.Tag a b -> List ((top a) : (map top b))
N.Formals a b -> List ((top a) : (map top b))
N.Proc a b c -> wrap3 "proc" (top a) (List $ map top b) (top c)
N.Func a b c d -> wrap4 "function" (top a) (List $ map top b) (List $ map top c) (top d)
N.FuncIs a b c d -> wrap4 "function-is" (top a) (List $ map top b) (List $ map top c) (top d)
N.Retypes a b c -> wrap3 "retypes" (top a) (top b) (top c)
N.ValRetypes a b c -> wrap3 "val-retypes" (top a) (top b) (top c)
N.Reshapes a b c -> wrap3 "reshapes" (top a) (top b) (top c)
N.ValReshapes a b c -> wrap3 "val-reshapes" (top a) (top b) (top c)
N.ValOf a b -> wrap2 "valof" (top a) (top b)
N.Sub (N.Node _ (N.SubPlain b)) a -> wrap2 "sub" (top a) (top b)
N.Sub (N.Node _ (N.SubFromFor b c)) a -> wrap3 "sub-from-for" (top a) (top b) (top c)
N.Sub (N.Node _ (N.SubFrom b)) a -> wrap2 "sub-from" (top a) (top b)
N.Sub (N.Node _ (N.SubFor b)) a -> wrap2 "sub-for" (top a) (top b)
N.CaseExps a b -> l2 (List $ map top a) (top b)
N.Else a -> wrap "else" (top a)
N.For a b c -> wrap3 "for" (top a) (top b) (top c)
N.Conv a b -> wrap2 "conv" (top a) (top b)
N.Round a b -> wrap2 "round" (top a) (top b)
N.Trunc a b -> wrap2 "trunc" (top a) (top b)
N.DyadicOp o a b -> wrap2 (dyadicName o) (top a) (top b)
N.MonadicOp o a -> wrap (monadicName o) (top a)
N.MostPos a -> wrap "mostpos" (top a)
N.MostNeg a -> wrap "mostneg" (top a)
N.Size a -> wrap "size" (top a)
N.Call a b -> wrapl1 "call" (top a) (map top b)
N.BytesIn a -> wrap "bytesin" (top a)
N.OffsetOf a b -> wrap2 "offsetof" (top a) (top b)
N.Guard a b -> List [top a, top b]
N.CondGuard a b -> wrap2 "cond" (top a) (top b)
N.Choice a b -> List [top a, top b]
N.Val a -> wrap "val" (top a)
N.ChanOf a -> wrap "chan" (top a)
N.PortOf a -> wrap "port" (top a)
N.Timer -> Item "timer"
N.Array a b -> wrap2 "array" (top a) (top b)
N.ArrayUnsized a -> wrap "array" (top a)
N.Counted a b -> wrap2 "::" (top a) (top b)
N.Bool -> Item "bool"
N.Byte -> Item "byte"
N.Int -> Item "int"
N.Int16 -> Item "int16"
N.Int32 -> Item "int32"
N.Int64 -> Item "int64"
N.Real32 -> Item "real32"
N.Real64 -> Item "real64"
N.Any -> Item "any"
N.TypedLit a b -> l2 (top a) (top b)
N.LitReal a -> Item a
N.LitInt a -> Item a
N.LitHex a -> Item a
N.LitByte a -> Item ("'" ++ a ++ "'")
N.LitString a -> Item ("\"" ++ a ++ "\"")
N.LitArray a -> List (map top a)
N.True -> Item "true"
N.False -> Item "false"
N.Name a -> Item a
_ -> error $ "Unsupported node: " ++ (show node)
where top = nodeToSOccam
wrap name arg = List [Item name, arg]
wrap2 name arg1 arg2 = List [Item name, arg1, arg2]
wrap3 name arg1 arg2 arg3 = List [Item name, arg1, arg2, arg3]
wrap4 name arg1 arg2 arg3 arg4 = List [Item name, arg1, arg2, arg3, arg4]
wrapl name args = List ((Item name) : args)
wrapl1 name arg1 args = List ((Item name) : arg1 : args)
wrapl2 name arg1 arg2 args = List ((Item name) : arg1 : arg2 : args)
l2 arg1 arg2 = List [arg1, arg2]

View File

@ -1,21 +0,0 @@
all: writeup.dvi writeup.pdf fco-pres.dvi fco-pres.pdf
LATEX = latex -interaction=nonstopmode
writeup.dvi: writeup.tex the.bib
rm -f writeup.bbl
$(LATEX) writeup.tex
bibtex writeup
$(LATEX) writeup.tex
$(LATEX) writeup.tex
rm -f writeup.aux writeup.bbl writeup.blg writeup.log writeup.toc
fco-pres.dvi: fco-pres.tex
$(LATEX) fco-pres.tex
$(LATEX) fco-pres.tex
$(LATEX) fco-pres.tex
rm -f fco-pres.aux fco-pres.log fco-pres.toc
%.pdf: %.dvi
dvipdf $<

View File

@ -1,85 +0,0 @@
%==============================================================================
% PPRadam.sty by Adam Sampson <ats@offog.org> -- based on:
% Prosper -- (PPRframes.sty) Style file
% A LaTeX class for creating slides
% Author: Frederic Goualard (Frederic.Goualard@irin.univ-nantes.fr)
% Institut de Recherche en Informatique de Nantes
% University of Nantes, France
%
% Copyright (c) 2000 Frederic Goualard
% All rights reserved.
%
% Permission is hereby granted, without written agreement and without
% license or royalty fees, to use, copy, modify, and distribute this
% software and its documentation for any purpose, provided that the
% above copyright notice and the following two paragraphs appear in
% all copies of this software.
%
% IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
% SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF
% THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE AUTHOR HAS BEEN ADVISED
% OF THE POSSIBILITY OF SUCH DAMAGE.
%
%
% THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES,
% INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
% AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
% ON AN "AS IS" BASIS, AND THE AUTHOR HAS NO OBLIGATION TO
% PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
%
% CVSId : $Id: PPRadam.sty,v 1.1 2005/09/13 18:47:50 azz Exp $
%==============================================================================
\NeedsTeXFormat{LaTeX2e}[1995/12/01]
\ProvidesPackage{PPRadam}[2006/09/15]
\typeout{`Adam' style for prosper ---}
\typeout{(c) 2000 Frederic Goualard, IRIN, France, 2005, 2006 Adam Sampson}
\typeout{CVSId: $Id: PPRadam.sty,v 1.1 2005/09/13 18:47:50 azz Exp $}
\typeout{ }
\RequirePackage{semhelv}
\RequirePackage{amssymb}
% Chargement des fichiers de pstricks (on teste les noms en vigueur pour
% gérer les différentes versions de pstricks).
\IfFileExists{pst-grad}{\RequirePackage{pst-grad}}{\RequirePackage{gradient}}
\newgray{gris1}{.40}
\newgray{gris2}{.85}
\newgray{gris3}{.30}
\newgray{gris4}{.25}
\newgray{gris5}{.90}
\FontTitle{\usefont{T1}{phv}{b}{sl}\fontsize{14.4pt}{12pt}\selectfont}{%
\usefont{T1}{phv}{b}{sl}\fontsize{14.4pt}{12pt}\selectfont}
\FontText{\usefont{T1}{phv}{m}{n}\fontsize{13pt}{12pt}\selectfont}{%
\usefont{T1}{phv}{m}{n}\fontsize{13pt}{12pt}\selectfont}
\def\labelitemi{\ensuremath{\blacktriangleright}}
\myitem{1}{\ensuremath{\blacktriangleright}}
\myitem{2}{\ensuremath{\blacktriangleright}}
\myitem{3}{\ensuremath{\blacktriangleright}}
\newcommand{\slidetitle}[1]{%
\rput[rb](11.6,3.6){%
\parbox{9cm}{\begin{flushright}\fontTitle{#1}\end{flushright}}}}
\LogoPosition{-.7,-.1}
\def\TWRFrame#1{%
\psframe[linestyle=none,fillstyle=gradient,gradangle=90,
gradbegin=black,gradend=gris2,gradmidpoint=0.7]%
(-1.2,7)(11.6,7.1)
\PutLogo % Mandatory
{#1}}
\NewSlideStyle[115mm]{t}{5.3,3.0}{TWRFrame}
\PDFCroppingBox{10 40 594 820}
\endinput
%%% Local Variables:
%%% mode: latex
%%% TeX-master: t
%%% End:

View File

@ -1,650 +0,0 @@
\documentclass[adam,pdf,slideColor]{prosper}
\usepackage{graphicx}
\usepackage{pifont}
\usepackage{xspace}
\usepackage{alltt}
\def\occampi{{occam-\Pisymbol{psy}{112}}\xspace}
\def\picalculus{{\Pisymbol{psy}{112}-calculus}\xspace}
\definecolor{Red}{rgb}{0.6,0.0,0.0}
\definecolor{Green}{rgb}{0.0,0.6,0.0}
\definecolor{Blue}{rgb}{0.0,0.0,0.6}
\definecolor{Purple}{rgb}{0.6,0.0,0.6}
\definecolor{Brown}{rgb}{0.5,0.5,0.0}
\def\keyword#1{{\color{Green}#1}}
\def\operator#1{{\color{Purple}#1}}
\def\highlight#1{{\color{Brown}#1}}
\Logo(-1.0,7.3){\includegraphics[width=3cm]{kent.eps}}
\title{Exploring nanopass compilation in Haskell}
\author{Adam Sampson}
\email{{\tt ats1@kent.ac.uk}}
\institution{University of Kent\\ {\tt http://www.cs.kent.ac.uk/}}
\slideCaption{FCO}
\begin{document}
\maketitle
\begin{slide}{Why?}
\begin{itemize}
\item We've been looking at using Scheme
\begin{itemize}
\item Popular in the States, less so over here
\item Good libraries for compilation
\end{itemize}
\item Lots of Haskell users in the lab already
\begin{itemize}
\item \ldots including all our undergrads
\item Also heavily used for compiler work
\end{itemize}
\item Should we use Haskell instead?
\end{itemize}
\end{slide}
\begin{slide}{What I've been doing}
\begin{itemize}
\item Building bits of FCO: a nanopass occam compiler in Haskell
\begin{itemize}
\item Functional Compiler for occam
\end{itemize}
\item This is an exploration -- a ``spike solution''
\item Checking that all the things we want to do are possible
\item Warning: may contain traces of handwaving
\item I'll assume some knowledge of occam and Haskell; if anything's
not clear, please shout
\end{itemize}
\end{slide}
\begin{slide}{Haskell}
\begin{itemize}
\item Mature purely-functional language
\item Open spec; several implementations
\item Powerful static type system
\item Built-in pattern matching
\item Good support for monadic programming
\item Lazy evaluation
\item Supports lightweight concurrency
\item Nice syntax (for occam programmers)
\item Lots of cool recent work (some of which I'll show later)
\item See the ``History of Haskell'' HoPL paper
\end{itemize}
\end{slide}
\begin{slide}{Existing work}
\begin{itemize}
\item occam2.1 language spec -- with a BNF syntax
\item 42 -- the model for this, in Scheme
\item occ21 -- the only full existing implementation, in C
\item JHC -- Haskell to C, in Haskell
\item Pugs -- Perl 6 to various, in Haskell
\item (Also GHC, Mincaml and a few others)
\end{itemize}
\end{slide}
\begin{slide}{A nanopass refresher}
\begin{itemize}
\item Parse language into AST
\item Do many small passes over the AST
\begin{itemize}
\item Checks
\item Annotations
\item Transformations
\end{itemize}
\item Output
\end{itemize}
\end{slide}
\begin{slide}{FCO's target}
\begin{itemize}
\item Translate occam to idiomatic C
\begin{itemize}
\item Use CIF for concurrency
\item Bugbear: calculating stack usage
\end{itemize}
\item Whole-program compilation
\begin{itemize}
\item Allows whole-program optimisations and checks
\item Can still do separate parsing/checks/early passes; just do final
translation at ``link time''
\end{itemize}
\item FCO is not finished, but I believe the approach is sound
\end{itemize}
\end{slide}
\begin{slide}{Challenges}
\begin{itemize}
\item Parsing
\item Data structures
\item Writing transformations
\item Driver and IO
\item Extensibility
\item Debugging and tracing
\end{itemize}
\end{slide}
\begin{slide}{Parsing}
\begin{itemize}
\item 42 hasn't tackled this yet
\item occam2.1 has some oddities:
\begin{itemize}
\item Lots of lookahead needed
\item Indentation-based syntax
\item Odd line-continuation rules
\item Ambiguities
\item Left-recursive productions
\end{itemize}
\item occ21's parser keeps lots of state; we don't want to do that
\end{itemize}
\end{slide}
\begin{slide}{Parsing by passes}
\begin{itemize}
\item Break down ``parsing'' into several passes:
\begin{itemize}
\item Execute (some) preprocessor instructions
\item Detect and rejoin continuation lines
\item Convert indentation changes to tokens
\item \ldots then just use a regular parser
\item Resolve ambiguities later
\end{itemize}
\item (\verb|occamdoc| does much of this already)
\end{itemize}
\end{slide}
\begin{slide}{Parsing by Parsec}
\begin{itemize}
\item A combinator-based parsing library for Haskell
\item Productions look pretty much like BNF, e.g.:
\begin{verbatim}
specification = declaration
<|> abbreviation
<|> definition
sequence = do { sSEQ ; eol ; indent ;
ps <- many1 process ;
...
\end{verbatim}
\item Uses Prolog-style backtracking and cuts -- no lookahead problems
\item All done using monads
\end{itemize}
\end{slide}
\begin{slide}{Monads}
\begin{itemize}
\item A data type that wraps a value
\item A monadic function can return a value and/or change the wrapper
\item For example:
\begin{itemize}
\item \verb|State| monad contains a user-defined state value
\item \verb|IO| monad has ``state of the world'' (so IO functions
can interact with the world)
\item \verb|Parser| monad (from Parsec) keeps track of file location,
backtracking, etc.
\end{itemize}
\end{itemize}
\end{slide}
\begin{slide}{Monad combinators}
\begin{itemize}
\item Combinators let you chain monadic functions together
\item Each monad defines how the ``bind'' operator (a standard
combinator) works -- e.g. pass state through, backtrack upon
failure, etc.
\item Haskell has special syntax (\verb|do| blocks) for bind
\item Monads can define their own combinators (e.g. \verb,<|>, in Parsec)
\end{itemize}
\end{slide}
\begin{slide}{Monad example}
\begin{itemize}
\item Function in \verb|State| monad that returns unique ID
\item The \verb|get| and \verb|put| functions come from the monad
\item \verb|return| wraps a value in the monad
\item \verb|do| block chains the three functions together
\end{itemize}
\begin{verbatim}
-- Define our own monad based on State.
-- The state is a single Int.
type UniqueState t = State Int t
-- This returns an Int in our monad.
getID :: UniqueState Int
getID = do counter <- get
put (counter + 1)
return counter
\end{verbatim}
\end{slide}
\begin{slide}{Ambiguities}
\begin{itemize}
\item Two ambiguities in occam 2.1 syntax as specified in the
manual
\item \verb|c ! x ; y|
\begin{itemize}
\item Is \verb|x| a tag or a variable name?
\end{itemize}
\item \verb|foo[x]|
\begin{itemize}
\item Is this an array element, or\ldots
\item \ldots is this retyping the array literal \verb|[x]| to the
user-defined type \verb|foo|?
\item You can't retype an array -- fix the grammar to reflect this
\end{itemize}
\item Any more? What about \occampi?
\end{itemize}
\end{slide}
\begin{slide}{Data structures}
\begin{itemize}
\item Represent an AST node
\item Metadata
\begin{itemize}
\item Source position
\item Annotations from passes
\end{itemize}
\item In occ21 and 42, a record for each node type
\item In Haskell, I've tried two approaches
\end{itemize}
\end{slide}
\begin{slide}{Data structures: ``parse tree''}
\begin{itemize}
\item A catch-all \verb|Node| ADT
\item \verb|Meta| contains metadata
\begin{verbatim}
data Node = (Meta, NodeType)
data NodeType = Seq [Node]
| AltGuard Node Node
| Name String
| ...
\end{verbatim}
\item Pattern matching's easy
\item Straightforward to write traversal code
\item This feels a bit non-Haskell-ish
\end{itemize}
\end{slide}
\begin{slide}{Data structures: ``AST''}
\begin{itemize}
\item One ADT per production (with simplifications)
\begin{verbatim}
data Process = Seq Meta [Process]
| Alt Meta [AltGrd]
| Assign [Var] ExpList
| Skip Meta | ...
data AltGrd = AltGrd Meta Guard Process
| ...
\end{verbatim}
\item This is what other compilers do
\item \ldots but writing code to process it is harder
\item (I'll come back to this later)
\end{itemize}
\end{slide}
\begin{slide}{Transformations}
\begin{itemize}
\item Walk over the tree, finding interesting bits of it and Doing
Stuff to them
\item Some bits are purely functional; some must carry state
\item Checks and annotations are just transformations that don't
change the tree
\item 42 does this using PLT's pattern-matching library
\item Can we use Haskell's built-in pattern-matching?
\end{itemize}
\end{slide}
\begin{slide}{Pattern-matching}
\begin{itemize}
\item Yes! At least, it's trivial for the \verb|Node| data structure:
\begin{verbatim}
myPass n = case n of
Seq ps -> doSeq (map myPass ps)
Name s -> doName s
...
\end{verbatim}
\item Writing the boilerplate like that for every pass is rather
boring (and hard to extend), though
\end{itemize}
\end{slide}
\begin{slide}{Base passes}
\begin{itemize}
\item Rather than direct recursion, have a
``base pass'' function
\begin{verbatim}
basePass top n = case n of
Seq ps -> Seq (map top ps)
Name s -> Name s
\end{verbatim}
\item Then every pass can use that:
\begin{verbatim}
doStuff n = case n of
Thing a -> doThing (pt a)
otherwise -> pt a
where pt = basePass doStuff
\end{verbatim}
\item Slightly fancier glue for error handling, etc.
\end{itemize}
\end{slide}
\begin{slide}{Generated base passes}
\begin{itemize}
\item No need to write the base passes by hand
\item Generate them automatically from the data type definition (using
Template Haskell, or simpler approaches)
\item Can generate several base passes for different subsets of the
allowed productions
\end{itemize}
\end{slide}
\begin{slide}{The bad news}
\begin{itemize}
\item However, this all only works because we're restricted to one
data type
\item And complicated passes get very messy\ldots
\item How do we do this for the ``AST'' data types?
\item We'd need to write \emph{generic} functions that would work on
several data types
\item Fortunately\ldots
\end{itemize}
\end{slide}
\begin{slide}{Scrap Your Boilerplate}
\begin{itemize}
\item The ``SYB'' Generics package lets you turn:
\begin{verbatim}
foo :: Process -> Process
\end{verbatim}
into:
\begin{verbatim}
foo :: Typeable a => a -> a
\end{verbatim}
\ldots a function you can apply to any \verb|Typeable| type
\item A \emph{really} cute hack to provide introspection and dynamic
typing (\verb|cast|) in Haskell
\end{itemize}
\end{slide}
\begin{slide}{An aside on typeclasses}
\begin{itemize}
\item Typeclasses are really nothing like OO classes
\item A typeclass is an interface: a set of functions
\begin{itemize}
\item The \verb|Show| typeclass provides the \verb|show| function
\item \verb|Eq| provides equality tests
\item \verb|Typeable| provides ``what's the type of this value?''
\end{itemize}
\item If a type is an instance of a typeclass, then it has
implementations of all that typeclass's functions for that type
\item The Haskell compiler can ``derive'' instances of the built-in
typeclasses (including \verb|Typeable| and \verb|Data|) automatically
\end{itemize}
\end{slide}
\begin{slide}{Scrap More Boilerplate}
\begin{itemize}
\item These functions do not automatically recurse
\item Instead, you get \verb|gmapT| -- ``generic map'', which peers
carefully at the value's type, and maps across all the type
constructor arguments
\item For example:
\end{itemize}
\begin{verbatim}
data Thing = Thing Foo Bar Baz
deriving (Typeable, Data)
v :: Thing -- some Thing
t :: Typeable a => a -> a -- a transform
f = gmap t v
f' (Thing x y z) = Thing (t x) (t y) (t z)
\end{verbatim}
\begin{itemize}
\item \verb|f| and \verb|f'| are equivalent -- but f is itself generic.
\end{itemize}
\end{slide}
\begin{slide}{Scrap The Whole Boiler}
\begin{itemize}
\item Using this, you can write a function that'll recurse across an
entire data structure, applying the transform wherever it can
\item \ldots and, actually, they give it to you -- \verb|everywhere|
\item You also get functions to extend a generic transform with a
type-specific case
\begin{verbatim}
doAny = doGen `extT` doPar
`extT` doSeq ...
\end{verbatim}
\item There are monadic and query (i.e. returning a result rather than
doing a transformation) versions of all these facilities too
\end{itemize}
\end{slide}
\begin{slide}{Scrapping My Boilerplate}
\begin{itemize}
\item By making \verb|Node| derive \verb|Data|, we can do away with
the base passes in favour of generic functions
\item This works for any set of ADTs
\item For example:
\begin{verbatim}
doName :: Name -> Name
doName (Name s) =
Name [if c == '.' then '_' else c
| c <- s]
cIshNamesPass = everywhere (mkT doName)
\end{verbatim}
\item This can be applied to a \verb|Name|, \verb|Process|,
\verb|Declaration|, etc. and works recursively
\end{itemize}
\end{slide}
\begin{slide}{Scrap Your Boilerstate}
\begin{itemize}
\item Passes that need state can use the \verb|State| monad
\item For example, tracking variable scope
\item I have a marvellous example of this but this slide is too small
to contain it -- see the real code
\item The ``unique names'' pass is rather more concise in FCO than
in 42 owing to the use of generics
\end{itemize}
\end{slide}
\begin{slide}{Driver and IO}
\begin{itemize}
\item The code that sets up and runs all the bits of the compiler, and
interacts with the OS
\item Dead easy in Haskell -- very good OS interfacing
\item Worth cribbing from for \occampi's standard libraries\ldots
\item We have lists of passes to apply (like 42)
\item Passes are functions from one tree to another
\item The tree is pretty-printed after each pass (in debug mode)
\end{itemize}
\end{slide}
\begin{slide}{Extensibility}
\begin{itemize}
\item Want to be able to add:
\begin{itemize}
\item New AST node types
\item New passes
\end{itemize}
\item Also accessibility, really
\begin{itemize}
\item Undergrads should be able to write extensions
\end{itemize}
\end{itemize}
\end{slide}
\begin{slide}{Extensibility problems}
\begin{itemize}
\item Slotting in new passes is pretty trivial
\item Creating new types of nodes is harder
\item You can't extend an ADT
\item More generally, transformations can't replace a value with a
different type of value
\item Several ways we could get around this\ldots
\end{itemize}
\end{slide}
\begin{slide}{Extensibility: ``plan ahead''}
\begin{itemize}
\item We could\ldots
\item Include everything you could ever possibly want in your data
types ahead of time
\item Doable with \verb|Node|
\begin{itemize}
\item Early version of FCO extracted comment annotations in
the \verb|Node| definition to decide which passes each node type was
valid in
\item Metaprogramming -- you can do it (Template Haskell), but it's a
bit experimental\ldots
\end{itemize}
\item Really nasty with lots of ADTs
\end{itemize}
\end{slide}
\begin{slide}{Extensibility: ``whatEVERRRRR''}
\begin{itemize}
\item We could\ldots
\item Use a very general data type
\begin{verbatim}
data Node = Node String Meta [Node]
\end{verbatim}
\item Emulating dynamic typing
\item So why use a statically-typed language?
\end{itemize}
\end{slide}
\begin{slide}{Extensibility: ``from time to time''}
\begin{itemize}
\item We could\ldots
\item Have several sets of ADTs
\item When we need to change the language, have a big transformation
pass between them
\item This is what JHC/Pugs do (and what FCO does currently)
\item Not really nanopass any more
\begin{itemize}
\item The transformation passes aren't necessarily entirely mechanical
\end{itemize}
\item Awkward to extend
\end{itemize}
\end{slide}
\begin{slide}{Extensibility: ``deep magic''}
\begin{itemize}
\item We could\ldots
\item Use typeclasses and existential types (a la \verb|HList|)
\item Doable but very messy
\item Could require generating class instances automatically (using
DrIFT or similar) as ``glue''
\item Not clear how this interacts with generics
\item Makes pattern-matching awkward (impossible?)
\end{itemize}
\end{slide}
\begin{slide}{Extensibility: the big worry}
\begin{itemize}
\item I feel like I'm fighting the type system\ldots
\item Is the nanopass approach fundamentally incompatible with static
typing?
\item Or, alternately, is the Haskell type system insufficiently
powerful to express it cleanly?
\begin{itemize}
\item Given the Haskell type system is one of the most powerful out
there, these two are essentially equivalent
\end{itemize}
\item Is there some clean approach to this that I'm not seeing?
\end{itemize}
\end{slide}
\begin{slide}{Debugging}
\begin{itemize}
\item Even with static typing, many programs do not work right first
time \verb|:-)|
\item Haskell lacks debugging facilities
\item Lazy evaluation can make debugging harder
\item At the very least, we need tracing (as 42 has) -- ``\verb|printf|
debugging''
\item To do that, you need to use another monad
\end{itemize}
\end{slide}
\begin{slide}{Monad transformers}
\begin{itemize}
\item Much of our code will be running in a monad already
\item \ldots but if you want to use more than one monad?
\item Monad transformers let you ``stack up'' monads (e.g. State
around IO)
\item Getting at actions in the ``inner'' monads is slightly awkward
(\verb|liftM|)
\item \ldots and we want tracing, plus error-handling, plus the monads
each pass needs -- perhaps four stacked monads in some code
\item Lots of wrapper functions
\begin{verbatim}
trace' = liftM . liftM . liftM . trace
\end{verbatim}
\end{itemize}
\end{slide}
\begin{slide}{Tracing}
\begin{itemize}
\item Awkward to insert tracing into monadic code
\item \ldots but it's much more awkward to trace (or support failure
in) functional code, because you have to first make it monadic
\item Can't be hidden with wrappers
\item \ldots so, in practice, everything becomes monadic
\item A downside of the purely functional approach
\end{itemize}
\end{slide}
\begin{slide}{Conclusions}
\begin{itemize}
\item Haskell's a really elegant language\ldots
\item \ldots but I don't think it's suited to this particular problem
\item A number of useful things have come out of FCO, though\ldots
\end{itemize}
\end{slide}
\begin{slide}{Lessons}
\begin{itemize}
\item Decomposing the parser makes it simpler
\item Use a backtracking parser
\item Look at the full language
\begin{itemize}
\item Subtleties can require big changes
\item Can test on real code
\end{itemize}
\item Take advantage of generic/introspective programming (perhaps
with Scheme record annotations)
\item Consider doing occam-to-C translation
\begin{itemize}
\item At a higher level than the VM
\end{itemize}
\end{itemize}
\end{slide}
\begin{slide}{And finally}
\begin{itemize}
\item Any questions?
\end{itemize}
\end{slide}
\begin{slide}{References}
\begin{itemize}
\item The code's on my web site: \\
\verb|http://offog.org/darcs/research/fco/|
\item Wikibooks Haskell tutorial: \\
\verb|http://en.wikibooks.org/wiki/Haskell|
\item History of Haskell: \\
{\tiny \verb|http://haskell.org/haskellwiki/History_of_Haskell|}
\item JHC: \\
\verb|http://repetae.net/john/computer/jhc/|
\item Pugs: \\
\verb|http://www.pugscode.org/|
\item Scrap Your Boilerplate: \\
\verb|http://www.cs.vu.nl/boilerplate/|
\end{itemize}
\end{slide}
\end{document}

Binary file not shown.

View File

@ -1,14 +0,0 @@
@Article{syb1,
author = "Ralf L{\"a}mmel and Simon {Peyton Jones}",
title = "Scrap your boilerplate:
a practical design pattern for generic programming",
journal = "ACM SIG{\-}PLAN Notices",
publisher = "ACM Press",
volume = "38",
number = "3",
pages = "26--37",
month = mar,
year = "2003",
note = "Proceedings of the ACM SIGPLAN Workshop
on Types in Language Design and Implementation (TLDI~2003)"
}

View File

@ -1,209 +0,0 @@
\documentclass[a4paper,12pt]{article}
\usepackage{times}
\usepackage{a4wide}
\usepackage{xspace}
\def\occam{{\sffamily occam}\xspace}
\def\occampi{{\sffamily occam-\Pisymbol{psy}{112}}\xspace}
\begin{document}
\title{Compiling \occam using Haskell}
\author{Adam Sampson}
\maketitle
\section{Introduction}
This is the ongoing story of FCO, a functional compiler for \occam.
FCO is a spike solution (albeit a fairly elaborate one): the aim is to
implement just enough of a compiler in Haskell to tell us whether
using it for a proper compiler would be a sensible idea.
The result is that my goal is fairly modest: FCO translates an \occam 2.1
subset into ANSI C, using CIF for concurrency facilities. It should
support enough of the \occam language to do commstime and q7-ats1.
By design, FCO is a whole-program compiler: it does not support separate
compilation of libraries. The downside is that you need to have the
AST for the entire program, including the standard library, available at
code-generation time; the major upside for now is that it's much easier
to write. I believe that the whole-program strategy may be worth
pursuing in a production compiler, since it would also allow
whole-program optimisations and specialisations; it need not cause
horrendous performance problems, since libraries can still be parsed and
usage-checked ahead of time.
I'll assume the reader has some knowledge of both \occam and Haskell; if
there's anything that's not clear, please let me know. I'll also assume
the reader has access to the source of FCO while reading this document.
I would throroughly recommend the Haskell history paper (cite) -- it
explains many of the design decisions behind Haskell, and it's an
excellent overview of the features available in the language.
\section{Why Haskell?}
Why should we consider Haskell as an option for an implementation
language? Like Scheme, it's a popular, mature, well-documented
functional language, it's used heavily by people who're into programming
language research, and it's been used to implement a number of solid
compilers for other languages. The result is that there are a number of
useful libraries that we can take advantage of.
There's lots of Haskell experience in the department already. It's the
only language other than Java that our undergrads are guaranteed to have
experience with, which might be useful for student projects.
Haskell also has some similarities with \occam: it has an
indentation-based syntax, it makes a point of distinguishing between
side-effecting and functional code, it emphasises compile-time safety
checks, and it has excellent support for lightweight concurrency. \occam
may therefore be of interest to some Haskell programmers.
\section{Existing work}
42 -- \occam to ETC, Scheme
JHC -- Haskell to C, Haskell
Pugs -- Perl 6 to various, Haskell
GHC -- probably not!
Mincaml -- ML subset to assembler, ML
\section{Technologies}
\subsection{Monads}
Failure support for free.
\subsection{SYB Generics}
\cite{syb1}
\label{gen-par-prob} Using generics with parametric types confuses the
hell out of the typechecker; you can work around this by giving explicit
instances of the types you want to use, but it's not very nice.
(This is a downside of using a statically-typed language; code that's
obviously correct sometimes needs very non-obvious type declarations, or
can't be statically typed at all.)
There's also Strafunski. (Which differs how?)
And HSXML. Actually looks more useful -- but would require using DrIFT
to generate instances of the classes.
http://article.gmane.org/gmane.comp.lang.haskell.general/13589
\subsection{Parsec}
Parsec is a combinator-based parsing library, which means that you're
essentially writing productions that look like BNF with variable
bindings, and the library takes care of matching and backtracking as
appropriate. Parsec's dead easy to use.
The parsing operations are actually operations in the \verb|Parser t|
monad.
\section{Parsing}
The parser is based on the grammar from the \occam 2.1 manual, with a
number of alterations:
\begin{itemize}
\item I took a leaf out of Haskell's book for handling the
indentation-based syntax: a preprocessor analyses the indentation and
adds explicit markers for "indent", "outdent" and "end of significant
line" that the parser can match later. The preprocessor's a bit limited
at the moment; it doesn't handle continuation lines or inline
\verb|VALOF|.
\item The original compiler assumes you're keeping track of what's in
scope while you're parsing, which we don't want to do. This makes some
things ambiguous, and some productions in the grammar turn out to be
identical if you don't know what type things are (for example, you can't
tell the difference between channels, ports and timers at parse time, so
the FCO grammar handles them all with a single set of productions).
(I think it'd be possible to simulate the behaviour of the original
compiler by using the GenParser monad rather than Parser, since that
lets you keep state. I'm pretty sure we wouldn't want to track scope
this way, but it might turn out not to be too painful to handle
indentation directly in the parser.)
\item Left-recursive productions (those that parse subscripts) don't
work; I split each into two productions, one which parses everything
that isn't left-recursive in the original grammar, and one which parses
the first followed by one or more subscripts.
\item The original grammar would parse \verb|x[y]| as a conversion of
the array literal \verb|[y]| to type \verb|x|, which isn't legal \occam.
I split the \verb|operand| production into a version that didn't include
\verb|table| and a version that did, so \verb|conversion| can now
explicitly match an operand that isn't an array literal.
\item Similarly, you can't tell at parse time whether in \verb|c ! a; b|
or \verb|x[a]| whether \verb|a| is a variable or a tag -- I'll have to
fix this up in a later pass.
\item I rewrote the production for lists of formal arguments, since the
original one's specified as lists of lists of arguments which might be
typed, and that doesn't work correctly in Parsec when written in the
obvious way. (It should be possible to express it more elegantly with a
bit more work.)
\end{itemize}
The parser was the first bit of FCO I wrote, and partly as a result my
Haskell coding style in the parser is especially poor; the Pugs parser,
also using Parsec, is a much better example. (But theirs doesn't parse
\occam, obviously.)
\section{Data structures}
I've experimented with two different ways of encoding the abstract
syntax tree in FCO.
\subsection{Parse tree}
\subsection{AST}
My first version of the AST types included a parametric
\verb|Structured t| type used to represent things that could include
replicators and specifications, such as \verb|IF| and \verb|ALT|
processes; I couldn't combine generic operations over these with others,
though (see \ref{gen-par-prob}).
Some things are simplified in the AST when compared with the grammar:
channels are just variables, for example.
Need to pass metadata through to the AST.
\section{Generic strategies}
Need to walk over the tree, tracking state.
Unique naming comes out nicer in Haskell than in Scheme, since I can
just use monads and generic transformations, and don't need to write out
all the productions again just to add an extra argument.
Generics appear to work better in GHC 6.6 than GHC 6.4, since some
restrictions on the types of mutually recursive functions have been
lifted. (Check this against release notes.)
\section{C generation}
\section{Future work}
The obvious bit of future work is writing the full compiler that this
was a prototype of.
Turns out I quite like Haskell -- and there are tools provided with GHC
to parse Haskell. If we wrote a Haskell concurrency library (CSP-style),
we should investigate writing an \occam-style usage checker for it.
\bibliographystyle{unsrt}
\bibliography{the}
\end{document}

View File

@ -1,70 +0,0 @@
-- Demonstrate how to do unique naming.
import Control.Monad.State
import Data.Generics
import Data.List
--
data Name = Name String
deriving (Show, Eq, Typeable, Data)
data Direction = Input Name | Output Name
deriving (Show, Eq, Typeable, Data)
data Process = Declare Name Process | Use Direction | Seq [Process]
deriving (Show, Eq, Typeable, Data)
--
type UniqueState = (Int, [(String, String)])
type UniqueM t = State UniqueState t
uniquelyName :: Process -> Process
uniquelyName p = evalState (doAny p) (0, [])
doAny :: Data t => t -> UniqueM t
doAny = doGeneric `extM` doName `extM` doProcess
doGeneric :: Data t => t -> UniqueM t
doGeneric = gmapM doAny
doProcess :: Process -> UniqueM Process
doProcess p = case p of
Declare (Name n) _ -> do
(count, vars) <- get
put (count + 1, (n, n ++ "." ++ show count) : vars)
p' <- doGeneric p
(count', _) <- get
put (count', vars)
return p'
otherwise -> doGeneric p
doName :: Name -> UniqueM Name
doName (Name s) = do
(count, vars) <- get
let s' = case lookup s vars of
Just n -> n
Nothing -> error $ "Name " ++ s ++ " not declared before use"
return $ Name s'
--
demo :: Process -> IO ()
demo p = do
putStrLn $ show p
let p' = uniquelyName p
putStrLn $ show p'
putStrLn ""
main :: IO ()
main = do
demo $ Declare (Name "foo") (Use (Input (Name "foo")))
demo $ Declare (Name "a") (Seq [Use (Input (Name "a")),
Use (Output (Name "a"))])
demo $ Declare (Name "a") (Declare (Name "b") (Seq [Use (Input (Name "a")),
Use (Input (Name "b")),
Declare (Name "b") (Seq [Use (Input (Name "a")),
Use (Input (Name "b"))])]))

View File

@ -1,430 +0,0 @@
;; soccam2.1 syntax definition -- based on occam2.1 syntax
;; Sample translation:
;;
;; PROC graphics.plex ([num.sprites]CHAN OF SPRITE in, [num.sprites]CHAN OF BOOL in.req, CHAN OF GRAPHICS.COMMAND out, CHAN OF BOOL req)
;; [num.sprites][max.graphic]BYTE lump:
;; [num.sprites]INT lump.len, x, y, col:
;; WHILE TRUE
;; SEQ
;; BOOL b:
;; req ? b
;; PAR i = 0 FOR num.sprites
;; SEQ
;; in.req[i] ! TRUE
;; in[i] ? lump.len[i]::lump[i] ; x[i] ; y[i] ; col[i]
;; SEQ i = 0 FOR num.sprites
;; out ! sprite ; lump.len[i]::lump[i] ; x[i] ; y[i] ; col[i]
;; out ! flip
;; :
;;
;; becomes:
;;
;; (proc graphics.plex (((array num.sprites (chan-of sprite)) in)
;; ((array num.sprites (chan-of bool)) in.req)
;; ((chan-of graphics.command) out)
;; ((chan-of bool) req))
;; (: ((array num.sprites (array max.graphic byte)) lump)
;; (: ((array num.sprites int) lump.len x y col)
;; (while true
;; (seq
;; (: (bool b) (? req b))
;; (par (for i 0 num.sprites)
;; (seq
;; (! (sub in.req i) true)
;; (? (sub in i) ((:: (sub lump.len i) (sub lump i)) (sub x i) (sub y i) (sub col i)))))
;; (seq (for i 0 num.sprites)
;; (! out (sprite (:: (sub lump.len i) (sub lump i)) (sub x i) (sub y i) (sub col i))))
;; (! out flip))))))
;; deprecate non-specifier forms in this
<abbreviation> :=
(is <name> <variable>)
(is <specifier> <name> <variable>)
(val-is <name> <expression>)
(val-is <specifier> <name> <expression>)
(is <name> <channel>)
(is <specifier> <name> <channel>)
(is <name> (<channel>+)) ;; is this really only 1d?
(is <specifier> <name> (<channel>+))
(is <name> <timer>)
(is <specifier> <name> <timer>)
(is <name> <port>)
(is <specifier> <name> <port>)
<actual> :=
<variable>
<channel>
<timer>
<port>
<expression>
<allocation> :=
(place-at <name> <expression>)
<alternation> :=
(alt <alternative>+)
(alt <replicator> <alternative>)
(pri-alt <alternative>+)
(pri-alt <replicator> <alternative>)
<alternative> :=
<guarded.alternative>
<alternation>
(?case <channel> <variant>+)
(cond <boolean> (?case <channel> <variant>+))
(: <specification> <alternative>)
<assignment> :=
(:= <variable-list> <expression-list>)
<base> :=
<expression>
<boolean> :=
<expression>
<byte> :=
'<character>'
<case.expression> :=
<expression>
<case.input> :=
(?case <channel> <variant>+)
;; unsure about "sub" -- the subscript forms could be generalised
<channel> :=
<name>
(sub <channel> <expression>)
(sub-from-for <channel> <base> <count>)
(sub-from <channel> <base>)
(sub-for <channel> <count>)
<channel.type> :=
(chan-of <protocol>)
(array <expression> <channel.type>)
;; occam2.1 syntax doesn't define <character>, which can include * escapes
<choice> :=
<guarded.choice>
<conditional>
(: <specification> <choice>)
<conditional> :=
(if <choice>+)
(if <replicator> <choice>)
<conversion> :=
(conv <data.type> <operand>)
(round <data.type> <operand>)
(trunc <data.type> <operand>)
<count> :=
<expression>
<data.type> :=
bool
byte
int
int16
int32
int64
real32
real64
<name>
(array <expression> <data.type>)
(array <data.type) ;; not in occam2.1 syntax?
<declaration> :=
(<data.type> <name>+)
(<channel.type> <name>+)
(<timer.type> <name>+)
(<port.type> <name>+)
;; deprecate reshapes?
<definition> :=
(data-type <name> <data.type>)
(data-type <name> <structured.type>)
(protocol <name> <simple.protocol>)
(protocol <name> <sequential.protocol>)
(protocol <name> (<tagged-protocol>+))
(proc <name> (<formal>*) <process>)
(function (<data.type>+) <function-header> <value.process>)
(function-is (<data.type>+) <function-header> <expression.list>)
(retypes <specifier> <name> <variable>)
(val-retypes <specifier> <name> <expression>)
(retypes <specifier> <name> <channel>)
(retypes <specifier> <name> <port>)
(reshapes <specifier> <name> <variable>)
(val-reshapes <specifier> <name> <expression>)
(reshapes <specifier> <name> <channel>)
(reshapes <specifier> <name> <port>)
<delayed.input> :=
(?after <timer> <expression>)
<dyadic.operator> :=
+
-
*
/
mod ;; was \
rem
plus
minus
times
bitand ;; also /\
bitor ;; also \/
bitxor ;; was ><
and
or
=
<> ;; probably want != or something
<
>
>=
<=
after
<exponent> :=
+<digits>
-<digits>
<expression> :=
<operand>
(<monadic.operator> <operand>)
(<dyadic.operator> <operand> <operand>)
(mostpos <data.type>)
(mostneg <data.type>)
(size <data.type>)
<conversion>
;; This is an oddity in the syntax, since it has to allow for multi-valued results from functions.
<expression.list> :=
(<expression>+)
(call <name> <expression>*) ;; eww, but necessary?
value-process>
<field.name> :=
<name>
<formal> :=
(<specifier> <name>+)
(val <specifier> <name>+)
;; Yuck -- this bit of the syntax really doesn't work nicely for soccam.
<function.header> :=
<name> (<formal>*)
<guard> :=
<input>
(cond <boolean> <input>)
(cond <boolean> skip)
<guarded.alternative> :=
(<guard> <process>)
<guarded.choice> :=
(<bool-exp> <process>)
<input> :=
(? <channel> <input.item>+)
(?case <channel> <tagged.list>)
<timer.input>
<delayed.input>
(? <port> <variable>)
<input.item> :=
<variable>
(:: <variable> <variable>)
<integer> :=
<digits>
#<hex.digits>
<literal> :=
<integer>
<byte>
<real>
(<data.type> <integer>)
(<data.type> <byte>)
(<data.type> <real>)
true
false
<loop> :=
(while <boolean> <process>)
<monadic.operator> :=
-
bitnot ;; also ~
not
size
<operand> :=
<variable>
<literal>
<table>
<expression> ;; rather than (expression)
<value.process>
(call <name> <expression>*) ;; eww, as above
(sub <operand> <expression>) ;; nothing explicit for field names? I guess they're treated as arrays
(bytesin <operand>)
(bytesin <data.type>)
(offsetof <name> <field.name>)
<option> :=
((<case.expression>+) <process>)
(else <process>)
(: <specification> <option>)
<output> :=
(! <channel> <output.item>+)
(!case <channel> (<tag> <output.item>*))
(! <port> <expression>)
<output.item> :=
<expression>
(:: <expression> <expression>)
<parallel> :=
(par <process>+)
(par <replicator> <process>)
(pri-par <process>+)
(pri-par <replicator> <process>)
<placedpar>
<placedpar> :=
(placed-par <placedpar>+)
(placed-par <replicator> <placedpar>)
(processor <expression> <process>)
<port> :=
<name>
(sub <port> <expression>)
(sub-from-for <port> <base> <count>)
(sub-from <port> <base>)
(sub-for <port> <count>)
<port.type> :=
(port-of <data.type>)
(array <expression> <port.type>)
<proc.instance> :=
(<name> <actual>*)
<process> :=
<assignment>
<input>
<output>
skip
stop
<sequence>
<conditional>
<selection>
<loop>
<parallel>
<alternation>
<case.input>
<proc.instance>
(: <specification> <process>)
(: <allocation> <process>)
<protocol> :=
<name>
<simple.protocol>
<real> :=
<digits>.<digits>
<digits>.<digits>e<exponent>
<replicator> :=
(for <name> <base> <count>)
<selection> :=
(case <selector> <option>+)
<selector> :=
<expression>
<sequence> :=
(seq <process>+)
(seq <replicator> <process>)
<sequential.protocol> :=
(<simple.protocol>+)
<simple.protocol> :=
<data.type>
any ;; deprecate
(:: <data.type> <data.type>)
<specification> :=
<declaration>
<abbreviation>
<definition>
<specifier> :=
<data.type>
<channel.type>
<timer.type>
<port.type>
(array <specifier>)
(array <expression> <specifier>)
<structured.type> :=
(record (<data.type> <field.name>+)+)
(packed-record (<data.type> <field.name>+)+)
<table> :=
<string>
(<string> <name>) ;; this is literals of named types, e.g. "foo"(FILENAME)
(<expression>+)
(sub <table> <expression>)
(sub-from-for <table> <base> <count>)
(sub-from <table> <base>)
(sub-for <table> <count>)
<tag> :=
<name>
<tagged.list> :=
(<tag> <input.item>*)
<tagged.protocol> :=
(<tag> <sequential.protocol>*)
<timer.input> :=
(? <timer> <variable>)
<timer> :=
<name>
(sub <timer> <expression>)
(sub-from-for <timer> <base> <count>)
(sub-from <timer> <base>)
(sub-for <timer> <count>)
<timer.type> :=
timer
(array <expression> <timer.type>) ;; nope, I don't know why you'd want an array of timers either
<value.process> :=
(valof <process> <expression.list>)
(: <specification> <value.process>)
<variable> :=
<name>
(sub <variable> <expression>)
(sub-from-for <variable> <base> <count>)
(sub-from <variable> <base>)
(sub-for <variable> <count>)
<variable.list> :=
(<variable>+)
<variant> :=
(<tagged.list> <process>)
(: <specification> <variant>)

View File

@ -1,13 +0,0 @@
PROC main ()
INT a, b:
VAL INT c IS 42:
VAL INT d IS a + b:
INT e IS a:
[4]BYTE a RETYPES a:
VAL BYTE b IS a[0]:
SEQ i = (a + 20) FOR (b + 30)
VAL INT ii IS (i + 40):
SKIP
:

View File

@ -1,221 +0,0 @@
-- This file aims to exercise every bit of occam2.1 syntax.
-- As such, it should compile (with loads of warnings), but not actually do anything when run.
-- Still to check: line continuation rules.
PROC test.syntax ()
INT x, y, a, b:
[10]INT xs:
BOOL la, lb, lc:
BYTE bb:
REAL32 f:
CHAN OF INT c:
[10]CHAN OF INT cs:
[10][10]CHAN OF INT css:
PROTOCOL P
CASE
foo
bar
:
CHAN OF P cc:
DATA TYPE R
RECORD
INT a:
INT b:
:
SEQ
-- abbreviation
xx IS x:
SKIP
INT xx IS x:
SKIP
VAL xx IS x:
SKIP
VAL INT xx IS x:
SKIP
-- allocation
[2]INT q:
-- Doesn't work in KRoC.
PLACE q AT 12345:
SKIP
-- alternation
ALT
c ? x
SKIP
TRUE & SKIP
SKIP
ALT i = 0 FOR 10
cs[i] ? xs[i]
SKIP
PRI ALT
c ? x
SKIP
TRUE & SKIP
SKIP
PRI ALT i = 0 FOR 10
cs[i] ? xs[i]
SKIP
xx IS x:
ALT
TRUE & SKIP
SKIP
-- alternative
ALT
TRUE & cc ? CASE
foo
SKIP
bar
STOP
cc ? CASE
foo
SKIP
bar
STOP
-- assignment
x := y
x, y := a, b
-- byte
bb := 'x'
-- caseInput
cc ? CASE
foo
SKIP
bar
STOP
-- channel
css[4][2] ? x
cs[4] ? x
c ? x
[cs FROM 1 FOR 2][1] ? x
[cs FROM 0][4] ? x
[cs FOR 2][1] ? x
-- channelType
CHAN OF INT q:
[10]CHAN OF INT qs:
SKIP
-- character
bb := 'x'
bb := '*c'
bb := '*#FF'
-- occamChoice
INT x:
IF
TRUE
SKIP
-- conditional
IF
TRUE
SKIP
FALSE
STOP
IF i = 0 FOR 42
xs[i] = 11
STOP
-- conversion
x := INT bb
f := REAL32 ROUND x
f := REAL32 TRUNC x
-- dataType
BOOL a:
BYTE a:
INT a:
INT16 a:
INT32 a:
INT64 a:
REAL32 a:
REAL64 a:
[10]INT a:
R a:
SKIP
-- declaration
INT a:
INT a, b, c:
SKIP
-- definition
DATA TYPE T1 IS INT:
DATA TYPE T2
RECORD
INT x:
:
PROTOCOL P1 IS INT; INT:
PROTOCOL P2 IS ANY:
PROTOCOL P3
CASE
foo
:
PROC p1 (INT x)
SKIP
:
INT FUNCTION f1 (VAL INT x) IS x:
INT FUNCTION f2 (VAL INT x)
VALOF
SKIP
RESULT x
:
INT xx RETYPES x:
INT xx RESHAPES x:
VAL INT xx RETYPES x:
VAL INT xx RESHAPES x:
SKIP
-- dyadicOperator
x := a + b
x := a - b
x := a * b
x := a / b
x := a \ b
x := a REM b
x := a PLUS b
x := a MINUS b
x := a TIMES b
x := lb /\ lc
x := lb \/ lc
x := lb >< lc
x := lb BITAND lc
x := lb BITOR lc
la := lb AND lc
la := lb OR lc
la := a = b
la := a <> b
la := a < b
la := a > b
la := a <= b
la := a >= b
la := a AFTER b
-- occamExponent
f := 1.2345e+67
f := 1.2345e-67
-- expression
x := -a
x := MOSTPOS INT
x := MOSTNEG INT
x := SIZE INT
x := a + b
-- expressionList
INT, INT FUNCTION twofunc (VAL INT x, y) IS x, y:
a, b := twofunc (x, y)
a, b := x, y
-- real
f := 1.2345e+67
f := 1.2345
:

View File

@ -1,25 +0,0 @@
PROTOCOL MYPROTO
CASE
tag1
tag2
:
PROC n ()
CHAN OF INT c1:
CHAN OF MYPROTO c2:
BOOL b:
ALT
c1 ? x
STOP
b & c1 ? x
STOP
c2 ? CASE
tag1
STOP
tag2
STOP
b & c2 ? CASE
tag1
STOP
tag2
STOP
:

View File

@ -1,43 +0,0 @@
-- Some ambiguities in the occam2.1 syntax as specified.
PROC x ()
SEQ
[10]INT a:
INT b, r:
SEQ
r := a[b]
DATA TYPE T
RECORD
INT b:
:
T a:
INT r:
SEQ
r := a[b]
DATA TYPE a IS [1]INT:
INT b:
a r:
SEQ
-- Permitted by the syntax but not by the language: you can't do a data
-- type conversion on an array (perhaps specifically to resolve this
-- ambiguity!).
r := a[b]
PROTOCOL P
CASE
a; INT
:
CHAN OF P c:
INT b:
SEQ
c ! a; b
PROTOCOL P IS INT; INT:
CHAN OF P c:
INT a, b:
SEQ
c ! a; b
:

File diff suppressed because it is too large Load Diff

View File

@ -1,89 +0,0 @@
-- commstime, from the KRoC distribution
#USE "course.lib"
--{{{ PROC seq.delta (CHAN INT in?, out.0!, out.1!)
PROC seq.delta (CHAN INT in?, out.0!, out.1!)
WHILE TRUE
INT n:
SEQ
in ? n
out.0 ! n
out.1 ! n
:
--}}}
--{{{ PROC consume (VAL INT n.loops, CHAN INT in?, CHAN BYTE out!)
PROC consume (VAL INT n.loops, CHAN INT in?, CHAN BYTE out!)
TIMER tim:
INT t0, t1:
INT value:
SEQ
--{{{ warm-up loop
VAL INT warm.up IS 16:
SEQ i = 0 FOR warm.up
in ? value
--}}}
WHILE TRUE
SEQ
tim ? t0
--{{{ bench-mark loop
SEQ i = 0 FOR n.loops
in ? value
--}}}
tim ? t1
--{{{ report
VAL INT microsecs IS t1 MINUS t0:
VAL INT64 nanosecs IS 1000 * (INT64 microsecs):
SEQ
out.string ("Last value received = ", 0, out!)
out.int (value, 0, out!)
out.string ("*c*n", 0, out!)
out.string ("Time = ", 0, out!)
out.int (microsecs, 0, out!)
out.string (" microsecs*c*n", 0, out!)
out.string ("Time per loop = ", 0, out!)
out.int (INT (nanosecs/(INT64 n.loops)), 0, out!)
out.string (" nanosecs*c*n", 0, out!)
out.string ("Context switch = ", 0, out!)
out.int (INT ((nanosecs/(INT64 n.loops))/4), 0, out!)
out.string (" nanosecs*c*n*n", 0, out!)
--}}}
:
--}}}
--{{{ PROC comms.time (CHAN BYTE keyboard?, screen!, error!)
PROC comms.time (CHAN BYTE keyboard?, screen!, error!)
BOOL use.seq.delta:
SEQ
--{{{ announce
SEQ
out.string ("*c*nCommstime in occam ...*c*n*n", 0, screen!)
out.string ("Using the SEQ-output version of the delta process*c*n", 0, screen!)
out.string ("yields a more accurate measure of context-switch time*c*n*n", 0, screen!)
out.string ("Using the PAR-output version carries an extra overhead*c*n", 0, screen!)
out.string ("of one process startup/shutdown per Commstime loop*c*n*n", 0, screen!)
out.string ("By comparing **loop** times between the SEQ and PAR versions,*c*n", 0, screen!)
out.string ("the process startup/shutdown overhead may be deduced*c*n*n", 0, screen!)
--}}}
ask.bool ("Sequential delta? ", use.seq.delta, keyboard?, screen!)
out.string ("*nCommstime starting ...*c*n*n", 0, screen!)
CHAN INT a, b, c, d:
PAR
prefix (0, b?, a!)
IF
use.seq.delta
seq.delta (a?, c!, d!) -- the one defined above
TRUE
delta (a?, c!, d!) -- the one that does a parallel output
succ (c?, b!)
consume (1000000, d?, screen!)
:
--}}}

View File

@ -1,21 +0,0 @@
PROC p (VAL INT x, y, INT z)
z := x + y
:
INT FUNCTION f (VAL INT x, y)
VALOF
SKIP
RESULT x + y
:
PROC test.expressions ()
INT a:
INT b:
INT c:
SEQ
a := 1
b := 2
c := f (a, b)
c := (42 * a) + (b - (72 / c))
p (a, b, c)
:

View File

@ -1,20 +0,0 @@
-- test 1
-- this here is a comment
PROC graphics.plex ([num.sprites]CHAN OF SPRITE in, [num.sprites]CHAN OF BOOL in.req, CHAN OF GRAPHICS.COMMAND out, CHAN OF BOOL req)
[num.sprites][max.graphic]BYTE lump:
[num.sprites]INT lump.len, x, y, col:
WHILE TRUE
SEQ
BOOL b: -- I never much liked that variable
req ? b
PAR i = 0 FOR num.sprites
SEQ
in.req[i] ! TRUE
in[i] ? lump.len[i]::lump[i] ; x[i] ; y[i] ; col[i]
SEQ i = 0 FOR num.sprites
out ! sprite ; lump.len[i]::lump[i] ; x[i] ; y[i] ; col[i]
out ! flip
:

View File

@ -1,6 +0,0 @@
PROC test.simple (CHAN OF BYTE in, out, err)
SEQ
out ! 'h'
out ! 'i'
out ! '*n'
:

View File

@ -1,9 +0,0 @@
PROC other (CHAN OF INT c)
SKIP
:
PROC foo ()
[10]CHAN OF INT xs:
PAR i = 0 FOR SIZE xs
other (xs[i])
:

View File

@ -1,11 +0,0 @@
PROC test.syntax ()
[1000][1000]CHAN OF INT css:
[1000]CHAN OF INT cs:
SEQ
-- channel
css[111][222] ? x
cs[333] ? x
[cs FROM 444 FOR 11][555] ? x
[cs FROM 666][77] ? x
[cs FOR 888][99] ? x
:

View File

@ -1,5 +0,0 @@
PROC main ()
SEQ
SKIP
STOP
:

Some files were not shown because too many files have changed in this diff Show More