Skip to content

Making YS be Valid YAML

I'll say it again. All YS syntax is 100% YAML. The YS compiler uses a third party YAML parser so there is no way that YS code cannot be YAML.

YS code also uses a lot of Clojure syntax. This makes sense since the code gets compiled to Clojure in the end.

Some Clojure code looks a lot like YAML but with subtle differences.

Today we'll talk about the little gotchas that can trip you up when you're writing YS code, and how to work around them.

YS + escaping🔗

The * operator is polymorphic. If you "multiply" a string by a number, it will repeat the string that many times.

$ ys -e 'say: "X" * 3'
Compile error: while scanning an alias
 in reader, line 2, column 10:
    say: "X" * 3
             ^

That didn't go as expected. What happened?

Well, that is simply not valid YAML. You can't have extra text after the end of a quoted string (except for comments).

We want the value of the key say to be a YS expression, which means that it needs to be a YAML plain (unquoted) scalar.

Plain scalars can't start with a YAML syntax character like #%[{'"!&*|>.

One way to get around this particular case is to use parens. Plain scalars can start with a paren.

$ ys -e 'say: ("X" * 3)'
XXX

A more general way is to use the + YS escape character.

$ ys -e 'say: +"X" * 3'
XXX

The + makes the value be a valid YAML plain scalar. YS will then ignore a + that precedes a YAML syntax character.

You can have whitespace after the +, so this is valid:

$ ys -e '
say: +
  "X" * 3'
XXX

Map and Vector Literals🔗

How about this?

$ ys -mc -Ye '=>: {a: 1, b: 2}'
Compile error: Flow mappings not allowed in code mode

The -mc flag tells YS to start in code mode. This is the default for -e but we use it here to make it clear.

YS does not allow YAML flow mappings or flow sequences in code mode.

We can use the + escape character to get around this:

$ ys -mc -Ye '=>: +{a: 1, b: 2}'
Compile error: mapping values are not allowed here
 in reader, line 2, column 8:
    =>: +{a: 1, b: 2}

Oops! What happened now?

Well, the + makes this a YAML plain scalar, but YAML plain scalars can't have : in them.

Also this is not the correct way to express a map literal in YS or Clojure. It's close, but:

  • No : is used
  • Strings need to be quoted
  • Commas are optional whitespace

Let's try again:

$ ys -mc -Ye '=>: +{"a" 1, "b" 2}'
a: 1
b: 2

There we go.

We could also have switched to data mode for the value, which has to allow YAML flow mappings and sequences.

$ ys -mc -Ye '=>:: {a: 1, b: 2}'
a: 1
b: 2

Sometimes normal YAML syntax (via data mode) is the best way to go.

We could even use a YAML block mapping here instead:

$ ys -mc -Ye '=>::
  a: 1
  b: 2'
a: 1
b: 2

There are more gotchas to talk about, but I'll save them for another time.

If you come across gotchas of your own, please share them in the comments!

Comments