diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,5 +1,18 @@
 # Revision history for zeolite-lang
 
+## 0.24.0.1  -- 2023-12-10
+
+### Language
+
+* **[fix]** Fixes parsing of `&.` with prefix/infix function notation, e.g.,
+  ``a `foo&.bar` b``.
+
+### Compiler CLI
+
+* **[fix]** Fixes static linking of binaries for C++ compilers that don't handle
+  weak symbols. Previously, static linking would fail for some compilers with an
+  error message related to functions that handle unboxed types.
+
 ## 0.24.0.0  -- 2023-12-09
 
 ### Language
diff --git a/example/highlighter/.zeolite-module b/example/highlighter/.zeolite-module
new file mode 100644
--- /dev/null
+++ b/example/highlighter/.zeolite-module
@@ -0,0 +1,17 @@
+root: "../.."
+path: "example/highlighter"
+extra_paths: [
+  "example/highlighter/src"
+  "example/highlighter/test"
+]
+private_deps: [
+  "lib/testing"
+  "lib/util"
+  "lib/container"
+  "lib/file"
+]
+mode: binary {
+  category: ZeoliteHighlight
+  function: run
+  link_mode: static
+}
diff --git a/example/highlighter/README.md b/example/highlighter/README.md
new file mode 100644
--- /dev/null
+++ b/example/highlighter/README.md
@@ -0,0 +1,17 @@
+# Zeolite Code Highlighter
+
+This example is a work in progress and doesn't do anything yet. Eventually it
+will generate syntax-highlighted HTML from Zeolite source files.
+
+The module should still build and the tests should pass, however:
+
+```shell
+# This is just to locate the example code. Not for normal use!
+ZEOLITE_PATH=$(zeolite --get-path)
+
+# Compile the example.
+zeolite -p "$ZEOLITE_PATH" -r example/highlighter
+
+# Run the unit tests.
+zeolite -p "$ZEOLITE_PATH" -t example/highlighter
+```
diff --git a/example/highlighter/main.0rx b/example/highlighter/main.0rx
new file mode 100644
--- /dev/null
+++ b/example/highlighter/main.0rx
@@ -0,0 +1,27 @@
+/* -----------------------------------------------------------------------------
+Copyright 2023 Kevin P. Barry
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+----------------------------------------------------------------------------- */
+
+// Author: Kevin P. Barry [ta0kira@gmail.com]
+
+concrete ZeoliteHighlight {
+  @type run () -> ()
+}
+
+define ZeoliteHighlight {
+  run () {
+    // TODO
+  }
+}
diff --git a/example/highlighter/src/tokenizer.0rp b/example/highlighter/src/tokenizer.0rp
new file mode 100644
--- /dev/null
+++ b/example/highlighter/src/tokenizer.0rp
@@ -0,0 +1,72 @@
+/* -----------------------------------------------------------------------------
+Copyright 2023 Kevin P. Barry
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+----------------------------------------------------------------------------- */
+
+// Author: Kevin P. Barry [ta0kira@gmail.com]
+
+$ModuleOnly$
+
+@value interface TextToken<|#l> {
+  immutable
+
+  exportWith<#f> (TextFormatter<#l, #f>) -> (#f)
+}
+
+@value interface TextFormatter<#l|#f> {
+  format (String content:, #l label:) -> (#f)
+}
+
+concrete TextStream {
+  @type new (String) -> (TextStream)
+
+  @value current () -> (optional Char)
+  @value forward () -> (TextStream)
+  @value reset () -> (TextStream)
+  @value take () -> (String)
+  @value preview () -> (String)
+  @value atEnd () -> (Bool)
+}
+
+@value interface Tokenizer<#t, #v> {
+  tokenize (TextStream, KVReader<#t, Tokenizer<#t, #v>>) -> (optional #v, optional #t, Int)
+}
+
+concrete MultiTokenizer<#t, #v> {
+  refines Tokenizer<#t, #v>
+
+  visibility Tokenizer<#t, #v>
+
+  @type new () -> (#self)
+
+  @value include (#t) -> (#self)
+}
+
+concrete StreamTokenizer<#t, #v> {
+  @category new<#t, #v>
+    #v requires TextToken<any>
+  (KVReader<#t, Tokenizer<#t, #v>> tokenizers:, Tokenizer<#t, #v> default:) -> (StreamTokenizer<#t, #v>)
+
+  @value tokenizeAll (TextStream, Append<#v>) -> (#self)
+}
+
+concrete CharType {
+  @type lower (Char) -> (Bool)
+  @type upper (Char) -> (Bool)
+  @type digit (Char) -> (Bool)
+  @type alphaNum (Char) -> (Bool)
+  @type whitespace (Char) -> (Bool)
+  @type oneOf (Char, String) -> (Bool)
+  @type escapeBreaks (DefaultOrder<Char>) -> (String)
+}
diff --git a/example/highlighter/src/tokenizer.0rx b/example/highlighter/src/tokenizer.0rx
new file mode 100644
--- /dev/null
+++ b/example/highlighter/src/tokenizer.0rx
@@ -0,0 +1,201 @@
+/* -----------------------------------------------------------------------------
+Copyright 2023 Kevin P. Barry
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+----------------------------------------------------------------------------- */
+
+// Author: Kevin P. Barry [ta0kira@gmail.com]
+
+define TextStream {
+  $ReadOnly[source]$
+
+  @value String source
+  @value Int tokenStart
+  @value Int tokenEnd
+
+  new (source) {
+    return TextStream{ source, 0, 0 }
+  }
+
+  current () (char) {
+    char <- empty
+    if (tokenEnd < source.size()) {
+      char <- source.readAt(tokenEnd)
+    }
+  }
+
+  forward () {
+    if (tokenEnd < source.size()) {
+      tokenEnd <- tokenEnd+1
+    }
+    return self
+  }
+
+  reset () {
+    tokenEnd <- tokenStart
+    return self
+  }
+
+  take () {
+    cleanup {
+      tokenStart <- tokenEnd
+    } in return preview()
+  }
+
+  preview () {
+    return source.subSequence(tokenStart, tokenEnd-tokenStart)
+  }
+
+  atEnd () {
+    return tokenStart >= source.size()
+  }
+}
+
+define MultiTokenizer {
+  $ReadOnlyExcept[]$
+
+  @value [DefaultOrder<#t> & Append<#t>] types
+
+  new () {
+    return #self{ Vector<#t>.new() }
+  }
+
+  include (type) {
+    \ types.append(type)
+    return self
+  }
+
+  tokenize (input, tokenizers) (token, nextType, popCount) {
+    token <- empty
+    nextType <- empty
+    popCount <- 0
+    traverse (types.defaultOrder() -> #t type) {
+      scoped {
+        optional Tokenizer<#t, #v> tokenizer <- tokenizers.get(type)
+      } in if (`present` tokenizer) {
+        token, nextType, popCount <- require(tokenizer).tokenize(input, tokenizers)
+      }
+    } update {
+      if (`present` token) {
+        break
+      }
+    }
+  }
+}
+
+define StreamTokenizer {
+  $ReadOnlyExcept[]$
+
+  @value KVReader<#t, Tokenizer<#t, #v>> tokenizers
+  @value Tokenizer<#t, #v> defaultTokenizer
+
+  new (tokenizers, defaultTokenizer) {
+    return delegate -> StreamTokenizer<#t, #v>
+  }
+
+  tokenizeAll (input, output) {
+    scoped {
+      Stack<Tokenizer<#t, #v>> tokenizerStack <- Vector<Tokenizer<#t, #v>>.new().push(defaultTokenizer)
+      Tokenizer<#t, #v> tokenizer <- defaultTokenizer
+      optional #v token <- empty
+      optional #t nextType <- empty
+      Int popCount <- 0
+    } in while (!input.atEnd()) {
+      $Hidden[tokenizerStack,defaultTokenizer]$
+      $ReadOnly[tokenizer]$
+      token, nextType, popCount <- tokenizer.tokenize(input, tokenizers)
+    } update {
+      // Process new token.
+      if (`present` token) {
+        \ output.append(`require` token)
+      } else {
+        // Tokenizing failed => let the caller deal with the rest of the input.
+        break
+      }
+      // Pop tokenizers.
+      while (popCount > 0 && tokenizerStack.size() > 0) {
+        tokenizer <- tokenizerStack.pop()
+      } update {
+        popCount <- popCount-1
+      }
+      if (popCount > 0) {
+        tokenizer <- defaultTokenizer
+        popCount <- 0
+      }
+      // Set new tokenizer.
+      scoped {
+        optional Tokenizer<#t, #v> nextTokenizer <- empty
+        if (`present` nextType) {
+          nextTokenizer <- tokenizers.get(`require` nextType)
+        }
+      } in if (`present` nextTokenizer) {
+        \ tokenizerStack.push(tokenizer)
+        tokenizer <- `require` nextTokenizer
+      }
+    }
+    return self
+  }
+}
+
+define CharType {
+  $ReadOnlyExcept[]$
+
+  @category KVReader<Char, String> escapes <- HashedMap<Char, String>.new()
+      .set('\t', "\\t")
+      .set('\n', "\\n")
+      .set('\r', "\\r")
+      .set('\"', "\\\"")
+
+  lower (c) {
+    return c >= 'a' && c <= 'z'
+  }
+
+  upper (c) {
+    return c >= 'A' && c <= 'Z'
+  }
+
+  digit (c) {
+    return c >= '0' && c <= '9'
+  }
+
+  alphaNum (c) {
+    return lower(c) || upper(c) || digit(c)
+  }
+
+  whitespace (c) {
+    return c `oneOf` "\n\t\r "
+  }
+
+  oneOf (c, allowed) (match) {
+    match <- false
+    traverse (allowed.defaultOrder() -> Char c2) {
+      if (c == c2) {
+        return true
+      }
+    }
+  }
+
+  escapeBreaks (string) {
+    [Append<Formatted> & Build<String>] builder <- String.builder()
+    traverse (string.defaultOrder() -> Char c) {
+      scoped {
+        optional String replacement <- escapes.get(c)
+      } in if (`present` replacement) {
+        \ builder.append(`require` replacement)
+      } else {
+        \ builder.append(c)
+      }
+    }
+    return builder.build()
+  }
+}
diff --git a/example/highlighter/src/zeolite.0rp b/example/highlighter/src/zeolite.0rp
new file mode 100644
--- /dev/null
+++ b/example/highlighter/src/zeolite.0rp
@@ -0,0 +1,72 @@
+/* -----------------------------------------------------------------------------
+Copyright 2023 Kevin P. Barry
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+----------------------------------------------------------------------------- */
+
+// Author: Kevin P. Barry [ta0kira@gmail.com]
+
+$ModuleOnly$
+
+concrete ZeoliteToken {
+  refines Formatted
+  refines TestCompare<ZeoliteToken>
+  refines TextToken<String>
+
+  visibility Tokenizer<String, ZeoliteToken>
+
+  @type new (String content:, String label:) -> (optional ZeoliteToken)
+}
+
+@type interface NamedTokenizer {
+  tokenizer () -> (Tokenizer<String, ZeoliteToken>)
+  tokenizerName () -> (String)
+}
+
+concrete ZeoliteTokenizers {
+  refines KVReader<String, Tokenizer<String, ZeoliteToken>>
+
+  @type new () -> (ZeoliteTokenizers)
+
+  @value include<#t>
+    #t defines NamedTokenizer
+  () -> (ZeoliteTokenizers)
+}
+
+concrete ZeoliteSelectFrom {
+  refines Tokenizer<String, ZeoliteToken>
+
+  visibility Tokenizer<String, ZeoliteToken>
+
+  @type new () -> (ZeoliteSelectFrom)
+
+  @value include<#t>
+    #t defines NamedTokenizer
+  () -> (ZeoliteSelectFrom)
+}
+
+concrete ZeoliteWhitespace {
+  defines NamedTokenizer
+}
+
+concrete ZeoliteLineComment {
+  defines NamedTokenizer
+}
+
+concrete ZeoliteCategoryName {
+  defines NamedTokenizer
+}
+
+concrete ZeoliteParamName {
+  defines NamedTokenizer
+}
diff --git a/example/highlighter/src/zeolite.0rx b/example/highlighter/src/zeolite.0rx
new file mode 100644
--- /dev/null
+++ b/example/highlighter/src/zeolite.0rx
@@ -0,0 +1,214 @@
+/* -----------------------------------------------------------------------------
+Copyright 2023 Kevin P. Barry
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+----------------------------------------------------------------------------- */
+
+// Author: Kevin P. Barry [ta0kira@gmail.com]
+
+define ZeoliteToken {
+  @value String content
+  @value String label
+
+  new (content, label) {
+    if (content.size() < 1) {
+      return empty
+    } else {
+      return delegate -> #self
+    }
+  }
+
+  exportWith (formatter) {
+    return formatter.format(content: content, label: label)
+  }
+
+  testCompare (actual, report) {
+    \ MultiChecker.new(report)
+        .tryCheck(
+            title: "content",
+            actual.content(),
+            CheckValue:equals(content))
+        .tryCheck(
+            title: "label",
+            actual.label(),
+            CheckValue:equals(label))
+  }
+
+  formatted () {
+    return String.builder()
+        .append("ZeoliteToken{\"")
+        .append(CharType.escapeBreaks(content))
+        .append("\",")
+        .append(label)
+        .append("}")
+        .build()
+  }
+
+  @value content () -> (String)
+  content () {
+    return content
+  }
+
+  @value label () -> (String)
+  label () {
+    return label
+  }
+}
+
+define ZeoliteTokenizers {
+  $ReadOnlyExcept[]$
+
+  @value HashedMap<String, Tokenizer<String, ZeoliteToken>> tokenizers
+
+  new () {
+    return #self{ HashedMap<String, Tokenizer<String, ZeoliteToken>>.new() }
+  }
+
+  include () {
+    \ #t.tokenizerName() `tokenizers.set` #t.tokenizer()
+    return self
+  }
+
+  get (k) {
+    return delegate -> `tokenizers.get`
+  }
+}
+
+define ZeoliteSelectFrom {
+  $ReadOnlyExcept[]$
+
+  @value MultiTokenizer<String, ZeoliteToken> selector
+
+  new () {
+    return #self{ MultiTokenizer<String, ZeoliteToken>.new() }
+  }
+
+  include () {
+    \ selector.include(#t.tokenizerName())
+    return self
+  }
+
+  tokenize (input, tokenizers) {
+    return delegate -> `selector.tokenize`
+  }
+}
+
+define ZeoliteWhitespace {
+  refines Tokenizer<String, ZeoliteToken>
+
+  tokenizer () {
+    return #self{ }
+  }
+
+  tokenizerName () {
+    return typename<#self>().formatted()
+  }
+
+  tokenize (input, tokenizers) (token, nextType, popCount) {
+    \ input.reset()
+    token <- empty
+    nextType <- empty
+    popCount <- 0
+    optional Char current <- input.current()
+    while (`present` current && CharType.whitespace(`require` current)) {
+      current <- input.forward().current()
+    }
+    token <- ZeoliteToken.new(content: input.take(), label: tokenizerName())
+  }
+}
+
+define ZeoliteLineComment {
+  refines Tokenizer<String, ZeoliteToken>
+
+  tokenizer () {
+    return #self{ }
+  }
+
+  tokenizerName () {
+    return typename<#self>().formatted()
+  }
+
+  tokenize (input, tokenizers) (token, nextType, popCount) {
+    \ input.reset()
+    token <- empty
+    nextType <- empty
+    popCount <- 0
+    \ input.forward().forward()
+    if (input.preview() != "//") {
+      return _
+    }
+    optional Char current <- input.current()
+    while (`present` current && !CharType.oneOf(`require` current, "\r\n")) {
+      current <- input.forward().current()
+    }
+    token <- ZeoliteToken.new(content: input.take(), label: tokenizerName())
+  }
+}
+
+define ZeoliteCategoryName {
+  refines Tokenizer<String, ZeoliteToken>
+
+  tokenizer () {
+    return #self{ }
+  }
+
+  tokenizerName () {
+    return typename<#self>().formatted()
+  }
+
+  tokenize (input, tokenizers) (token, nextType, popCount) {
+    \ input.reset()
+    token <- empty
+    nextType <- empty
+    popCount <- 0
+    optional Char current <- input.current()
+    if (! `present` current || !CharType.upper(`require` current)) {
+      return _
+    }
+    while (`present` current && CharType.alphaNum(`require` current)) {
+      current <- input.forward().current()
+    }
+    token <- ZeoliteToken.new(content: input.take(), label: tokenizerName())
+  }
+}
+
+define ZeoliteParamName {
+  refines Tokenizer<String, ZeoliteToken>
+
+  tokenizer () {
+    return #self{ }
+  }
+
+  tokenizerName () {
+    return typename<#self>().formatted()
+  }
+
+  tokenize (input, tokenizers) (token, nextType, popCount) {
+    \ input.reset()
+    token <- empty
+    nextType <- empty
+    popCount <- 0
+    optional Char current <- input.current()
+    if (! `present` current || `require` current != '#') {
+      return _
+    }
+    current <- input.forward().current()
+    if (! `present` current || !CharType.lower(`require` current)) {
+      return _
+    }
+    while (`present` current && CharType.alphaNum(`require` current)) {
+      current <- input.forward().current()
+    }
+    token <- ZeoliteToken.new(content: input.take(), label: tokenizerName())
+  }
+}
diff --git a/example/highlighter/test/zeolite.0rt b/example/highlighter/test/zeolite.0rt
new file mode 100644
--- /dev/null
+++ b/example/highlighter/test/zeolite.0rt
@@ -0,0 +1,49 @@
+/* -----------------------------------------------------------------------------
+Copyright 2023 Kevin P. Barry
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+----------------------------------------------------------------------------- */
+
+// Author: Kevin P. Barry [ta0kira@gmail.com]
+
+testcase "working tests" {
+  success TestChecker
+}
+
+unittest test {
+  ZeoliteTokenizers allTokenizers <- ZeoliteTokenizers.new()
+      .include<ZeoliteWhitespace>()
+      .include<ZeoliteLineComment>()
+      .include<ZeoliteCategoryName>()
+      .include<ZeoliteParamName>()
+  ZeoliteSelectFrom categoryOrParam <- ZeoliteSelectFrom.new()
+      .include<ZeoliteWhitespace>()
+      .include<ZeoliteLineComment>()
+      .include<ZeoliteCategoryName>()
+      .include<ZeoliteParamName>()
+
+  TextStream input <- TextStream.new("Category #param // done")
+  Vector<ZeoliteToken> output <- Vector<ZeoliteToken>.new()
+
+  \ StreamTokenizer:new(tokenizers: allTokenizers, default: categoryOrParam).tokenizeAll(input, output)
+
+  DefaultOrder<ZeoliteToken> expected <- Vector<ZeoliteToken>.new()
+      .append(`require` ZeoliteToken.new(content: "Category", label: "ZeoliteCategoryName"))
+      .append(`require` ZeoliteToken.new(content: " ", label: "ZeoliteWhitespace"))
+      .append(`require` ZeoliteToken.new(content: "#param", label: "ZeoliteParamName"))
+      .append(`require` ZeoliteToken.new(content: " ", label: "ZeoliteWhitespace"))
+      .append(`require` ZeoliteToken.new(content: "// done", label: "ZeoliteLineComment"))
+
+  \ input.atEnd() `Matches:with` CheckValue:equals(true)
+  \ output `Matches:with` CheckSequence:matches(CheckSequence:using(expected))
+}
diff --git a/src/CompilerCxx/Procedure.hs b/src/CompilerCxx/Procedure.hs
--- a/src/CompilerCxx/Procedure.hs
+++ b/src/CompilerCxx/Procedure.hs
@@ -622,8 +622,8 @@
     compile (Expression c (CategoryCall c2 cn (FunctionCall c fn ps as)) [])
   callFunctionSpec c as (FunctionSpec _ (TypeFunction c2 tn) fn ps) =
     compile (Expression c (TypeCall c2 tn (FunctionCall c fn ps as)) [])
-  callFunctionSpec c as (FunctionSpec _ (ValueFunction c2 e0) fn ps) =
-    compile (Expression c (ParensExpression c2 e0) [ValueCall c AlwaysCall (FunctionCall c fn ps as)])
+  callFunctionSpec c as (FunctionSpec _ (ValueFunction c2 o e0) fn ps) =
+    compile (Expression c (ParensExpression c2 e0) [ValueCall c o (FunctionCall c fn ps as)])
   callFunctionSpec c as (FunctionSpec c2 UnqualifiedFunction fn ps) =
     compile (Expression c (UnqualifiedCall c2 (FunctionCall c fn ps as)) [])
   compile (Literal l) = compileValueLiteral l
@@ -675,8 +675,8 @@
     compile (Expression c (CategoryCall c2 cn (FunctionCall c fn ps (Positional [(Nothing,e1),(Nothing,e2)]))) [])
   compile (InfixExpression c e1 (FunctionOperator _ (FunctionSpec _ (TypeFunction c2 tn) fn ps)) e2) =
     compile (Expression c (TypeCall c2 tn (FunctionCall c fn ps (Positional [(Nothing,e1),(Nothing,e2)]))) [])
-  compile (InfixExpression c e1 (FunctionOperator _ (FunctionSpec _ (ValueFunction c2 e0) fn ps)) e2) =
-    compile (Expression c (ParensExpression c2 e0) [ValueCall c AlwaysCall (FunctionCall c fn ps (Positional [(Nothing,e1),(Nothing,e2)]))])
+  compile (InfixExpression c e1 (FunctionOperator _ (FunctionSpec _ (ValueFunction c2 o e0) fn ps)) e2) =
+    compile (Expression c (ParensExpression c2 e0) [ValueCall c o (FunctionCall c fn ps (Positional [(Nothing,e1),(Nothing,e2)]))])
   compile (InfixExpression c e1 (FunctionOperator _ (FunctionSpec c2 UnqualifiedFunction fn ps)) e2) =
     compile (Expression c (UnqualifiedCall c2 (FunctionCall c fn ps (Positional [(Nothing,e1),(Nothing,e2)]))) [])
   compile (InfixExpression _ e1 (NamedOperator c o) e2) = do
diff --git a/src/Parser/Procedure.hs b/src/Parser/Procedure.hs
--- a/src/Parser/Procedure.hs
+++ b/src/Parser/Procedure.hs
@@ -492,9 +492,12 @@
   sourceParser = valueFunc <|> categoryFunc <|> typeFunc where
     valueFunc = do
       c <- getSourceContext
-      q <- try sourceParser
-      valueSymbolGet
-      return $ ValueFunction [c] q
+      -- NOTE: We duplicate parsing of ValueOperation here so that we don't
+      -- inadvertently parse "&" in "&." separately.
+      s <- try sourceParser
+      vs <- many (try sourceParser)
+      o <- (valueSymbolGet >> return AlwaysCall) <|> (valueSymbolMaybeGet >> return CallUnlessEmpty)
+      return $ ValueFunction [c] o $ Expression [c] s vs
     categoryFunc = do
       c <- getSourceContext
       q <- try $ do  -- Avoids consuming the type name if : isn't present.
diff --git a/src/Test/Procedure.hs b/src/Test/Procedure.hs
--- a/src/Test/Procedure.hs
+++ b/src/Test/Procedure.hs
@@ -354,7 +354,7 @@
                                 (Literal (IntegerLiteral _ False 1))
                                 (FunctionOperator _
                                   (FunctionSpec _
-                                    (ValueFunction _
+                                    (ValueFunction _ AlwaysCall
                                       (Expression _ (NamedVariable (OutputValue _ (VariableName "something"))) []))
                                     (FunctionName "foo") (Positional [])))
                                 (Literal (IntegerLiteral _ False 2))) -> True
@@ -366,7 +366,7 @@
                                 (Literal (IntegerLiteral _ False 1))
                                   (FunctionOperator _
                                     (FunctionSpec _
-                                      (ValueFunction _
+                                      (ValueFunction _ AlwaysCall
                                         (Expression _ (BuiltinCall _ (FunctionCall _ BuiltinRequire (Positional [])
                                           (Positional [(Nothing,Expression _ (NamedVariable (OutputValue _ (VariableName "x"))) [])]))) []))
                                         (FunctionName "foo") (Positional [])))
@@ -407,7 +407,7 @@
                               (UnaryExpression _
                                 (FunctionOperator _
                                   (FunctionSpec _
-                                    (ValueFunction _
+                                    (ValueFunction _ AlwaysCall
                                       (Expression _ (NamedVariable (OutputValue _ (VariableName "bits"))) []))
                                     (FunctionName "not") (Positional [])))
                                 (Literal (IntegerLiteral _ False 2))) -> True
@@ -418,8 +418,20 @@
                               UnaryExpression _
                                   (FunctionOperator _
                                     (FunctionSpec _
-                                      (ValueFunction _
+                                      (ValueFunction _ AlwaysCall
                                         (Expression _ (BuiltinCall _ (FunctionCall _ BuiltinRequire (Positional [])
+                                          (Positional [(Nothing,Expression _ (NamedVariable (OutputValue _ (VariableName "x"))) [])]))) []))
+                                        (FunctionName "not") (Positional [])))
+                                (Literal (IntegerLiteral _ False 2)) -> True
+                              _ -> False),
+
+    checkParsesAs "`strong(x)&.not` 2"
+                  (\e -> case e of
+                              UnaryExpression _
+                                  (FunctionOperator _
+                                    (FunctionSpec _
+                                      (ValueFunction _ CallUnlessEmpty
+                                        (Expression _ (BuiltinCall _ (FunctionCall _ BuiltinStrong (Positional [])
                                           (Positional [(Nothing,Expression _ (NamedVariable (OutputValue _ (VariableName "x"))) [])]))) []))
                                         (FunctionName "not") (Positional [])))
                                 (Literal (IntegerLiteral _ False 2)) -> True
diff --git a/src/Types/Builtin.hs b/src/Types/Builtin.hs
--- a/src/Types/Builtin.hs
+++ b/src/Types/Builtin.hs
@@ -16,7 +16,6 @@
 
 -- Author: Kevin P. Barry [ta0kira@gmail.com]
 
-{-# LANGUAGE CPP #-}
 {-# LANGUAGE Safe #-}
 
 module Types.Builtin (
@@ -106,9 +105,6 @@
 isOpaqueMulti _               = False
 
 requiredStaticTypes :: Set.Set CategoryName
-#ifdef darwin_HOST_OS
--- Weak linking doesn't work on MacOS, so we need these in order to link against
--- boxed.cpp without linker errors.
 requiredStaticTypes = Set.fromList [
     BuiltinBool,
     BuiltinChar,
@@ -117,6 +113,3 @@
     BuiltinPointer,
     BuiltinIdentifier
   ]
-#else
-requiredStaticTypes = Set.empty
-#endif
diff --git a/src/Types/Procedure.hs b/src/Types/Procedure.hs
--- a/src/Types/Procedure.hs
+++ b/src/Types/Procedure.hs
@@ -271,7 +271,7 @@
   CategoryFunction [c] CategoryName |
   TypeFunction [c] TypeInstanceOrParam |
   -- TODO: Does this need to allow conversion calls?
-  ValueFunction [c] (Expression c) |
+  ValueFunction [c] ValueCallType (Expression c) |
   UnqualifiedFunction
   deriving (Show)
 
diff --git a/tests/cli-tests.sh b/tests/cli-tests.sh
--- a/tests/cli-tests.sh
+++ b/tests/cli-tests.sh
@@ -489,6 +489,12 @@
 }
 
 
+test_example_highlighter() {
+  do_zeolite -p "$ZEOLITE_PATH" $PARALLEL -r example/highlighter -f
+  do_zeolite -p "$ZEOLITE_PATH" -t example/highlighter
+}
+
+
 test_example_parser() {
   do_zeolite -p "$ZEOLITE_PATH" $PARALLEL -r example/parser -f
   do_zeolite -p "$ZEOLITE_PATH" -t example/parser
@@ -585,6 +591,7 @@
   test_check_defs
   test_custom_testcase
   test_example_hello
+  test_example_highlighter
   test_example_parser
   test_example_primes
   test_example_random
diff --git a/tests/modified-storage.0rt b/tests/modified-storage.0rt
--- a/tests/modified-storage.0rt
+++ b/tests/modified-storage.0rt
@@ -533,6 +533,16 @@
   \ Testing.checkEquals(value&.formatted(), "123")
 }
 
+unittest prefixEmpty {
+  optional Int value <- empty
+  \ Testing.checkEquals(`value&.formatted()&.readAt` 0, empty)
+}
+
+unittest prefixNonEmpty {
+  optional Int value <- 123
+  \ Testing.checkEquals(`value&.formatted()&.readAt` 0, '1')
+}
+
 unittest chainedCalls {
   optional Int value <- 123
   \ Testing.checkEquals(value&.formatted()&.readAt(0), '1')
diff --git a/zeolite-lang.cabal b/zeolite-lang.cabal
--- a/zeolite-lang.cabal
+++ b/zeolite-lang.cabal
@@ -1,7 +1,7 @@
 cabal-version:       2.2
 
 name:                zeolite-lang
-version:             0.24.0.0
+version:             0.24.0.1
 synopsis:            Zeolite is a statically-typed, general-purpose programming language.
 
 description:
@@ -97,6 +97,12 @@
                      example/random/.zeolite-module,
                      example/random/README.md,
                      example/random/*.0rx,
+                     example/highlighter/.zeolite-module,
+                     example/highlighter/README.md,
+                     example/highlighter/*.0rx,
+                     example/highlighter/src/*.0rp,
+                     example/highlighter/src/*.0rx,
+                     example/highlighter/test/*.0rt,
                      lib/container/README.md,
                      lib/container/.zeolite-module,
                      lib/container/*.0rp,
