Haskell 快乐工具赋值给变量
Haskell Happy implement assign to variable
我正在尝试用 x = 4 和 pritn x 实现一些语言,使用 haskell 快乐的构造
到目前为止,我已经定义了这样的语法
terms
: term { [] }
| term terms { : }
term
: var '=' int { Assign }
| print var { Print }
当我 运行 它超过
x = 4
print x
y = 5
print y
我明白了
[Assign "x" 4, Print "x", Assign "y" 5, Print "y"]
现在想具体实现,但是不知道怎么实现"assign"
虽然我不擅长 haskell,但从快乐的文档中我看到了 "let" 实现并得到了一些环境 p 在
中通过和评估的想法
Exp : let var '=' Exp in Exp { \p -> ((, p):p) }
| Exp1 { }
Exp1 : Exp1 '+' Term { \p -> p + p }
| Exp1 '-' Term { \p -> p - p }
| Term { }
Term : Term '*' Factor { \p -> p * p }
| Term '/' Factor { \p -> p `div` p }
| Factor { }
Factor
: int { \p -> }
| var { \p -> case lookup p of
Nothing -> error "no var"
Just i -> i }
| '(' Exp ')' { }
我想 "assign" 实现必须对这个环境做些什么,但我找不到任何例子。我如何实施分配和打印,或者我在哪里可以找到它的信息或示例?
您已经非常熟悉解析器了。但是你想要构建的是一个 interpreter 用于你的小表达式语言,与解析逻辑分开。解析器将只为程序生成 AST,然后我们将单独对其进行评估。
代码实际上很小,但它被分成几个模块,所以我把它放在这个要点中:https://gist.github.com/sdiehl/c2dd1880e0ec6b65a120
我猜你的 AST 看起来像这样:
data Expr
= Var String
| Num Int
| Print Expr
| Assign String Int
deriving (Eq,Show)
解析器看起来不错,但我认为您需要添加一个 var
产生式,这样像 print x
和 print 1
这样的表达式在语法中都可以是格式正确的。
%token
int { TokenNum $$ }
var { TokenSym $$ }
print { TokenPrint }
'=' { TokenEq }
%%
terms
: term { [] }
| term terms { : }
term
: var { Var }
| var '=' int { Assign }
| print term { Print }
对于解释器,我们将使用 StateT + IO monad 来保存分配的变量并为我们程序中的每个 Print 函数调用 Haskell 的 print 函数。状态 monad 将保存变量与值的关联列表。 Assign
将简单地向列表添加一个新引用,而 Var
引用将在状态上使用 lookup
函数。
data Value
= VInt Int
| VUnit
instance Show Value where
show (VInt x) = show x
type Eval = StateT Env IO
type Env = [(String, Value)]
eval1 :: Expr -> Eval Value
eval1 expr = case expr of
Num a -> return (VInt a)
Var a -> do
env <- get
case lookup a env of
Just val -> return val
Nothing -> error "Not in scope"
Print a -> do
a' <- eval1 a
liftIO $ print a'
return VUnit
Assign ref val -> do
modify $ \s -> (ref, VInt val) : s
return VUnit
eval :: [Expr] -> IO ()
eval xs = evalStateT (mapM_ eval1 xs) []
仅此而已。
我正在尝试用 x = 4 和 pritn x 实现一些语言,使用 haskell 快乐的构造 到目前为止,我已经定义了这样的语法
terms
: term { [] }
| term terms { : }
term
: var '=' int { Assign }
| print var { Print }
当我 运行 它超过
x = 4
print x
y = 5
print y
我明白了
[Assign "x" 4, Print "x", Assign "y" 5, Print "y"]
现在想具体实现,但是不知道怎么实现"assign"
虽然我不擅长 haskell,但从快乐的文档中我看到了 "let" 实现并得到了一些环境 p 在
中通过和评估的想法Exp : let var '=' Exp in Exp { \p -> ((, p):p) }
| Exp1 { }
Exp1 : Exp1 '+' Term { \p -> p + p }
| Exp1 '-' Term { \p -> p - p }
| Term { }
Term : Term '*' Factor { \p -> p * p }
| Term '/' Factor { \p -> p `div` p }
| Factor { }
Factor
: int { \p -> }
| var { \p -> case lookup p of
Nothing -> error "no var"
Just i -> i }
| '(' Exp ')' { }
我想 "assign" 实现必须对这个环境做些什么,但我找不到任何例子。我如何实施分配和打印,或者我在哪里可以找到它的信息或示例?
您已经非常熟悉解析器了。但是你想要构建的是一个 interpreter 用于你的小表达式语言,与解析逻辑分开。解析器将只为程序生成 AST,然后我们将单独对其进行评估。
代码实际上很小,但它被分成几个模块,所以我把它放在这个要点中:https://gist.github.com/sdiehl/c2dd1880e0ec6b65a120
我猜你的 AST 看起来像这样:
data Expr
= Var String
| Num Int
| Print Expr
| Assign String Int
deriving (Eq,Show)
解析器看起来不错,但我认为您需要添加一个 var
产生式,这样像 print x
和 print 1
这样的表达式在语法中都可以是格式正确的。
%token
int { TokenNum $$ }
var { TokenSym $$ }
print { TokenPrint }
'=' { TokenEq }
%%
terms
: term { [] }
| term terms { : }
term
: var { Var }
| var '=' int { Assign }
| print term { Print }
对于解释器,我们将使用 StateT + IO monad 来保存分配的变量并为我们程序中的每个 Print 函数调用 Haskell 的 print 函数。状态 monad 将保存变量与值的关联列表。 Assign
将简单地向列表添加一个新引用,而 Var
引用将在状态上使用 lookup
函数。
data Value
= VInt Int
| VUnit
instance Show Value where
show (VInt x) = show x
type Eval = StateT Env IO
type Env = [(String, Value)]
eval1 :: Expr -> Eval Value
eval1 expr = case expr of
Num a -> return (VInt a)
Var a -> do
env <- get
case lookup a env of
Just val -> return val
Nothing -> error "Not in scope"
Print a -> do
a' <- eval1 a
liftIO $ print a'
return VUnit
Assign ref val -> do
modify $ \s -> (ref, VInt val) : s
return VUnit
eval :: [Expr] -> IO ()
eval xs = evalStateT (mapM_ eval1 xs) []
仅此而已。