#pragma GCC optimize("Ofast")
#include "bits/stdc++.h"
#pragma GCC target("avx2")
#include <immintrin.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
#define rep1(i, n) for (int i = 1; i < (n); ++i)
#define rep1n(i, n) for (int i = 1; i <= (n); ++i)
#define repr(i, n) for (int i = (n) - 1; i >= 0; --i)
//#define pb push_back
#define eb emplace_back
#define all(a) (a).begin(), (a).end()
#define rall(a) (a).rbegin(), (a).rend()
#define each(x, a) for (auto &x : a)
#define ar array
#define vec vector
#define range(i, n) rep(i, n)
using namespace std;
using ll = long long;
using ull = unsigned long long;
using ld = long double;
using str = string;
using pi = pair<int, int>;
using pl = pair<ll, ll>;
using vi = vector<int>;
using vl = vector<ll>;
using vpi = vector<pair<int, int>>;
using vvi = vector<vi>;
int Bit(int mask, int b) { return (mask >> b) & 1; }
template<class T>
bool ckmin(T &a, const T &b) {
if (b < a) {
a = b;
return true;
}
return false;
}
template<class T>
bool ckmax(T &a, const T &b) {
if (b > a) {
a = b;
return true;
}
return false;
}
// [l, r)
template<typename T, typename F>
T FindFirstTrue(T l, T r, const F &predicat) {
--l;
while (r - l > 1) {
T mid = l + (r - l) / 2;
if (predicat(mid)) {
r = mid;
} else {
l = mid;
}
}
return r;
}
template<typename T, typename F>
T FindLastFalse(T l, T r, const F &predicat) {
return FindFirstTrue(l, r, predicat) - 1;
}
const int INFi = 2e9;
const ll INF = 2e18;
const int N = (1 << 17);
int a[N];
void add(int L, int R, int x) {
for(; L < R && L % 8 != 0; ++L) {
a[L] += x;
}
__m256i X = _mm256_set1_epi32(x);
for(; L < R && L + 8 <= R; L += 8) {
__m256i A = _mm256_load_si256((__m256i*) &a[L]);
__m256i z = _mm256_add_epi32(A, X);
_mm256_store_si256((__m256i*) &a[L], z);
}
for(; L < R; ++L) {
a[L] += x;
}
}
int tmp[256];
int get(int L, int R) {
int x = 0;
for(; L < R && L % 8 != 0; ++L) {
x ^= a[L];
}
__m256i X = _mm256_set1_epi32(0);
for(; L < R && L + 8 <= R; L += 8) {
__m256i A = _mm256_load_si256((__m256i*) &a[L]);
X = _mm256_xor_epi32(A, X);
}
for(; L < R; ++L) {
x ^= a[L];
}
_mm256_store_si256((__m256i*) &tmp[0], X);
for(int i = 0; i < 8; ++i) {
x ^= tmp[i];
}
return x;
}
int tin[N];
int head[N];
int up[N];
vi g[N];
int sz[N];
int T = 0;
void dfs_sz(int v) {
sz[v] = 1;
for(auto &u : g[v]) {
g[u].erase(find(all(g[u]), v));
dfs_sz(u);
sz[v] += sz[u];
}
sort(all(g[v]), [&] (const int &i, const int &j) { return sz[i] > sz[j]; });
}
void dfs_hld(int v) {
a[T] = v;
tin[v] = T++;
for(auto &u : g[v]) {
if (u == g[v][0]) {
up[u] = up[v];
head[u] = head[v];
} else {
up[u] = v;
head[u] = T;
}
dfs_hld(u);
}
}
void Add(int u, int v, int x) {
while (head[u] != head[v]) {
if (tin[u] > tin[v]) swap(u, v);
add(head[v], tin[v] + 1, x);
v = up[v];
}
add(min(tin[v], tin[u]), max(tin[v], tin[u]) + 1, x);
}
int Get(int u, int v) {
int x = 0;
while (head[u] != head[v]) {
if (tin[u] > tin[v]) swap(u, v);
x ^= get(head[v], tin[v] + 1);
v = up[v];
}
x ^= get(min(tin[v], tin[u]), max(tin[v], tin[u]) + 1);
return x;
}
void solve() {
int n; cin >> n;
int q; cin >> q;
rep(_, n - 1) {
int u, v; cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
dfs_sz(1);
dfs_hld(1);
rep(_, q) {
char tp; cin >> tp;
if (tp == '+') {
int u, v, x; cin >> u >> v >> x;
Add(u, v, x);
} else {
int u, v; cin >> u >> v;
cout << Get(u, v) << '\n';
}
}
}
signed main() {
ios_base::sync_with_stdio(false);
cin.tie(0);
cout << setprecision(12) << fixed;
int t = 1;
// cin >> t;
rep(i, t) {
solve();
}
return 0;
}