From 99ae23851c7576ddaca72b8e383ef4edcfd64f91 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Mon, 19 Aug 2024 14:41:04 -0400 Subject: [PATCH] Add docs for `lang` --- docs/README.md | 1 + docs/langs.md | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 docs/langs.md diff --git a/docs/README.md b/docs/README.md index 55bc63b..5bb35de 100644 --- a/docs/README.md +++ b/docs/README.md @@ -23,6 +23,7 @@ Information about Tomo's built-in types can be found here: - [Floating point numbers](nums.md) - [Integer Ranges](ranges.md) - [Integers](integers.md) +- [Languages](langs.md) - [Sets](sets.md) - [Structs](structs.md) - [Tables](tables.md) diff --git a/docs/langs.md b/docs/langs.md new file mode 100644 index 0000000..b9f9878 --- /dev/null +++ b/docs/langs.md @@ -0,0 +1,84 @@ +# 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("&", "&") + t = t:replace("<", "<") + t = t:replace(">", ">") + t = t:replace('"', """) + t = t:replace("'", "'") + return HTML.from_unsafe_text(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.from_unsafe_text("'$(text:replace("'", "''"))'") + + func execute(sh:Sh)->Text: + ... + +dir := Text.read_line("List which dir? ") +cmd := $Sh@(ls -l @dir) +result := cmd:execute() +```