Haskell - 解析表达式中的变量状态(变量是否绑定?)

时间:2017-10-07 23:32:58

标签: parsing variables haskell

嗨所以我得到了一个函数,它接受一个字符串并计算值,无论是int还是bool。该字符串可以是:“* let X = 3 in + X 1 2”,表示“(let X = 3 in (X + 1)) * 2”。如果我要求布尔,这将给我8或假。很简单。但我还想检查解析器中绑定变量的状态。即如果我使用像这个“* let X = 3 in + X 1 X”这样的字符串作为输入,它将代表“(let X = 3 in (X + 1)) * X”,这使得最后一个X不受约束。这将给我一个错误,说变量A是未绑定的或其他东西。 如果有人能给我任何关于如何做到这一点的想法,我将非常感激!

已解析字符串的示例:

((let X = 3 in (X + 1)) * 2)

会给我:

(Mul (Let (Vari X) (Lit (Single 3)) (Sum (Vari X) (Lit (Single 1)))) (Vari X))

所以我的问题基本上是如何检查解析后的字符串是否有任何未绑定的变量。

data Number = Single Int | Many Int Number deriving (Eq, Show)

data Expr = Vari Chara | Lit Number | Sub Expr | Sum Expr Expr | Mul Expr Expr | Let Expr Expr Expr | If Expr Expr Expr deriving (Eq, Show)

2 个答案:

答案 0 :(得分:0)

重新解析解析后的表达式,并保存一组绑定变量。每当您遇到Let时,请记录下来,每次遇到Var时,请检查它是否在您的集合中,每次遇到其他内容时,请检查其子表达式。

checkVars' :: Set String -> Expr -> Bool
checkVars' bound (Var v) = S.member v set
checkVars' bound (Let var val expr) = checkVars' bound val && checkVars' (insert var bound) expr
checkVars' bound (Mul expr1 expr2) = checkVars' bound expr1 && checkVars' bound expr2
-- Similar for other ctors...

checkVars :: Expr -> Bool
checkVars = checkVars' S.empty

您也可以向相反的方向工作,从下到上构建一组自由变量,并在看到Let约束时删除变量。 (这感觉更自然。)

freeVars :: Expr -> Set String
freeVars (Var v) = S.singleton v
freeVars (Let var val expr) = S.union (freeVars val) $ S.delete var $ freeVars expr
freeVars (Add expr1 expr2) = S.union expr1 expr2
-- etc.

checkVars :: Expr -> Bool
checkVars = S.null . freeVars

-- With recursion-schemes
data ExprF a = Add a a
             | Mul a a
             | Let String a a
             | Var String
             | Lit Int
             deriving (Eq, Show, Read, Functor, Foldable, Traversable)

type Expr = Fix ExprF

freeVars :: Expr -> Set String
freeVars = cata go -- -XLambdaCase, anyone?
  where go :: ExprF (Set String) -> Set String
        go (Var var) = S.singleton var
        go (Let var val expr) = S.union val $ S.delete var expr
        go e = foldl S.union S.empty e -- Handle everything else

答案 1 :(得分:0)

我建议在带有绑定的语法中为变量(例如the extraordinarily useful bound library提供的变量)使用本地无名表示。在本地无名表示中,使用de Bruijn索引表示绑定变量,使用名称表示未绑定变量。当涉及到α等价和捕获 - 避免替换等问题时,这可以为您节省大量的工作,并且回答问题"这个变量是否受约束?"变得非常直截了当。

