2014-05-22 2 views
0

Я считаю, что это трудно узнать Парсек в Haskell, так что я пытаюсь сделать мой проект колледжа (синтаксический анализатор, который анализирует файлы с формойСтрока имени переменной Haskell

x=3 
y=4 
z=x+y 
badluck=(x+sqrt(z)*7) 

мне удалось написать функцию, которая получает все из файла и проверяет файл, и я застрял на том, чтобы 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 

Функция isexpression объединяет все перечисленные выше функции для проверки строки файла. Проект выглядит так: вам нужно прочитать случайный файл, который выглядит как выше, и содержит переменную name = expression и распознает (,), +, -, *, /,^которая является степенью, sqrt (k), где k это число или имя переменной, а также мода, вычисление выражений и предоставление результирующих имен переменных и их значений.

+3

Непонятный вопрос. Можете ли вы перефразировать то, что именно вы сделали (код, возможно?) И что именно вы хотите достичь (например, пары ввода и вывода)? –

+1

@larsmans 'ghci' называется :) (рассказать вам о' 'System.Eval.Haskell'] (http://hackage.haskell.org/package/plugins-1.5.1.4/docs/System-Eval-Haskell.html)) –

+0

Возможно, вы ищете переводчика, это неясно. Подумайте, используя подсказку, например: http://stackoverflow.com/questions/5582926/haskell-how-to-evaluate-a-string-like-12/5584638#5584638 –

ответ

5

Вы, скорее всего, хотите разобрать так называемое абстрактное дерево синтаксиса. Для фрагмента приведенного здесь, это может выглядеть

data AST 
    = Var String 
    | Lit Double 
    | Plus AST AST 
    | Mult AST AST 
    | Sqrt AST 
    | Assign String AST 
    | Then AST AST 

При таком выборе 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))) 
    ] 

Затем выбрать функцию оценщика, который преобразует эти значения AST к их «каноническим» значениям. В этом случае базовое каноническое значение будет 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'') 

Технически это можно было сделать с помощью монады стеки трансформатора State Env и Maybe, но, как написано операция может быть немного более ясной, если утомительным.

+0

Это замечательно , но я не понимаю слишком много кода. Я прочитал документацию Parsec и книгу «Real World Haskell» в главе 16 и до сих пор не могу обернуть ее вокруг. Этот код не Parsec, но все еще пытается понять, что это больно. Также я обновил вопрос – user3665704

+0

Parsec - это способ построения функции типа 'parse :: String -> AST'. Это первый шаг. Затем вы оцениваете AST с чем-то вроде 'eval Map.empty :: AST -> Maybe (Double, Env)'. –

2

Почему бы вам просто не использовать Data.Map? В Haskell нет «переменных», поэтому он будет служить вам лучше и будет намного проще в использовании.


Выработать немного: когда вы читаете идентификатор переменный из файла с нужным синтаксисом intialization, например, так:

<<name>> = <<value>> 

вставить новую запись в вашу Map, который бы просто (name, value) пара. Имена, скорее всего, будут иметь тип String, а типы с плавающей запятой должны быть в порядке для типа значения (если вы имеете дело только с числами). Каждый раз, когда назначение переменной зависит от другой переменной, используйте только lookup и используйте значение. Чтобы сохранить изменяющуюся карту значений, вы можете просто использовать State.

+0

Довольно минималистически объяснено, но хорошая идея, тем не менее – user3665704

Смежные вопросы