After spending a few years with JavaScript, I wanted to branch out into lower levels of programming. While researching starter projects I came across Robert Nystrom's wonderful book Crafting Interpreters, and writing an interpreter in C sounded like a good way to go about that. However, copy-pasting code as I read the book lulled me into a passive learning state where I wasn't retaining the material. I tried typing each line of code instead, thinking that would force me to pay attention, but that just ended up being tedious.
For the book's first half, using Python instead of Java helped me stay focused. I decided to take the same approach for the second half, but which alternative language should I use? Python didn't meet my lower-level goals. C++ seemed like a natural choice, but it was too difficult for me as a newcomer to quickly get up and running (I did eventually finish a PoC using C++ and plan to revisit it someday). Rust came up a lot during my search and not wanting to waste any more time, I thought, what the heck.
With an alternative host language selected, I began working through clox and started to wonder how the same techniques I was learning would apply to a different guest language. For whatever reason, a DSL I had used years ago when I was at ExactTarget/Salesforce named AMPscript came to mind. Many super frustrating months later, I ended up with ETscript.
To be clear, this is a personal hobby project without a real-world application. Salesforce doesn't offer a trial/developer account for their Marketing Cloud product (AMPscript's domain), so I had to rely on online resources and my fading memory of using it (I left in 2019). In other words, this is all experimental. Though only a subset of functions are implemented, anyone who doesn't have Marketing Cloud access should still be able to get a feel for AMPscript's syntax by using this.
There isn't a fancy UI right now, so interaction takes place on the command line. Installing Docker then running it using the included shell scripts will be the fastest way to try things out.
After installing Docker, download ETscript's source code then change into the repo's directory:
$ git clone https://github.com/markgomez/etscript
$ cd etscript
Run the OS-appropriate script. For Linux and macOS:
$ ./dev
For Windows Terminal (recommended) or PowerShell 7:
> .\dev.ps1
When the Docker image finishes building, a command prompt similar to the following should appear after the container starts:
etscript@3a60ddb1e9b3:/workspace$
ETscript can be run in at least two ways. One way is interactively using the REPL:
# build and run
$ cargo run
ETscript 0.1.1 (press Control-C to quit)
>>> %%=Add(2, 2)=%%
4
Another way is to run your source code from a plain text file (recommended). Create a new
file, hello.ets
(filename extension doesn't matter), in the working directory then
copy-paste the following code into it:
%%[
var @you
set @you = "world"
]%%
Hello, %%=v(@you)=%%!
Then pass the source file to the run
command:
# build and run
$ cargo run hello.ets
Hello, world!
To leave the container and return to the host shell, press Control-D or type exit
:
etscript@3a60ddb1e9b3:/workspace$ exit
If you prefer not to use Docker, you'll need to install and set up the latest versions of
the following before running the cargo
commands:
- Rust
- .NET SDK 8 (required for Native AOT)
Many of AMPscript's functions for handling dates or formatting numbers and strings expect
.NET format specifiers.
C#, along with external links to Microsoft's documentation, is referenced throughout
AMPscript's own documentation.
Date libraries are complicated things. I figured using date APIs that are native to .NET
would be easier than trying to emulate them using
time
or chrono
.
ETscript uses SQLite to emulate data extensions (tables). A table named _test_table
is
automatically created and can be used to try out the data extension functions. You can
also create additional tables using your favorite SQL client, but you'll need to stick
to the following expected typenames, some of which are custom*:
Typename | Affinity |
---|---|
Integer | Integer |
Real | Real |
Boolean* | Numeric |
DateTime* | Numeric |
Text | Text |
The database file, etscript.db
, will be created (if it doesn't already exist) in the
working directory (e.g., the repo's root).
A quick Lookup
example:
%%[
UpsertData("_test_table", 1, "email", "me@example.com", "locale", "en-us")
]%%
Email: %%=Lookup("_test_table", "email", "locale", "en-us")=%%
$ cargo run hello.ets
Email: me@example.com
- Attribute scope
- Garbage collection
- Testing
- Documentation