字符串到变量名Haskell

use*_*704 0 variables parsing haskell eval parsec

我发现在Haskell中学习Parsec很难,所以我正在尝试创建我的大学项目(一个用表格解析文件的解析器)

x=3
y=4
z=x+y
badluck=(x+sqrt(z)*7)
Run Code Online (Sandbox Code Playgroud)

我设法写了一个函数,从文件中获取所有内容并验证文件,我坚持让x成为变量名.我知道在javascript中它是eval但我在Haskell中找不到类似的东西.请帮忙!

这是我到目前为止所做的事情:

ischarorscore :: Char -> Bool
ischarorscore a = if ((a>='A' && a<='Z') || (a>='a' && a<='z') || a=='_')
        then True 
        else False

ischarscoredigit :: Char -> Bool
ischarscoredigit a = if ((a>='A' && a<='Z') || 
                      (a>='a' && a<='z') || 
                       a=='_' ||
                       a>='0' && a<='9') then True else False

isvar :: String -> Bool
isvar [] = False
isvar (h:t) = if (ischarorscore h) then (isvarbody t) else False

isvarbody :: String -> Bool
isvarbody [] = True
isvarbody (h:t) = if (ischarscoredigit h) then (isvarbody t) else False

isoperator :: Char -> Bool
isoperator a = if (a=='+' || a=='-' || a=='*' || a=='/' || a=='^') then True else False

issqrt :: String -> Bool
issqrt [] = True
issqrt x = if (x=="sqrt(") then True else False

isnumber :: String -> Bool
isnumber (h:t) = if (h>='0' && h<='9') then isnumber t else False

charsetall :: String -> Bool
charsetall [] = True
charsetall (h:t) = if (h>='0' && h<='9' ||
                h>='a' && h<='z' ||
                h>='A' && h<='Z' ||
                h>='0' && h<='9' ||
                h=='+' || h=='-' || h=='*' || h=='/' || h=='^' ||           h=='(' || h==')' || h=='=')
                then charsetall t else False

charsetexpr :: String -> Bool
charsetexpr [] = True
charsetexpr (h:t) = if (h>='0' && h<='9' ||
                h>='a' && h<='z' ||
                h>='A' && h<='Z' ||
                h>='0' && h<='9' ||
                h=='+' || h=='-' || h=='*' || h=='/' || h=='^' || h=='(' || h==')')
                then charsetexpr t else False

paranthesis :: String -> Int -> Bool
paranthesis [] a = if (a==0) then True else False
paranthesis (h:t) a = if (h=='(') then (paranthesis t (a+1)) 
                    else (if (h==')') then
                      paranthesis t (a-1) else paranthesis t a)

paranthesis' :: String -> Bool
paranthesis' (h:t) = paranthesis (h:t) 0

obeyrules :: String -> Bool
obeyrules [] = True
obeyrules (h:t) = if (ischarorscore h) then (contvar t) 
              else if (h>='0' && h<='9') then (contnumber t)
              else if (h=='(' || h=='-') then obeyrules t
              else False

contnumber :: String -> Bool
contnumber [] = True
contnumber (h:t) = if (h>='0' && h<='9') then contnumber t
            else if (isoperator h) then contoperator t
            else if (h==')') then contoperator h
            else False

contvar :: String -> Bool
contvar [] = True
contvar (h:t) = if (ischarorscore h || h>='0' && h<='9') then contvar t
            else if (isoperator h) then contoperator t
            else if (h==')') then contoperator
            else False

contoperator :: String -> Bool
contoperator [] = True
contoperator (h:t) = if (ischarorscore h) then contvar t
                else if (h>='0' && h<='9') then contnumber t
                else if (h=='(') then obeyrules t
                else False


isexpression :: String -> Bool
isexpression [] = True
isexpression (h:t) = if (charsetexpr (h:t) && paranthesis' (h:t) && obeyrules (h:t)) then True else False
Run Code Online (Sandbox Code Playgroud)

isexpression函数结合了上述所有函数来验证文件的一行.项目是这样的:你必须读取一个看起来像上面那个的随机文件并包含变量name = expression并识别(,),+, - ,*,/,^这是幂,sqrt(k)其中k是数字或变量名称,mod,计算表达式并提供结果变量名称及其值.

J. *_*son 6

您最有可能想要解析所谓的抽象语法树.对于这里给出的片段,它可能看起来像

data AST
  = Var String
  | Lit Double
  | Plus AST AST
  | Mult AST AST
  | Sqrt AST
  | Assign String AST
  | Then AST AST
Run Code Online (Sandbox Code Playgroud)

有了AST的这种选择,你给出的代码片段就像

fragment :: AST
fragment = foldr1 Then [
    Assign "x" (Lit 3)
  , Assign "y" (Lit 4)
  , Assign "z" (Plus (Var "x") (Var "y"))
  , Assign "badluck" (Plus (Var "x") (Mult (Sqrt (Var "z")) (Lit 7)))
  ]
Run Code Online (Sandbox Code Playgroud)

然后我们选择一个求值函数,将这些AST值转换为它们的"规范"值.在这种情况下,基本规范值将是a Double,但由于AST可能由于引用未知变量而失败,因此我们将具有规范值Maybe Double.最后,由于我们希望在评估表达式时环境发生变化,因此我们返回更新的环境.

为了操作此评估者,我们需要一个被分配的环境概念.一个很好的选择是Data.Map

import           Control.Applicative
import qualified Data.Map            as Map
import           Data.Map            (Map)

type Env = Map String Double

eval :: Env -> AST -> Maybe (Double, Env)
eval env (Var s) = fmap (\x -> (x, env)) (Map.lookup s env)
eval env (Plus e1 e2) = do -- maybe monad
  (v1, env')  <- eval env  e1
  (v2, env'') <- eval env' e2
  return (v1 + v2, env'')
eval env (Mult e1 e2) = do -- maybe monad
  (v1, env')  <- eval env  e1
  (v2, env'') <- eval env' e2
  return (v1 * v2, env'')
eval env (Sqrt e)     = fmap (\(x, e) -> (sqrt x, e)) (eval env e)
eval env (Assign s e) = do
  (v, env') <- eval env e
  return (v, Map.insert s v env')
eval env (Then e1 e2) = do 
  (_, env')  <- eval env  e1
  (v, env'') <- eval env' e2
  return (v, env'')
Run Code Online (Sandbox Code Playgroud)

从技术上讲,这可能已经使用的单子转换栈完成State EnvMaybe,但如果编写繁琐的操作可能是一个更加清楚一点.