Because you might want an integer or a float type.
Because you might want to be able to test the logic of your code on one environment, the only environment where it will be run.
Because you might want a reasonable dependency tree.
Because you might not want to rewrite portions of your code to run them on the server side.
I could go on.
A secondary reason is to constrain your users so they don't end up writing constructs that will be impossible to manipulate with tooling. Early in Bazel's [2] history at Google, BUILD files were just ordinary Python, and you could use any Python code you wanted, and all that mattered were the calls you made into the build rules. This worked great, with people writing all sorts of list comprehensions and conditionals to minimize code duplication. And then tools came out to reformat BUILD files, and automatically query for dependencies, and detect dead binaries, and all sorts of other cool stuff. The tools were a lot more useful than the ability to include arbitrary code, but (because of the halting problem) they would choke whenever they encountered arbitrary flow constructs. So now arbitrary code is limited to the portions of the build system that explicitly allow it.
The EDSL approach (where you just use a regular programming language and a bunch of calls into the engine) can be a good idea, but you give up a lot in tooling. Usually it's most useful in the early stages of a project, where you don't have manpower to write the tooling anyway, and then becomes a legacy hassle as you assign more engineers, write more code, and get more leverage from writing tools.
[1] https://github.com/google/gumbo-parser
[2] http://bazel.io/