# Domain-Specific Languages Tomo supports defining different flavors of text that represent specific languages, with type safety guarantees that help prevent code injection. Code injection occurs when you insert untrusted user input into a string without properly escaping the user input. Tomo's `lang` feature addresses this issue by letting you define custom text types that automatically escape interpolated values and give type checking errors if you attempt to use one type of string where a different type of string is needed. ```tomo lang HTML: func escape(t:Text -> HTML): t = t:replace_all({ $/&/ = "&", $// = ">", $/"/ = """, $/'/ = "'", }) return HTML.without_escaping(t) func paragraph(content:HTML -> HTML): return $HTML"

$content

" ``` In this example, we're representing HTML as a language and we want to avoid situations where a malicious user might set their username to something like ``. ``` >> username := Text.read_line("Choose a username: ") = "" page := $HTML" Hello $username! How are you? " say(page.text_content) ``` What we _don't_ want to happen is to get a page that looks like: ```html Hello ! How are you? ``` Thankfully, Tomo handles automatic escaping and gives you a properly sanitized result: ```html Hello <script>alert('pwned')</script>! How are you? ``` This works because the compiler checks for a function in the HTML namespace called `HTML.escape` or any method that starts with `HTML.escape_` that takes a `Text` argument and returns an `HTML` value. When performing interpolation, the interpolation will only succeed if such a function exists and it will apply that function to the value before concatenating it. If you have a function that only accepts an `HTML` argument, you cannot use a `Text` value, you must produce a valid `HTML` value instead. The same is true for returning a value for a function that returns an `HTML` value or assigning to a variable that holds `HTML` values. Languages can also be built around a namespace-based method call API, instead of building a global function API that takes language arguments. For example, instead of building a global function called `execute()` that takes a `ShellScript` argument, you could instead build something like this: ```tomo lang Sh: func escape(text:Text -> Sh): return Sh.without_escaping("'" ++ text:replace($/'/, "''") ++ "'") func execute(sh:Sh -> Text): ... dir := ask("List which dir? ") cmd := $Sh@(ls -l @dir) result := cmd:execute() ```