diff --git a/BStruct.hs b/BStruct.hs
new file mode 100644
--- /dev/null
+++ b/BStruct.hs
@@ -0,0 +1,128 @@
+module BStruct (BSigned(..), BType(..), BStruct(..), bStructs, BEnum(..), bEnums) where
+import CAnalyze
+
+import qualified Data.ByteString.Char8  -- input for Language.C parser
+import qualified Text.Show.Pretty       -- cabal install pretty-show
+
+import Language.C                       -- cabal install language-c
+import Language.C.Pretty                -- pretty-show
+import Language.C.Data.Ident
+import Language.C.Analysis.AstAnalysis
+import Language.C.Analysis.TravMonad
+import Language.C.Analysis.SemRep
+-- import Language.C.Syntax.AST
+-- import Language.C.Data.Position   
+
+import Data.Map               -- Language.C.Analysis output
+import Data.List
+import Control.Monad
+
+import System
+
+-- Simple enum dictionary type to complement BStruct.
+data BEnum = BEnum [(String, Integer)] deriving (Show, Eq)
+
+bEnums top = bes where
+  syms = enums $ globalDecls top
+  bes = liftM (BEnum . sort) $ sequence $ Prelude.map be $ assocs syms
+  be (Ident id _ _, e) = 
+    case evalC syms e of
+      Right val -> Right (id, val)
+      Left err  -> Left err
+
+
+  
+
+-- Binary representable types.  No pointers!
+data BSigned = S | U deriving (Show, Eq)
+data BType = BInt BSigned Integer
+             | BFloat Integer
+             | BArr Integer BType               
+             | BComp SUERef      -- open recursion
+             deriving (Show, Eq)
+  
+data BStruct = BStruct String [(BType, String)]
+          deriving (Show, Eq)
+                   
+                
+-- Base type representation.  Note that the point of parsing these
+-- structs is to parse binary blobs, which do not contain pointer or
+-- function pointers.
+
+-- http://hackage.haskell.org/packages/archive/language-c/0.4.2/doc/html/Language-C-Analysis-SemRep.html#t:Type
+flatType syms = typ where
+  
+  oops msg x = error $ "\n" ++ msg ++ "\n" ++ (show x)
+  
+  typ (DirectType name quals attrs) = nam name
+  typ (TypeDefType ref quals attrs) = tdnam ref
+  typ (ArrayType t s quals attrs) = do
+    s' <- as s
+    t' <- typ t
+    return $ BArr s' t'
+  -- PtrType Type TypeQuals Attributes	
+  -- FunctionType FunType Attributes	
+  typ _ = Left $ "PtrTpe or FunctionType are not flat types"
+
+  as (ArraySize _ e) = evalC syms e
+
+  -- tdnam (TypeDefRef (Ident id _ _) Nothing _) = id 
+  tdnam (TypeDefRef  _ (Just t) _) = typ t  -- recurse down typedefs
+  tdnam _ = Left $ "TypeDefRef: no type"
+
+  nam (TyIntegral t) = Right $ int t
+  nam (TyFloating t) = Right $ float t
+  nam (TyComp (CompTypeRef sueref kind _)) = Right $ BComp sueref
+
+  
+  -- FIXME: support when needed                       
+  nam TyVoid        = Left $ "TyVoid not supported"
+  nam (TyComplex _) = Left $ "TyComplex not supported"
+  nam (TyEnum    _) = Left $ "TyEnum not supported"
+  nam (TyBuiltin _) = Left $ "TyBuiltin not supported"
+
+  -- IntType
+  int TyBool   = BInt U 1
+  
+  int TyUChar  = BInt U 8
+  int TyUShort = BInt U 16
+  int TyUInt   = BInt U 32
+  int TyULong  = BInt U 32
+  int TyULLong = BInt U 64
+  
+  int TyChar   = BInt S 8
+  int TySChar  = BInt S 8
+  int TyShort  = BInt S 16
+  int TyInt    = BInt S 32
+  int TyLong   = BInt S 32
+  int TyLLong  = BInt S 64
+
+  -- FloatType
+  float TyFloat  = BFloat 32
+  float TyDouble = BFloat 64
+
+
+flat syms = sequence . Data.Map.elems . (Data.Map.map f)  where
+  f (CompDef (CompType r StructTag ms _ _)) = rv where
+    rv = case members (flatType syms) ms of
+      Right m -> Right $ BStruct (ref r) m
+      Left err -> Left err
+    
+  ref (NamedRef (Ident id _ _)) = id
+
+                                       
+
+
+-- Distill binary packed structs.
+bStructs toplevel = flt where
+  gs = globalDecls toplevel
+  es = enums gs
+  ss = packedStructs gs
+  flt = flat es ss
+  
+-- bEnums toplevel   
+
+                   
+-- TEST
+-- x f = report f "/tmp/test.c"
+-- x (aKeys packedStructs)
diff --git a/CAnalyze.hs b/CAnalyze.hs
new file mode 100644
--- /dev/null
+++ b/CAnalyze.hs
@@ -0,0 +1,135 @@
+{-# LANGUAGE NoMonomorphismRestriction, FlexibleInstances,
+    FunctionalDependencies, MultiParamTypeClasses, TypeSynonymInstances #-}
+
+module CAnalyze(reportC,
+                aKeys,
+                packedStructs,
+                enums,  -- :: GobalDecls -> Map Ident Expr
+                members,
+                evalC,
+                globalDecls) where
+
+import qualified Data.ByteString.Char8  -- input for Language.C parser
+import qualified Text.Show.Pretty       -- cabal install pretty-show
+
+import Language.C                       -- cabal install language-c
+import Language.C.Pretty                -- pretty-show
+import Language.C.Data.Ident
+import Language.C.Analysis.AstAnalysis
+import Language.C.Analysis.TravMonad
+import Language.C.Analysis.SemRep
+-- import Language.C.Syntax.AST
+-- import Language.C.Data.Position   
+
+import Data.Map               -- Language.C.Analysis output
+import Control.Monad
+
+import System
+
+import Char
+
+-- FIXME:
+-- 1.  No "Left" error handling: pattern matches will just fail.
+-- 2.  Not every corner case is supported.
+
+-------- AST analysis --------
+
+
+-- Convert translation unit to GlobalDecls, using the Trav () monad
+-- for analysis.
+globalDecls tu = fst ds where
+  Right ds = runTrav_ $ analyseAST tu
+
+
+-- Filter out enums
+enums :: GlobalDecls -> Map Ident Expr
+enums = (Data.Map.mapMaybe getEnum) . gObjs where 
+  unId (Ident id _ _) = id
+  getEnum = p where
+    p (EnumeratorDef (Enumerator (Ident id _ _) ex _ _)) = Just ex
+    p _ = Nothing
+
+
+-- Filter out packed structs from GlobalDecls.  All structure
+-- definitions are in the gTags member of GlobalDecls.
+packedStructs :: GlobalDecls -> Map SUERef TagDef
+packedStructs = (Data.Map.filter isPacked) . gTags where
+  isPacked = p where
+    p (CompDef (CompType _ StructTag _ attrs _)) = packedAttr attrs
+    p _ = False
+  packedAttr = not . Prelude.null . (Prelude.filter f) where
+    f (Attr (Ident id _ _) _ _) = 
+      any (id ==) ["packed", "__packed__"]
+
+-- For inspection of globalDecls analysis: get a list of keys of the
+-- Map elements of GlobalDecls structure as a list of strings.
+aKeys f = (Prelude.map show) . Data.Map.keys . f . globalDecls
+
+members typ = mems where
+  var (VarName (Ident id _ _) _) = id  
+  mems = sequence . (Prelude.map mem)
+  mem (MemberDecl (VarDecl v _ t) _ _) =
+    case (typ t) of
+      (Right td) -> Right (td, var v)
+      (Left err) -> Left err
+
+
+
+                          
+
+-- Simple expression evaluator.  Only supports const int and enums.
+evalC syms = ex where
+  ex (CVar id _) = ex $ syms Data.Map.! id
+  ex (CConst (CIntConst (CInteger i _ _) _)) = Right $ i
+  ex (CConst (CCharConst (CChar c _) _)) = Right $ toInteger $ ord c
+  ex (CBinary op e1 e2 _) = do
+    e1' <- ex e1
+    e2' <- ex e2
+    return $ (binop op) e1' e2'
+
+  ex  e = Left $ "CExpr evaluation not supported: " ++ show e
+   
+  binop CAddOp a b = a + b
+  binop CShlOp a b = a * (2 ^ b)
+
+-------- Parsing and prettyprinting --------
+
+-- Convert name and file contents to translation unit.
+parseC' filename text = ast where
+  (Right ast) = parseC (Data.ByteString.Char8.pack text) $ initPos filename
+
+-- Same, but tied to filesystem in IO monad.
+parseCFile' filename = do
+  s <- readFile filename
+  return $ parseC' filename s
+  
+-------- REPORTING --------
+
+-- Print out report of analysis for a specific file.
+reportC process file = do
+  ast <- parseCFile' file
+  case process ast of
+    (Right rep) -> putStrLn rep
+    (Left err) -> error err
+
+-------- INTERACTIVE DEBUGGING --------
+
+-- Clear the node info slot.
+strip = fmap $ \_ -> ()
+
+-- Prettyprinting AST.
+ppAst = Text.Show.Pretty.ppShow . strip
+ast = putStrLn . show. ppAst
+pp = putStr . Text.Show.Pretty.ppShow
+ppm m = do { v <- m ; pp v }
+
+
+kObjs     = aKeys gObjs
+kTypeDefs = aKeys gTypeDefs
+kTags     = aKeys gTags  
+kPacked   = aKeys packedStructs   -- is a filtered gTags
+
+
+-- TEST
+-- x f = reportC f "/tmp/test.c"
+
diff --git a/PrintLua.hs b/PrintLua.hs
new file mode 100644
--- /dev/null
+++ b/PrintLua.hs
@@ -0,0 +1,80 @@
+module PrintLua(packedLua, LuaData(..)) where
+import CAnalyze
+import BStruct
+import Text.PrettyPrint.HughesPJ
+import Control.Monad
+
+-- (useful subset of) Lua data syntax.
+data LuaData = LuaStr String
+             | LuaNum String
+             | LuaTable [(LuaData, LuaData)] deriving (Eq)
+                                          
+-- Prettyprinting with Text.PrettyPrint.HughesPJ
+instance Show LuaData where
+  show = (++"\n") . show . docLua
+
+docLua (LuaNum s) = text s
+docLua (LuaStr s) = quotes $ text s
+docLua (LuaTable xs) = doc where
+  doc = nest 2 $ braces $ cat $ punctuate comma $ list xs
+  list xs = zipWith pair [1..] xs
+  pair i (k,v) = docPair i k $ docLua v
+
+-- If i is in sequence, don't print index.
+docPair i (LuaNum n)   sv | show i == n = sv
+docPair _ n@(LuaNum _) sv = cat [ brackets $ docLua n, sv ]
+docPair _ (LuaStr s) sv = cat [cat [text s, equals], sv]
+docPair _ (LuaTable t) sv = error $ "Table as key."
+
+
+
+-- Compile BStruct list to Lua
+
+-- List as table.
+luaList :: [LuaData] -> LuaData
+luaList es = LuaTable $ zipWith entry [1..] es where
+  entry i e = (LuaNum $ show i, e)
+
+-- We only use string-indexed pairs.  For index: use luaList.
+luaPair s e = (LuaStr s, e)  
+
+-- Can't use keys because order is important, so we just use the first
+-- element of an array as the tag.
+luaTagList tag ls = luaList (LuaStr tag : ls)
+
+luaBStructs = LuaTable . (map pair)  where
+  
+  pair (BStruct name mems) = luaPair name $ luaList $ map mem mems
+  mem (t, n) = luaList [typ t, LuaStr n]
+    
+  typ (BInt s b) = luaTagList "int"    [sign s, bits b]
+  typ (BFloat b) = luaTagList "float"  [bits b]
+  typ (BComp r)  = luaTagList "struct" [ref r]
+  typ (BArr s t) = luaTagList "array"  [size s, typ t]
+  
+  sign = LuaStr . show
+  bits = LuaNum . show
+  size = LuaNum . show
+  ref = LuaStr . show
+    
+luaBEnums (BEnum ls) = LuaTable $ map be ls where
+  be (nam, val) = luaPair nam $ LuaNum $ show val
+  
+
+-- Wrap around C analyser.
+packedLua = reportC process where
+  process ast = do
+    bs <- bStructs ast
+    be <- bEnums ast
+    let 
+      ls = luaBStructs bs
+      es = luaBEnums be
+      tab = LuaTable [(LuaStr "struct", ls),
+                      (LuaStr "enum",   es)]
+      in do  
+      return $ ("return\n" ++) $ show $ tab
+
+    
+-- TEST    
+-- x = packedLua "/tmp/test.c"
+-- x = packedLua "/home/tom/structs.E.c"
diff --git a/clua.cabal b/clua.cabal
--- a/clua.cabal
+++ b/clua.cabal
@@ -1,5 +1,5 @@
 Name:                clua
-Version:             0.1
+Version:             0.2
 Synopsis:            C to Lua data wrapper generator
 Homepage:            http://zwizwa.be/-/meta
 License:             BSD3
@@ -8,7 +8,7 @@
 Maintainer:          tom@zwizwa.be
 Category:            Language
 Build-type:          Simple
-Extra-source-files:  README, parse-bin.lua
+Extra-source-files:  README, parse-bin.lua, BStruct.hs, CAnalyze.hs, PrintLua.hs
 Cabal-version:       >=1.2
 
 Description: Gather enums and packed struct definitions from a C file
