Letter, Number, Symbol, Space, Repeat
Haskell 300 bytes

This code should have no trailing newline. The function m1 takes the input as a Char and returns the answer as a Char.
f1 (l1 :n1 :p1 :y1 :l2 :n2 :p2 :y2 :r3 )x1 |y1 &gt;p1 =b1 (x1 )y2 (f1 (r3 )x1 )y1 (n1 )n2 |p2 &lt;p1 =b1 (x1 )y1 (n1 )p2 (f1 (p2 :y2 :r3 )x1 )l2 |p2 &gt;p1 =b1 (x1 )p1 (l2 )l1 (n2 )n1
;b1 (x1 )s1 (r1 )b1 (r2 )r3 |x1 &lt;s1 =r1 |x1 &gt;b1 =r2 |s1 &lt;b1 =r3
;m1 =f1 &quot;d0 \t4 \r0 ~d3 {d1 `d3 [d1 @d3 :d2 /d3 !d4 \n0 ?d0 &quot;

I couldn't resist a challenge that someone claimed was impossible for &quot;conventional&quot; languages.
You may dispute whether Haskell counts, but the majority of keywords and identifiers are multiple characters and cannot be used. However, top level function definitions, lists, string literals, strict comparisons, pattern matching and branching with guards work, as long as letters come just before digits, and if symbols also come just before letters we have escape characters like \t and \r. Unfortunately the permutations that work for general programming don't allow numeric literals, so I couldn't get numbers in any useful way.
How it works:

The intervals of character classes are encoded in the string on the last line, with boundary characters at most of the symbol places and the results in most of the digit places, although some at the ends are padding.
The main function is m1.
x1 is the character being analyzed.
The f1 function breaks up the string with list pattern matching, and has three branches: for when the boundaries are symbols larger than space, for when the boundaries are escaped control characters smaller than space, and for handling the final comparison to space itself. The names of the list pieces are mnemonic for the first branch: Letter, Number, sPace, sYmbol, Remainder.
The b1 function handles branching for two boundary characters s1 &lt; b1 at a time.

Try it online