diff --git a/token.inc b/token.inc
new file mode 100644
index 0000000..c8ff282
--- /dev/null
+++ b/token.inc
@@ -0,0 +1,66 @@
+<?php
+
+class TokenObject {
+  protected $data = NULL;
+
+  function __construct($data) {
+    $this->data = $data;
+  }
+
+  public function __get($name) {
+    if (isset($this->{$name})) {
+      if (is_array($this->data)) {
+        return $this->data[$name];
+      }
+      elseif (is_object($this->data) && property_exists($this->data, $name)) {
+        return $this->data->{$name};
+      }
+    }
+  }
+
+  public function __set($name, $value) {
+    if (is_array($this->data)) {
+      return $this->data[$name];
+    }
+    elseif (is_object($this->data)) {
+      return $this->data->{$name};
+    }
+  }
+
+  public function __isset($name) {
+    if (is_array($this->data)) {
+      return array_key_exists($name, $this->data);
+    }
+    elseif (is_object($this->data)) {
+      return property_exists($this->data, $name);
+    }
+  }
+
+  public function __unset($name) {
+    if (is_array($this->data)) {
+      unset($this->data[$name]);
+    }
+    elseif (is_object($this->data)) {
+      unset($this->data->{$name});
+    }
+  }
+}
+
+class EntityTokenObject extends TokenObject {
+  private $entity_type;
+
+  function __construct($entity_type, $entity) {
+    $this->entity_type = $entity_type;
+    $this->data = $entity;
+  }
+
+  public function __toString() {
+    return entity_label($this->entity_type, $this->data);
+  }
+}
+
+class MenuTokenObject extends TokenObject {
+  public function __toString() {
+    return $this->data['title'];
+  }
+}
\ No newline at end of file
diff --git a/token.info b/token.info
index b65e29f..94e416a 100644
--- a/token.info
+++ b/token.info
@@ -6,3 +6,4 @@ files[] = token.install
 files[] = token.tokens.inc
 files[] = token.pages.inc
 files[] = token.test
+files[] = token.inc
\ No newline at end of file
diff --git a/token.module b/token.module
index 65f952d..47b12b1 100644
--- a/token.module
+++ b/token.module
@@ -922,6 +922,45 @@ function token_menu_link_load($mlid) {
   return $cache[$mlid];
 }
 
+function token_menu_link_load_all_parents($mlid) {
+  $cache = &drupal_static(__FUNCTION__, array());
+
+  if (!is_numeric($mlid)) {
+    return array();
+  }
+
+  if (!isset($cache[$mlid])) {
+    $cache[$mlid] = array();
+    $plid = db_query("SELECT plid FROM {menu_links} WHERE mlid = :mlid", array(':mlid' => $mlid))->fetchField();
+    while ($plid && $parent = token_menu_link_load($plid)) {
+      $cache[$mlid] = array($plid => new MenuTokenObject($parent)) + $cache[$mlid];
+      $plid = $parent['plid'];
+    }
+  }
+
+  return $cache[$mlid];
+}
+
+function token_taxonomy_term_load_all_parents($tid) {
+  $cache = &drupal_static(__FUNCTION__, array());
+
+  if (!is_numeric($tid)) {
+    return array();
+  }
+
+  if (!isset($cache[$tid])) {
+    $cache[$tid] = array();
+    $parents = taxonomy_get_parents_all($tid);
+    array_shift($parents); // Remove this term from the array.
+    $parents = array_reverse($parents);
+    foreach ($parents as $term) {
+      $cache[$tid][$term->tid] = new EntityTokenObject('taxonomy_term', $term);
+    }
+  }
+
+  return $cache[$tid];
+}
+
 /**
  * Get a translated book menu link by its mlid, without access checking.
  *
diff --git a/token.tokens.inc b/token.tokens.inc
index a80a4e9..0a6c2be 100644
--- a/token.tokens.inc
+++ b/token.tokens.inc
@@ -138,6 +138,12 @@ function token_token_info() {
       'description' => t("The root term of the taxonomy term."),
       'type' => 'term',
     );
+    $info['tokens']['term']['parents'] = array(
+      'name' => t('Parents'),
+      'description' => t("An array of all the term's parents, starting with the root."),
+      'type' => 'array',
+      'sub-type' => 'term',
+    );
 
     $info['tokens']['vocabulary']['machine-name'] = array(
       'name' => t('Machine-readable name'),
@@ -222,6 +228,12 @@ function token_token_info() {
     'description' => t("The menu link's root."),
     'type' => 'menu-link',
   );
+  $info['tokens']['menu-link']['parents'] = array(
+    'name' => t('Parents'),
+    'description' => t("An array of all the menu link's parents, starting with the root."),
+    'type' => 'array',
+    'sub-type' => 'menu-link',
+  );
 
   // Current page tokens.
   $info['types']['current-page'] = array(
@@ -402,6 +414,11 @@ function token_tokens($type, $tokens, array $data = array(), array $options = ar
           $type_name = node_type_get_name($node);
           $replacements[$original] = $sanitize ? check_plain($type_name) : $type_name;
           break;
+        case 'parents':
+          if ($parents = token_taxonomy_term_load_all_parents($term->tid)) {
+            $replacements[$original] = token_render_array($parents, $options);
+          }
+          break;
       }
     }
 
@@ -464,6 +481,12 @@ function token_tokens($type, $tokens, array $data = array(), array $options = ar
             $replacements[$original] = $sanitize ? check_plain($root_term->name) : $root_term->name;
           }
           break;
+
+        case 'parents':
+          if ($parents = token_taxonomy_term_load_all_parents($term->tid)) {
+            $replacements[$original] = token_render_array($parents, $options);
+          }
+          break;
       }
     }
 
@@ -477,6 +500,13 @@ function token_tokens($type, $tokens, array $data = array(), array $options = ar
       if ($root_term->tid != $term->tid) {
         $replacements += token_generate('term', $root_tokens, array('term' => $root_term), $options);
       }
+
+      // [term:parents:*] chained tokens.
+      if ($parents_tokens = token_find_with_prefix($tokens, 'parents')) {
+        if ($parents = token_taxonomy_term_load_all_parents($term->tid)) {
+          $replacements += token_generate('array', $parents_tokens, array('array' => $parents), $options);
+        }
+      }
     }
   }
 
@@ -593,6 +623,11 @@ function token_tokens($type, $tokens, array $data = array(), array $options = ar
             $replacements[$original] = $sanitize ? check_plain($root['title']) : $root['title'];
           }
           break;
+        case 'parents':
+          if ($parents = token_menu_link_load_all_parents($link['mlid'])) {
+            $replacements[$original] = token_render_array($parents, $options);
+          }
+          break;
       }
     }
 
@@ -606,6 +641,12 @@ function token_tokens($type, $tokens, array $data = array(), array $options = ar
     if ($url_tokens = token_find_with_prefix($tokens, 'url')) {
       $replacements += token_generate('url', $url_tokens, array('path' => $link['href']), $options);
     }
+    // [menu-link:parents:*] chained tokens.
+    if ($parents_tokens = token_find_with_prefix($tokens, 'parents')) {
+      if ($parents = token_menu_link_load_all_parents($link['mlid'])) {
+        $replacements += token_generate('array', $parents_tokens, array('array' => $parents), $options);
+      }
+    }
   }
 
   // Current page tokens.
