haskell命令行

时间:2013-04-04 04:34:08

标签: haskell

您好我获得了在Haskell中运行的代码,我对这种语言有点新意。我得到它编译,但我不知道如何使用它。通过浏览它我相信它应该解析一个java类文件,我已经尝试解析file.class但它给我一个“无法加载接口文件名”。如果有人能指出我做错了什么(我确定它的东西很小)我会很感激。

module Parse where

import Data.Binary
import Data.Binary.Get
import Data.Word
import Control.Monad
import qualified Data.ByteString          as B
import qualified Data.ByteString.Internal as B
import qualified Data.ByteString.Lazy     as L

{-
    ClassFile {
        u4 magic;
        u2 minor_version;
        u2 major_version;
        u2 constant_pool_count;
        cp_info constant_pool[constant_pool_count-1];
        u2 access_flags;
        u2 this_class;
        u2 super_class;
        u2 interfaces_count;
        u2 interfaces[interfaces_count];
        u2 fields_count;
        field_info fields[fields_count];
        u2 methods_count;
        method_info methods[methods_count];
        u2 attributes_count;
        attribute_info attributes[attributes_count];
     }
-}

getWord16 :: Get Word16
getWord16 = getWord16be

getWord32 :: Get Word32
getWord32 = getWord32be

data JavaClass = JavaClass {
    classMinorVersion :: Word16
  , classMajorVersion :: MajorVersion
  , classConstantPoolCount :: Word16
  } deriving Show

getJavaClass :: Get JavaClass
getJavaClass = do
  checkClassMagic
  minorVersion   <- getMinorVersion
  majorVersion   <- getMajorVersion
  constPoolCount <- getConstantsPoolCount
  return $ JavaClass minorVersion majorVersion constPoolCount

checkClassMagic :: Get ()
checkClassMagic = do
  magic <- getWord32
  if magic /= 0xCAFEBABE then
    fail "Invalid magic number for Java class"
    else
    return ()

getMinorVersion :: Get Word16
getMinorVersion = getWord16 >>= return

{-
J2SE 7.0 = 51 (0x33 hex),
J2SE 6.0 = 50 (0x32 hex),
J2SE 5.0 = 49 (0x31 hex),
JDK 1.4 = 48 (0x30 hex),
JDK 1.3 = 47 (0x2F hex),
JDK 1.2 = 46 (0x2E hex),
JDK 1.1 = 45 (0x2D hex).
-}

data MajorVersion
  = J2SE_7_0
  | J2SE_6_0
  | J2SE_5_0
  | JDK_1_4
  | JDK_1_3
  | JDK_1_2
  | JDK_1_1
  deriving (Eq, Show)

getMajorVersion :: Get MajorVersion
getMajorVersion = do
    version <- getWord16be
    return $ convert version
    where convert 51 = J2SE_7_0
          convert 50 = J2SE_6_0
          convert 49 = J2SE_5_0
          convert 48 = JDK_1_4
          convert 47 = JDK_1_3
          convert 46 = JDK_1_2
          convert 45 = JDK_1_1

getConstantsPoolCount :: Get Word16
getConstantsPoolCount = getWord16 >>= return

getConstantsPool = undefined

data Tag
 = TAG_STRING
 | TAG_INTEGER
 | TAG_FLOAT
 | TAG_LONG
 | TAG_DOUBLE
 | TAG_CLASS_REF
 | TAG_STRING_REF
 | TAG_FIELD_REF
 | TAG_METHOD_REF
 | TAG_INTERFACE_REF
 | TAG_NAME_AND_TYPE

getTag :: Get Tag
getTag = do
  tag <- getWord8
  return $ convert tag
  where convert 1 = TAG_STRING
        convert 3 = TAG_INTEGER
        convert 4 = TAG_FLOAT
        convert 5 = TAG_LONG
        convert 6 = TAG_DOUBLE
        convert 7 = TAG_CLASS_REF
        convert 8 = TAG_STRING_REF
        convert 9 = TAG_FIELD_REF
        convert 10 = TAG_METHOD_REF
        convert 11 = TAG_INTERFACE_REF
        convert 12 = TAG_NAME_AND_TYPE

parse :: B.ByteString -> JavaClass
parse b = runGet getJavaClass $ L.fromChunks [b]

1 个答案:

答案 0 :(得分:1)

你可以像毛茸茸说的那样从GHCi调用它。

但是,如果您真正想要做的是从程序中调用它(也许您与提出此问题的人在同一个类中:Dissecting java Class file in haskell),那么将它放在同一目录中作为您的主程序,并在程序的顶部,放置:

import Parse

然后在您的程序中,阅读Java类文件。你可能已经看过这样的事了:

s <- readFile "MyJava.class"

如果我们想要将文件的内容读入String,那将会有效。但是parse命令需要ByteString,因此我们需要使用readFile的不同实现。所以在你的程序的顶部,放置:

import qualified Data.ByteString as BS

现在您可以像这样阅读文件:

s <- BS.readFile "MyJava.class"

现在您拥有了类数据,并且可以调用parse命令。这应该足以帮助您完成作业的这一部分。


<强>更新

让我们看看你给出的代码中函数parse的类型签名。

parse :: B.ByteString -> JavaClass

因此parse需要ByteString并返回JavaClass。现在让我们看一下JavaClass的定义。

data JavaClass = JavaClass {
    classMinorVersion :: Word16
  , classMajorVersion :: MajorVersion
  , classConstantPoolCount :: Word16
  } deriving Show

如上所述,所有parse都将为您提供次要版本,主要版本和常量池数。但是,如果您分析代码,您应该能够看到如何修改它以获取所需的其他信息。我建议你详细分析这些代码,以了解它是如何工作的。


更新2

如果你只是想在GHCI中尝试一下,你可以这样做:

:load Parse
:m + Data.ByteString
classData <- Data.ByteString.readFile "file.class"
Parse.parse classData

这是一个GHCi会议,我就是这样做的。

ghci> :load Parse
[1 of 1] Compiling Parse            ( Parse.hs, interpreted )
Ok, modules loaded: Parse.
ghci> :m + Data.ByteString
ghci> classData <- Data.ByteString.readFile "file.class"
Loading package array-0.4.0.1 ... linking ... done.
Loading package deepseq-1.3.0.1 ... linking ... done.
Loading package bytestring-0.10.0.2 ... linking ... done.
ghci> Parse.parse classData
Loading package containers-0.5.0.0 ... linking ... done.
Loading package binary-0.7.0.1 ... linking ... done.
JavaClass {classMinorVersion = 3, classMajorVersion = JDK_1_1, classConstantPoolCount = 85}

但是为了进入下一步,您将编写一个程序来调用parse,如上所述。假设您的程序位于名为“MyProgram.hs”的文件中。然后,您可以通过键入runghc MyProgram.hs

命令行运行它

我建议阅读Real World Haskell的第1章。

编辑:将“class”更改为“classData”,因为“class”是Haskell关键字。