From 817b4688d89b260b5a7b77c00a1e85cb0499ef24 Mon Sep 17 00:00:00 2001 From: Gergely Nagy Date: Sun, 26 Jan 2014 03:53:44 +0100 Subject: [PATCH] hy.contrib.walk: New contrib module for walking the Hy AST The hy.contrib.walk module provides a few functions to walk the Hy AST, and potentially transform it along the way. The main entry point is (walk), which takes two functions and a form as arguments, and applies the first (inner) function to each element of the form, building up a data structure of the same type as the original. Then applies outer (the second function) to the result. Two convenience functions are provided: (postwalk) and (prewalk), which do a depth-first, post/pre-order traversal of the form. Signed-off-by: Gergely Nagy --- hy/contrib/walk.hy | 47 ++++++++++++++++++++++++++++++ tests/__init__.py | 1 + tests/native_tests/contrib/walk.hy | 24 +++++++++++++++ 3 files changed, 72 insertions(+) create mode 100644 hy/contrib/walk.hy create mode 100644 tests/native_tests/contrib/walk.hy diff --git a/hy/contrib/walk.hy b/hy/contrib/walk.hy new file mode 100644 index 0000000..9fc3771 --- /dev/null +++ b/hy/contrib/walk.hy @@ -0,0 +1,47 @@ +;;; Hy AST walker +;; +;; Copyright (c) 2014 Gergely Nagy +;; +;; Permission is hereby granted, free of charge, to any person obtaining a +;; copy of this software and associated documentation files (the "Software"), +;; to deal in the Software without restriction, including without limitation +;; the rights to use, copy, modify, merge, publish, distribute, sublicense, +;; and/or sell copies of the Software, and to permit persons to whom the +;; Software is furnished to do so, subject to the following conditions: +;; +;; The above copyright notice and this permission notice shall be included in +;; all copies or substantial portions of the Software. +;; +;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +;; THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +;; DEALINGS IN THE SOFTWARE. + +(import [hy [HyExpression HyDict]] + [functools [partial]]) + +(defn walk [inner outer form] + "Traverses form, an arbitrary data structure. Applies inner to each + element of form, building up a data structure of the same type. + Applies outer to the result." + (cond + [(instance? HyExpression form) + (outer (HyExpression (map inner form)))] + [(instance? HyDict form) + (HyDict (outer (HyExpression (map inner form))))] + [(instance? list form) + ((type form) (outer (HyExpression (map inner form))))] + [true (outer form)])) + +(defn postwalk [f form] + "Performs depth-first, post-order traversal of form. Calls f on each + sub-form, uses f's return value in place of the original." + (walk (partial postwalk f) f form)) + +(defn prewalk [f form] + "Performs depth-first, pre-order traversal of form. Calls f on each + sub-form, uses f's return value in place of the original." + (walk (partial prewalk f) identity (f form))) diff --git a/tests/__init__.py b/tests/__init__.py index adcb583..e36ce11 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -17,3 +17,4 @@ from .native_tests.with_test import * # noqa from .native_tests.contrib.anaphoric import * # noqa from .native_tests.contrib.loop import * # noqa from .native_tests.contrib.meth import * # noqa +from .native_tests.contrib.walk import * # noqa diff --git a/tests/native_tests/contrib/walk.hy b/tests/native_tests/contrib/walk.hy new file mode 100644 index 0000000..854dfc7 --- /dev/null +++ b/tests/native_tests/contrib/walk.hy @@ -0,0 +1,24 @@ +(import [hy.contrib.walk [*]]) + +(def walk-form '(print {"foo" "bar" + "array" [1 2 3 [4]] + "something" (+ 1 2 3 4) + "quoted?" '(foo)})) + +(defn collector [acc x] + (.append acc x) + nil) + +(defn test-walk-identity [] + (assert (= (walk identity identity walk-form) + walk-form))) + +(defn test-walk [] + (let [[acc '()]] + (assert (= (walk (partial collector acc) identity walk-form) + [nil nil])) + (assert (= acc walk-form))) + (let [[acc []]] + (assert (= (walk identity (partial collector acc) walk-form) + nil)) + (assert (= acc [walk-form]))))