Civet
A Programming Language for the New Millenium
Code More with Less in a TypeScript Superset
Civet is a programming language that compiles to TypeScript or JavaScript, so you can use existing tooling (including VSCode type checking, hints, completion, etc.) while enabling concise and powerful syntax. It starts with 99% JS/TS compatibility, making it easy to transition existing code bases. Then it adds many features and syntactic sugar, with some highlights below and more comprehensive examples in the reference. See also Civet's design philosophy.
Highlights: Beyond TC39
Civet code on the lefttop, compiled TypeScript output on the rightbottom.
Pattern Matching
TC39 Proposal: Pattern Matching
switch x
0
console.log("zero")
/^\s+$/
console.log("whitespace")
[{type: "text", content}, ...rest]
console.log("leading text", content)
if (x === 0) {
console.log("zero");
} else if (
typeof x === "string" &&
/^\s+$/.test(x)
) {
console.log("whitespace");
} else if (
Array.isArray(x) &&
x.length >= 1 &&
typeof x[0] === "object" &&
x[0] != null &&
"type" in x[0] &&
x[0].type === "text" &&
"content" in x[0]
) {
const [{ type, content }, ...rest] = x;
console.log("leading text", content);
}
Pipelines
data
|> Object.keys
|> console.log
console.log(Object.keys(data));
Fat pipes manipulate the same object repeatedly:
document.createElement('div')
||> .className = 'civet'
||> .appendChild document.createTextNode 'Civet'
let ref;
((ref = document.createElement("div")).className =
"civet"),
ref.appendChild(
document.createTextNode("Civet"),
),
ref;
Pipe expression with shorthand functions:
a |> & + 1 |> bar
bar(a + 1);
Single-Argument Function Shorthand
x.map .name
x.map &.profile?.name[0...3]
x.map &.callback a, b
x.map &+1
x.map -&
x.map [&, &.toUpperCase()]
x.map(($) => $.name);
x.map(($1) => $1.profile?.name.slice(0, 3));
x.map(($2) => $2.callback(a, b));
x.map(($3) => $3 + 1);
x.map(($4) => -$4);
x.map(($5) => [$5, $5.toUpperCase()]);
Custom Infix Operators
operator {min, max} := Math
value min ceiling max floor
const { min, max } = Math;
max(min(value, ceiling), floor);
Declarations in Conditions and Loops
if match := regex.exec string
console.log match
let ref;
if ((ref = regex.exec(string))) {
const match = ref;
console.log(match);
}
Everything is an Expression
items = for item of items
if item.length
item.toUpperCase()
else
"<empty>"
const results = [];
for (const item of items) {
if (item.length) {
results.push(item.toUpperCase());
} else {
results.push("<empty>");
}
}
items = results;
return
if x == null
throw "x is null"
else
log `received x of ${x}`
x.value()
return (() => {
if (x == null) {
throw "x is null";
} else {
log(`received x of ${x}`);
return x.value();
}
})();
const fs = import {readFile, writeFile} from "fs"
let ref;
const fs =
((ref = await import("fs")),
{
readFile: ref.readFile,
writeFile: ref.writeFile,
});
x = do
const tmp = f()
tmp * tmp + 1
let ref;
{
const tmp = f();
ref = tmp * tmp + 1;
}
x = ref;
Bulleted Lists and Unbraced Object Literals
projects:
. name: "Civet"
url: "https://civet.dev"
. name: "TypeScript"
url: "https://www.typescriptlang.org"
colorPairs:
. . "red"
. "#f00"
. . "green"
. "#0f0"
. . "blue"
. "#00f"
({
projects: [
{ name: "Civet", url: "https://civet.dev" },
{
name: "TypeScript",
url: "https://www.typescriptlang.org",
},
],
colorPairs: [
["red", "#f00"],
["green", "#0f0"],
["blue", "#00f"],
],
});
Dedented Strings and Templates
text = """
This text is a string that doesn't include
the leading whitespace.
"""
text = `This text is a string that doesn't include
the leading whitespace.`;
text = ```
Also works for
${templates}!
```
text = `Also works for
${templates}!`;
Chained Comparisons
a < b <= c
value > min?
a is b is not c
a instanceof b not instanceof c
a < b && b <= c;
min != null && value > min;
a === b && b !== c;
a instanceof b && !(b instanceof c);
Default to const
for Iteration Items
for (item of [1, 2, 3, 4, 5]) {
console.log(item * item);
}
for (const item of [1, 2, 3, 4, 5]) {
console.log(item * item);
}
Spread in Any Position
Spreads in first or middle position:
[...head, last] = [1, 2, 3, 4, 5]
([...head] = [1, 2, 3, 4, 5]),
([last] = head.splice(-1));
{a, ...rest, b} = {a: 7, b: 8, x: 0, y: 1}
({ a, b, ...rest } = { a: 7, b: 8, x: 0, y: 1 });
function justDoIt(a, ...args, cb) {
cb.apply(a, args)
}
function justDoIt(a, ...args) {
let [cb] = args.splice(-1);
return cb.apply(a, args);
}
Import Syntax Matches Destructuring
import {X: LocalX, Y: LocalY} from "./util"
import { X as LocalX, Y as LocalY } from "./util";
Export Convenience
export a, b, c from "./cool.js"
export x = 3
export { a, b, c } from "./cool.js";
export var x = 3;
JSX
function Listing(props)
<h1 #heading>Hello Civet!
<ul .items>
<For each=props.items>
(item) =>
<li .item {props.style}><Item {item}>
function Listing(props) {
return (
<>
<h1 id="heading">Hello Civet!</h1>
<ul class="items">
<For each={props.items}>
{(item) => {
return (
<li
class="item"
style={props.style}
>
<Item item={item} />
</li>
);
}}
</For>
</ul>
</>
);
}
Sponsors
Thank you to all of our sponsors for your invaluable support and contribution to the Civet language!
Contributors
Thank you for your work and dedication to the Civet project!