I actually like DSLs, but I think the right level of abstraction to develop them is by exposing the tokenizer, parser, AST, typesystem, semantic analyzer, and execution engine of the language as libraries in the language itself. Things like 'ast' and 'parser' in Python, package 'go' in Go, Clang for C++, etc. I've tried to fill this gap for HTML5 with Gumbo - it was initially designed to support a semantically-aware templating language in Google.
The reason I think DSLs are good but macros are bad is because DSLs make you think long and hard about the costs of implementing a new language. You have to pay for it up-front by writing a compiler and hooking it into your build system, even if you have help from the host language's libraries, and then you'll end up with something that's clearly a separate language and must be documented and learned separately. That cost makes it clear that creating a DSL is not something to do lightly, and you need a problem domain where there really are much higher-level abstractions that can't be captured by an existing programming language.
The problem with macros is that you can create a new language construct with a half dozen or so lines of code, use it in a few hundred places in the codebase, and now your maintainers have a few hundred problems.
The reason I think DSLs are good but macros are bad is because DSLs make you think long and hard about the costs of implementing a new language. You have to pay for it up-front by writing a compiler and hooking it into your build system, even if you have help from the host language's libraries, and then you'll end up with something that's clearly a separate language and must be documented and learned separately. That cost makes it clear that creating a DSL is not something to do lightly, and you need a problem domain where there really are much higher-level abstractions that can't be captured by an existing programming language.
The problem with macros is that you can create a new language construct with a half dozen or so lines of code, use it in a few hundred places in the codebase, and now your maintainers have a few hundred problems.