通常,本地无名表示是表面语法树上作用域检查操作的输出。我们可以调整@HNTW's answer中的代码来生成已检查的术语,而不是Failed to load ApplicationContext java.lang.IllegalStateException: Failed to load ApplicationContext at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:124) at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:83) at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:117) at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:83) at org.springframework.boot.test.autoconfigure.SpringBootDependencyInjectionTestExecutionListener.prepareTestInstance(SpringBootDependencyInjectionTestExecutionListener.java:44) at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:230) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:228) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:287) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:289) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:247) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61) at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70) at org.junit.runners.ParentRunner.run(ParentRunner.java:363) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191) at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter.runTestClass(JUnitTestClassExecuter.java:114) at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter.execute(JUnitTestClassExecuter.java:57) at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassProcessor.processTestClass(JUnitTestClassProcessor.java:66) at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:51) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35) at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24) at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:32) at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:93) at com.sun.proxy.$Proxy1.processTestClass(Unknown Source) at org.gradle.api.internal.tasks.testing.worker.TestWorker.processTestClass(TestWorker.java:108) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35) at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24) at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:146) at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:128) at org.gradle.internal.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:404) at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:63) at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:46) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:55) at java.lang.Thread.run(Thread.java:748) Caused by: java.lang.ArrayIndexOutOfBoundsException: 0 at org.springframework.core.MethodParameter.getGenericParameterType(MethodParameter.java:387) at org.springframework.core.SerializableTypeWrapper$MethodParameterTypeProvider.getType(SerializableTypeWrapper.java:337) at org.springframework.core.SerializableTypeWrapper.forTypeProvider(SerializableTypeWrapper.java:149) at org.springframework.core.ResolvableType.forType(ResolvableType.java:1333) at org.springframework.core.ResolvableType.forMethodParameter(ResolvableType.java:1249) at org.springframework.core.ResolvableType.forMethodParameter(ResolvableType.java:1217) at org.springframework.beans.factory.config.DependencyDescriptor.getResolvableType(DependencyDescriptor.java:246) at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1104) at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1066) at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:835) at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:741) at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:189) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1193) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1095) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:513) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483) at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197) at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:761) at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:867) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:543) at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:693) at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:360) at org.springframework.boot.SpringApplication.run(SpringApplication.java:303) at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:120) at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:98) at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:116) ... 49 more

Bool

data Scoped a = SVar a | SLet (Scoped a) (Scope () Scoped a) -- The expression being let-bound, and the subexpression with the new variable in scope | SLit Number | SSub (Scoped a) | SSum (Scoped a) (Scoped a) | SMul (Scoped a) (Scoped a) | SIf (Scoped a) (Scoped a) (Scoped a) deriving (Functor, Foldable, Traversable) instance Applicative Scoped where pure = SVar (<*>) = ap instance Monad Scoped where return = SVar SVar a >>= g = g a SLet expr scope >>= g = SLet (expr >>= g) (scope >>>= g) SLit x >>= g = SLit x SSub x >>= g = SSub (x >>= g) SSum x y >>= g = SSum (x >>= g) (y >>= g) SMul x y >>= g = SMul (x >>= g) (y >>= g) SIf cond t f >>= g = SIf (cond >>= g) (t >>= g) (f >>= g) scoped :: Expr -> Scoped Char scoped (Var x) = SVar x scoped (Let name expr sub) = SLet (scope expr) (abstract1 name $ scoped sub) scoped (Lit x) = SLit x scoped (Sub s) = SSub (scoped s) scoped (Sum x y) = SSum (scoped x) (scoped y) scoped (Mul x y) = SMul (scoped x) (scoped y) scoped (If cond t f) = SIf (scoped cond) (scoped t) (scoped f) 将表达式视为&#34;容器&#34;名字bounda实例执行替换(Monad接受函数映射名称(>>=) :: Scoped a -> (a -> Scoped b) -> Scoped b到术语a并将它们一起移植),Scoped b库使用此替换操作来实现像abstract1这样的工具,它绑定子表达式中的变量,以本地无名形式生成一个新的子表达式。

(取消单独的bound类型并不是不合理的,只是直接生成Expr作为解析器的输出。)

Scoped Char&#39; ScopedFoldable个实例可让您找到表达式中的所有未绑定变量。

Traversable

unboundVariables :: Scoped a -> [a] unboundVariables = toList &#39; isClosed组合符可以满足您的需求:如果表达式有任何自由变量,它会返回bound

如果你在一个范围内走动,你需要知道一个特定的变量是绑定的还是免费的,你可以在Var&#39; s False和{{}上进行模式匹配。 1}}构造函数,或使用the supplied Prisms

B

有关F工作原理的详情,请参阅this blog postthese slides