【问题标题】:haskell command line哈斯克尔命令行
【发布时间】:2013-04-04 04:34:08
【问题描述】:

你好,我得到了在 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]

【问题讨论】:

  • 您能更清楚地了解您的尝试吗?您的意思是您在ghci 中加载了文件?还是你在编译使用它的代码时编译它并得到那个错误?
  • 是的,我用 ghci 加载了它,这很好,但我不知道使用什么命令行来实际运行程序
  • 加载后从该模块调用你需要的函数。就像:load Parse Parse.parse &lt;here will be input&gt; 并且在第100行的这段代码中有一个缩进错误
  • 请不要在发布答案后删除问题的内容。

标签: haskell


【解决方案1】:

你可以像 hairyhum 所说的那样从 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 关键字。

【讨论】:

  • 好的,我导入了你所说的内容,所以我们在同一页面上,我试图通过这个 haskell 程序解析器读取 java class.file 并能够计算某些东西,比如数字常量池中的项目数和类的字段数。
  • 好的,我现在更了解情况了。请参阅上面的更新。
  • 好的,谢谢你的澄清。我正在尝试尽可能多地研究它,尽管我仍然不知道如何使用它(我还是新手)。加载它后,我尝试使用“parse file.class”之类的行,但没有任何效果。更清楚地说,我不知道在命令行中输入什么来让程序执行它应该执行的操作。
  • 我对你的第二次更新感到困惑,因为如果我在 ghci 中输入任何这些内容,它会说不在范围内
  • 我确实在我的示例中发现了一个错误,我已经修复了它。 (如果您按照我最初的方式运行命令,那么当您输入class &lt;- Data.ByteString.readFile "file.class" 时,您应该会收到错误消息&lt;interactive&gt;:20:7: parse error on input
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多