array_pop($operator_stack), 'symbol' => $symbol, 'contents' => $template_parts, ); $template_parts = $parent_template_parts; break; case '$': $template_parts[] = array( 'type' => $operator, 'symbol' => $symbol, ); break; default: assert(false); exit(1); } } if ($scope_stack) { // TODO: Better error message here. echo "Template error.\n"; exit(1); } $end = substr($template, $last_offset); if ($end) { $template_parts[] = $end; } return $template_parts; } static function interpret($template_array, $params) { $output = ''; foreach($template_array as $template_part) { if (is_string($template_part)) { $output .= $template_part; } else { $symbol = $template_part['symbol']; $value = array_key_exists($symbol, $params) ? $params[$symbol] : null; switch($template_part['type']) { case '$': if ($value) { $output .= html_encode($value); } break; case '#': if ($value) { $output .= self::interpret_nested_content( $template_part['contents'], $params, $value); } break; case '^': if (!$value) { $output .= self::interpret( $template_part['contents'], $params); } break; default: assert(false); exit(1); } } } return $output; } static function interpret_nested_content($template_array, $params, $value) { if (is_object($value)) { return self::interpret($template_array, array_merge($params, (array) $value)); } if (is_array($value)) { // Just checking the first key to see if this is a list or object. // Should probably check every key? reset($value); if (is_int(key($value))) { $output = ''; foreach($value as $x) { if (is_object($x)) { $output .= self::interpret($template_array, array_merge($params, (array) $x)); } else if (is_array($x)) { $output .= self::interpret($template_array, array_merge($params, $x)); } else { // TODO: Better error? assert(false); exit(0); } } return $output; } else { return self::interpret($template_array, array_merge($params, $value)); } } else { return self::interpret($template_array, $params); } } }