Forth翻译

时间:2014-10-03 06:02:00

标签: haskell

我最近开始学习Haskell,我正在尝试使用Haskell为FORTH语言编写解释器。但是我在尝试运行最基本的操作时遇到了问题。例如,FORTH中的一个简单程序(在Haskell中作为字符串)将如下:“1 2 +”返回包含整数3的堆栈,在Haskell中可以表示一个整数列表:[3]。

我还有从堆栈中推送,删除和弹出元素的功能。我有一个添加函数来从堆栈中添加两个元素。

现在,手头有这些功能,如何解析简单程序“1 2 +”才能返回[3]?我希望这个例子看起来像这样:

forthInterpreter "1 2 +"
[3]

任何帮助都会非常感激。如果您有任何澄清问题,请告诉我。 感谢。

2 个答案:

答案 0 :(得分:5)

您拥有的每个命令都是来自Stack -> Stack的函数。这使得解释命令变得容易。我使用[Int]作为Stack的类型,其中数字位于列表顶部的堆栈顶部。

interpretCommand :: String -> [Int] -> [Int]
interpretCommand "+" = lift2 (+)
interpretCommand "-" = lift2 (-)
interpretCommand "*" = lift2 (*)
interpretCommand "/" = lift2 div
interpretCommand "MOD" = lift2 mod
interpretCommand number = \zs -> read number : zs

其中lift2将2参数函数提升到列表前2个元素的函数。参数xy从第seems to have the convention开始交换,第一个参数被推入堆栈是函数的第一个参数,堆栈顶部的参数是第二个参数。功能。

lift2 :: (a -> a -> a) -> [a] -> [a]
lift2 f (x:y:zs) = f y x:zs
lift2 f _ = error "not enough data on the stack"

为了解释多个命令,我们用words将它们分开,它在空白处拆分一个字符串,解释每个命令,然后将它们组合在一起。

composition :: [a -> a] -> a -> a
composition = foldl (flip (.)) id

interpretCommands :: String -> [Int] -> [Int]
interpretCommands = composition . map interpretCommand . words

然后我们可以通过调用interpretCommands并将初始(空)堆栈传递给它来解释一串命令。以下是两个例子。

main = do
    print $ interpretCommands "1 2 +" []
    print $ interpretCommands "1 2 + 4 - 3" []
    print $ interpretCommands "10 5 / 3" []
    print $ interpretCommands "13 7 MOD 2 / 4 *" []

输出

[3]
[3,-1]
[3,2]
[12]

请注意,[3,-1]的顺序与您建议的顺序相反,因为3位于堆栈顶部,因此位于列表的开头。 (这是您在fourthadd的评论中预测的行为。)

答案 1 :(得分:1)

与Cirdec相同的答案,但也许这对初学者来说更容易阅读:

-- Data type to represent a command.
data Command = Push Int | Add | Subtract deriving Show

-- Type synonym: a Program is a list of commands.
type Program = [Command]

-- Type synonym: a Stack is a list of Int.
type Stack = [Int]

-- The interpreter entry point.
interpretCommands :: String -> Int
interpretCommands str = runProgram (parseProgram str)

-- Parse a string and turn it into the corresponding Program.
parseProgram :: String -> Program
parseProgram str = map toCommand (words str)
    where toCommand "+" = Add
          toCommand "-" = Subtract
          toCommand x = Push (read x)

-- Run a Program on the empty stack, return the result at the top of the result stack.
runProgram :: Program -> Int
runProgram program = head (runProgram' program [])

-- Run a program on a given stack, return the result stack.
runProgram' :: Program -> Stack -> Stack
runProgram' [] stack = stack
runProgram' (command:rest) stack = runProgram' rest (runCommand command stack)

-- Run an individual command.
runCommand :: Command -> Stack -> Stack
runCommand (Push n) stack = n:stack
runCommand Add (n:m:stack) = n+m:stack
runCommand Subtract (n:m:stack) = n-m:stack

我已将该计划分为两部分:parserrunProgram。第一个接受字符串并将其转换为要运行的程序的抽象表示。第二个解释这个抽象表示。这是一个很好的分离用于这样的问题;例如,当您希望编写更好的解析逻辑时,您可以修改parser,而不必担心违反runProgram逻辑。