Introduce concat hook for components and attributes

```hbs
<x-foo a="b" c={{d}} e={{f g}} h="{{i}} {{j k}}"></x-foo>
<foo   a="b" c={{d}} e={{f g}} h="{{i}} {{j k}}"></foo>
```

now compiles to

```js
(function() {
  var child0 = (function() {
    return {
      isHTMLBars: true,
      cachedFragment: null,
      build: function build(dom) {
        var el0 = dom.createDocumentFragment();
        return el0;
      },
      render: function render(context, env, contextualElement) {
        var dom = env.dom;
        dom.detectNamespace(contextualElement);
        if (this.cachedFragment === null) {
          this.cachedFragment = this.build(dom);
        }
        var fragment = dom.cloneNode(this.cachedFragment, true);
        return fragment;
      }
    };
  }());
  return {
    isHTMLBars: true,
    cachedFragment: null,
    build: function build(dom) {
      var el0 = dom.createDocumentFragment();
      var el1 = dom.createTextNode("");
      dom.appendChild(el0, el1);
      var el1 = dom.createTextNode("\n");
      dom.appendChild(el0, el1);
      var el1 = dom.createElement("foo");
      dom.setAttribute(el1,"a","b");
      dom.appendChild(el0, el1);
      return el0;
    },
    render: function render(context, env, contextualElement) {
      var dom = env.dom;
      var hooks = env.hooks, get = hooks.get, subexpr = hooks.subexpr, concat = hooks.concat, component = hooks.component, attribute = hooks.attribute;
      dom.detectNamespace(contextualElement);
      if (this.cachedFragment === null) {
        this.cachedFragment = this.build(dom);
      }
      var fragment = dom.cloneNode(this.cachedFragment, true);
      dom.repairClonedNode(fragment,[0]);
      var element0 = fragment.childNodes[2];
      var morph0 = dom.createUnsafeMorphAt(fragment,0,1,contextualElement);
      component(morph0, "x-foo", context, {"a": "b", "c": get(context, "d", env), "e": subexpr("f", context, [get(context, "g", env)], {}, {}, env), "h": concat([get(context, "i", env), " ", subexpr("j", context, [get(context, "k", env)], {}, {}, env)], env)}, {morph: morph0, template: child0}, env);
      attribute(element0, "c", false, context, get(context, "d", env), {}, env);
      attribute(element0, "e", false, context, subexpr("f", context, [get(context, "g", env)], {}, {}, env), {}, env);
      attribute(element0, "h", true, context, concat([get(context, "i", env), " ", subexpr("j", context, [get(context, "k", env)], {}, {}, env)], env), {}, env);
      return fragment;
    }
  };
}())
```