From 976ef29dbece798ed5a011bd0e2916bddbeb8db2 Mon Sep 17 00:00:00 2001 From: Rik Huijzer Date: Tue, 4 Feb 2025 17:18:29 +0100 Subject: [PATCH 1/5] Init and setup CI --- .github/workflows/ci.yml | 29 + .gitignore | 4 + README.md | 9 +- config.toml | 12 + content/_index.md | 53 + content/blog/_index.md | 6 + content/blog/basic-arnoldc.md | 272 ++++ content/blog/iterators.md | 131 ++ content/blog/operator-precedence.md | 22 + content/blog/operator_precedence/Cargo.toml | 9 + .../operator_precedence.rs | 261 ++++ content/blog/why-hard.md | 247 ++++ content/contributing.md | 30 + content/theme.html | 22 + static/font/roboto-latin.woff2 | Bin 0 -> 15744 bytes static/font/source-code-pro-latin.woff2 | Bin 0 -> 9960 bytes static/highlight/github-dark.min.css | 10 + static/highlight/github.min.css | 10 + static/highlight/highlight.min.js | 1213 +++++++++++++++++ static/style.css | 384 ++++++ templates/index.html | 60 + templates/page.html | 8 + templates/post.html | 8 + templates/posts.html | 19 + templates/section.html | 8 + templates/shortcodes/operator_precedence.html | 1 + 26 files changed, 2827 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/ci.yml create mode 100644 .gitignore create mode 100644 config.toml create mode 100644 content/_index.md create mode 100644 content/blog/_index.md create mode 100644 content/blog/basic-arnoldc.md create mode 100644 content/blog/iterators.md create mode 100644 content/blog/operator-precedence.md create mode 100644 content/blog/operator_precedence/Cargo.toml create mode 100644 content/blog/operator_precedence/operator_precedence.rs create mode 100644 content/blog/why-hard.md create mode 100644 content/contributing.md create mode 100644 content/theme.html create mode 100644 static/font/roboto-latin.woff2 create mode 100644 static/font/source-code-pro-latin.woff2 create mode 100644 static/highlight/github-dark.min.css create mode 100644 static/highlight/github.min.css create mode 100644 static/highlight/highlight.min.js create mode 100644 static/style.css create mode 100644 templates/index.html create mode 100644 templates/page.html create mode 100644 templates/post.html create mode 100644 templates/posts.html create mode 100644 templates/section.html create mode 100644 templates/shortcodes/operator_precedence.html diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..85e6957 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,29 @@ +name: CI + +on: + push: + branches: + - main + pull_request: + workflow_dispatch: + +jobs: + run: + runs-on: ubuntu-22.04 + timeout-minutes: 10 + + steps: + - uses: actions/checkout@v4 + + - uses: taiki-e/install-action@v2 + with: + tool: zola@0.19.2 + + - run: zola build + + - uses: cloudflare/wrangler-action@v3 + if: github.ref == 'refs/heads/main' + with: + accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} + command: pages deploy public --project-name=transformrs --branch=main diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8eb0280 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +public/ +**/tmp* +**/target/ +**/Cargo.lock diff --git a/README.md b/README.md index 04268c6..00185ef 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,9 @@ # transformrs.org -Website for the transformrs Rust crate + +Website for the transformrs Rust crate (crate source at ). + +## Development + +```bash +$ zola serve +``` \ No newline at end of file diff --git a/config.toml b/config.toml new file mode 100644 index 0000000..19f49a4 --- /dev/null +++ b/config.toml @@ -0,0 +1,12 @@ +title = "xrcf" +description = "eXtensible and Reusable Compiler Framework" +default_language = "en" +base_url = "https://xrcf.org" + +author = "xrcf project authors" +generate_sitemap = true +generate_robots_txt = false +minify_html = false + +# Probably need to remove _css from .gitignore if enabling. +generate_feeds = false \ No newline at end of file diff --git a/content/_index.md b/content/_index.md new file mode 100644 index 0000000..de1ced5 --- /dev/null +++ b/content/_index.md @@ -0,0 +1,53 @@ ++++ +title = "Home" ++++ + +
+
+

+ Aim: A modern library for building production-grade compilers +

+
+
+ +
+ Building compilers doesn't have to be rocket science. + This open-source library aims to be easy to build and easy to understand while, thanks to Rust, remaining performant, reliable, and productive. +
+ +
+

Status

+
+ +In the long term, the aim for xrcf is to allow building compilers that can compile any programming language to any target architecture. + +In the near term, the aim is to use xrcf to build a fully functional compiler that can compile the ArnoldC language to an executable. +To see the compiler in action, see the [walkthrough](/blog/basic-arnoldc). +ArnoldC is just a test case. +If xrcf can handle it well, then it will be useful for other compiler projects too. + +
+

Lowering to CPU

+
+ +In the table below, a checkmark ✅ means that at least one implementation exists which can lower the construct to code that can be executed on the CPU (via LLVM). + +Construct | MLIR | LLVM dialect | LLVM IR +--- | --- | --- | --- +functions | ✅ | ✅ | ✅ +add | ✅ | ✅ | ✅ +print | ✅ | ✅ | ✅ +if else | ✅ | ✅ | ✅ +for loop | | | +while loop | | | +... | | | + +For example, this table means that print can be lowered from MLIR to the LLVM dialect and then to LLVM IR. +This means that to get your own `print` operation to run on the CPU, you only need to convert your own `print` operation to MLIR. +From there, xrcf can be used to run your code on the CPU. + +
+

Lowering to GPU

+
+ +High on the priority list. diff --git a/content/blog/_index.md b/content/blog/_index.md new file mode 100644 index 0000000..4bb6877 --- /dev/null +++ b/content/blog/_index.md @@ -0,0 +1,6 @@ ++++ +title = "blog" +sort_by = "date" +template = "posts.html" +page_template = "post.html" ++++ diff --git a/content/blog/basic-arnoldc.md b/content/blog/basic-arnoldc.md new file mode 100644 index 0000000..19130b1 --- /dev/null +++ b/content/blog/basic-arnoldc.md @@ -0,0 +1,272 @@ ++++ +title = "A New Compiler for the Arnold Schwarzenegger Language" +date = 2024-12-02 +description = "As an example of how to use xrcf to write a compiler, there is now a basic ArnoldC compiler in the repository." ++++ + +
+ +This is a blog post about the eXtensible Reusable Compiler Framework (xrcf). +I've always wanted to write a compiler, but found the task daunting. +I didn't know how to handle all the details such as lexing, parsing, type checking, and error handling. +That's why I'm building xrcf. +This projects handles the details so you can focus on building your own compiler. + +
+ + +Since the release of version 0.4, there is now a basic ArnoldC compiler in the repository. +This ArnoldC compiler is a test case for the compiler framework. +If the framework can handle this language well, then it will be useful for other languages too. +The full code for the compiler can be found [here](https://github.com/rikhuijzer/xrcf/tree/v0.5.0/arnoldc). + +In this blog post, I will show how the compiler can be used to generate fast code for the CPU. +To follow along, you can either clone the repository and run: +```sh +$ cargo install --path arnoldc +``` +Or download the `arnoldc` binary from the [release page](https://github.com/rikhuijzer/xrcf/releases/tag/v0.5.0). + +The [ArnoldC language](https://github.com/lhartikk/ArnoldC) is based on one-liners from Arnold Schwarzenegger movies. +This is what a valid "Hello, World!" program looks like: + +```arnoldc +IT'S SHOWTIME +TALK TO THE HAND "Hello, World!\n" +YOU HAVE BEEN TERMINATED +``` + +Here, `IT'S SHOWTIME` means "begin main", `TALK TO THE HAND` means "print", and `YOU HAVE BEEN TERMINATED` means "end main". + +Before we use the compiler, let's see whether the installation was successful: + +```sh +$ arnoldc --help +``` + +This should print: + +```text +A compiler for the ArnoldC language + +Usage: arnoldc [OPTIONS] [INPUT] + +Arguments: + [INPUT] The input file (- is interpreted as stdin) [default: -] + +Options: + --convert-scf-to-cf Convert structured control flow (scf) operations to cf + --convert-cf-to-llvm Convert control flow (cf) operations to LLVM + --convert-experimental-to-mlir Convert experimental operations to MLIR + --convert-func-to-llvm Convert function operations to LLVM + --convert-mlir-to-llvmir Convert MLIR to LLVM IR + --print-ir-before-all Print the IR before each pass + --convert-arnold-to-mlir Convert ArnoldC operations to MLIR + --compile Compile the code + --debug Print debug information + -h, --help Print help + -V, --version Print version +``` + +To compile ArnoldC, let's create a file called `hello.arnoldc` with the hello world program: + +```arnoldc +IT'S SHOWTIME +TALK TO THE HAND "Hello, World!\n" +YOU HAVE BEEN TERMINATED +``` + +Next, let's see what the compiler generates when we run the `--convert-arnold-to-mlir` pass: + +```sh +$ arnoldc --convert-arnold-to-mlir hello.arnoldc +``` + +This prints: + +```mlir +module { + func.func @main() -> i32 { + experimental.printf("Hello, World!\0A") + %0 = arith.constant 0 : i32 + return %0 : i32 + } +} +``` + +What this shows is that the compiler has converted the ArnoldC code to [MLIR](https://mlir.llvm.org/). +It also added a 0 return value to the `main` function. +This ensures that the program will return a 0 status code, which is the convention for programs that didn't crash. + +Although this MLIR code looks nice (or at least more so than ArnoldC), let's get it to run. +To do so, convert the MLIR code to LLVM IR by running all the required passes in order: + +```sh +$ arnoldc \ + --convert-arnold-to-mlir \ + --convert-experimental-to-mlir \ + --convert-scf-to-cf \ + --convert-cf-to-llvm \ + --convert-func-to-llvm \ + --convert-mlir-to-llvmir \ + hello.arnoldc +``` + +Think of each of these passes as a set of transformations. +For example, the `--convert-arnold-to-mlir` pass transforms: +```arnoldc +TALK TO THE HAND "Hello, World!\n" +``` +to +```mlir +experimental.printf("Hello, World!\0A") +``` + +The command with all passes applied prints the following LLVM IR: + +```llvm +; ModuleID = 'LLVMDialectModule' +source_filename = "LLVMDialectModule" + +declare i32 @printf(ptr) +define i32 @main() { + %3 = alloca i8, i16 15, align 1 + store [15 x i8] c"Hello, World!\0A\00", ptr %3, align 1 + %4 = call i32 @printf(ptr %3) + ret i32 0 +} + +!llvm.module.flags = !{!0} + +!0 = !{i32 2, !"Debug Info Version", i32 3} +``` + +Remembering these passes and in the order in which to run them is cumbersome, so it is also possible to use the `compile` flag, which is a wrapper around the above command and produces the same result: + +```sh +$ arnoldc --compile hello.arnoldc +``` + +To run our compiled code, we can use the LLVM interpreter via the `lli` command. +`lli` executes programs written in the LLVM bitcode format. +This executable is part of the LLVM project, so it can usually be installed via the package manager. +For example, on MacOS, `brew install llvm`. + +Enough talk, let's run the code! + +```sh +$ arnoldc --compile hello.arnoldc | lli +Hello, World! +``` + +Or produce a native executable: + +```sh +$ arnoldc --compile hello.arnoldc | llc -filetype=obj -o hello.o +$ clang hello.o -o hello +$ ./hello +Hello, World! +``` + +Although the compiler is still far from complete (see [status](/#status) for details), there is one more thing we can do. +We can print a variable: + +```arnoldc +IT'S SHOWTIME + +HEY CHRISTMAS TREE x +YOU SET US UP @NO PROBLEMO + +TALK TO THE HAND "x: " +TALK TO THE HAND x + +YOU HAVE BEEN TERMINATED +``` +This should print: + +```text +x: 1 +``` +because `HEY CHRISTMAS TREE x` is equivalent to what in Python would be `x =` and `@NO PROBLEMO` in ArnoldC is equivalent to the boolean `True`. + +Let's see what the compiler generates. +To get readable code, we do only the `--convert-arnold-to-mlir` pass: + +```sh +$ arnoldc --convert-arnold-to-mlir print.arnoldc +``` + +This prints: + +```mlir +module { + func.func @main() -> i32 { + %x = arith.constant 1 : i16 + experimental.printf("x: ") + experimental.printf("%d", %x) + %0 = arith.constant 0 : i32 + return %0 : i32 + } +} +``` + +Which returns the expected value: + +```sh +$ arnoldc --compile print.arnoldc | lli +x: 1 +``` + +As a final example, let's see what happens if we write invalid code: + +```arnoldc +IT'S SHOWTIME +TALK "Hello, World!" +YOU HAVE BEEN TERMINATED +``` + +If we now run the compiler, it will fail with a clear error message: + +```sh +$ arnoldc --compile invalid.arnoldc +thread 'main' panicked at arnoldc/src/main.rs:67:60: +called `Result::unwrap()` on an `Err` value: + +--- +0 | ITS SHOWTIME { +1 | TALK "Hello, World!\n" + ^ Unknown operation: TALK +--- +``` + +As is expected when building a compiler, the error message point to the exact location of the error with a description of the problem. + +This concludes the walkthrough, or as Arnold would say: + +```text +YOU HAVE BEEN TERMINATED +``` + +## Next Steps + +To learn how to build your own compiler, see the files inside the [`arnoldc` directory](https://github.com/rikhuijzer/xrcf/tree/v0.5.0/arnoldc). +It is split into three parts: + +1. `src/main.rs` defines the command line interface. +1. `src/arnold.rs` specifies how to parse the ArnoldC code (convert the text to data structures). +1. `src/arnold_to_mlir.rs` contains the `--convert-arnold-to-mlir` pass, which converts the ArnoldC code to MLIR. + +All other passes such as `--convert-func-to-llvm` are implemented in the `xrcf` crate. + +If you want to contribute to the compiler framework, see [contributing](/contributing). + +Although the compiler framework is not yet feature complete, if you want to build your own compiler, here are some modern compiler projects that could serve as inspiration: + +- [jax](https://github.com/jax-ml/jax): A Python library for accelerator-oriented computing +- [tvm](https://tvm.apache.org/): A end to end machine learning compiler framework for CPUs, GPUs, and accelerators. +- [torch-mlir](https://github.com/llvm/torch-mlir): Compiles PyTorch to MLIR. +- [Flang](https://flang.llvm.org/docs/): A LLVM-based Fortran compiler. + +Or you could build a compiler for a different movie star. +Or your favorite tensor processing unit. +It's up to you. diff --git a/content/blog/iterators.md b/content/blog/iterators.md new file mode 100644 index 0000000..c92a063 --- /dev/null +++ b/content/blog/iterators.md @@ -0,0 +1,131 @@ ++++ +title = "Why are Iterators so Common in Rust?" +date = 2024-12-20 +description = "In some languages, iterators are nice but people tell you to not overuse them. In Rust, iterators are everywhere. Why is that?" ++++ + +Iterators are a pretty cool concept. +For example, in Python, you can write: + +```python +values = [1, 2, 3] + +for i in range(len(values)): + values[i] += 1 + +print(values) +``` + +This prints +``` +[2, 3, 4] +``` + +With iterators, this can be written as: + +```python +values = [1, 2, 3] + +values = list(map(lambda x: x + 1, values)) + +print(values) +``` + +This is of course pretty cool. + +So a few years ago when I was programming in Python, I obviously went overboard with this and used iterators everywhere. +However, a more senior developer told me to not overuse them. +He said that iterators are nice, but often people find it easier to read loops. +And since code is read more than it is written, writers should just accept the inconvenience of loops. + +In Julia, iterators are also pretty common, but should also generally not be overused. +I don't know what the current state is, but last year the main reason in Julia was that iterators were much more difficult for the compiler to optimize. +It could do it, but if you wanted fast compilation times, you should just use loops so that's what I did. + +However, in Rust, iterators are everywhere and I didn't get why. +For example, this code from [`wasmtime`](https://github.com/bytecodealliance/wasmtime) looks like pretty standard Rust: + +```rust +let stdin_exist = options + .files + .iter() + .find(|file| *file == Path::new("-")) + .is_some(); +``` + +But why? +I wouldn't say that this code is particularly easy to read for most people. +I'm not saying that this is bad, but just that it will take most people a bit of time to get used to using iterators like this. + +So after a bit of searching online, I found three performance arguments that people are making. +One argument is that iterators can be optimized better by the compiler. +A hand-wavy argument for this is that since the compiler is responsible for a large part of the code, it knows more about the guarantees and thus can remove more bounds checks. + +Another argument is that iterators are a zero-cost abstraction anyway. +Some blog posts even show that iterators often produce exactly the same LLVM IR as loops. +I guess this depends a bit on the situation, but it does sound promising. + +The third performance argument is that iterators are lazy. +So while you can also decide to make a loop lazy by pre-emptively breaking out of it, iterators are a bit more convenient. + +But today a reason hit me that I think is not often mentioned. +Iterators are a workaround for when Rust frees variables too early. +Let me show an example. + +```rust +fn main() { + let text = String::from("Hello, world!").split(", "); + + for word in text { + println!("{}", word); + } +} +``` + +If you try to compile this code (`rustc tmp.rs`), you will get an error. +It says that `String::from("Hello, world!")` "creates a temporary value which is freed while still in use". + +One way to fix this is to make a separate variable: + +```rust +fn main() { + let text = String::from("Hello, world!"); + let text = text.split(", "); + + for word in text { + println!("{}", word); + } +} +``` + +It took me way too long to understand why this is happening. +The problem here is that `String::from("Hello, world!")` returns a `String` and `split` returns a `&str`. +This means that `split` does not create a new object, but instead it returns a reference to the original object. +This is very efficient, but problematic for us here for another fact about the compiler: +Rust frees objects which are not referenced at the end of the line! + +So what I mean here is that `String::from("Hello, world!")` is dropped before the loop starts. +This is why the compiler gives an error. +`split` gave us a reference to the original object, but the original object is dropped before we use the reference. +If we write a separate variable, the compiler does decide to keep the original object alive until the end of the function. + +So here is where iterators come in. +With iterators, we can just chain everything to keep it all one one line: + +```rust +fn main() { + String::from("Hello, world!").split(", ").for_each(|word| { + println!("{}", word); + }); +} +``` + +which prints + +``` +Hello +world! +``` + +This, I think, is why iterators are everywhere in Rust. +But maybe I'm wrong, let me know if you disagree. diff --git a/content/blog/operator-precedence.md b/content/blog/operator-precedence.md new file mode 100644 index 0000000..dcfb781 --- /dev/null +++ b/content/blog/operator-precedence.md @@ -0,0 +1,22 @@ ++++ +title = "Better Operator Precedence in Rust" +date = 2025-01-14 +description = "A Rust rewrite of Jamie Bandon's Pratt parser with operator comparisons" ++++ + +Jamie Brandon wrote [an example parser](https://www.scattered-thoughts.net/writing/better-operator-precedence/) that fixes some problems that Pratt parsers have (see also [matklad](https://matklad.github.io/2020/04/13/simple-but-powerful-pratt-parsing) for a very nice writeup on Pratt parsers). +The idea in Jamie's version is to compare operators directly. + +The example by Jamie was written in Zig. +However, I'm trying to embed it inside xrcf, which is written in Rust. +Having to translate both the Zig language and the algorithm at the same time is a bit too confusing for me. +So here is Jamie's algorithm rewritten in Rust: + +```rust +{{ operator_precedence() }} +``` + +These tests pass with Rust 1.84.0. + +A full cargo project is available at +. diff --git a/content/blog/operator_precedence/Cargo.toml b/content/blog/operator_precedence/Cargo.toml new file mode 100644 index 0000000..3851112 --- /dev/null +++ b/content/blog/operator_precedence/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "operator_precedence" +version = "0.1.0" +edition = "2021" + +[[test]] +name = "my_test" +path = "operator_precedence.rs" + diff --git a/content/blog/operator_precedence/operator_precedence.rs b/content/blog/operator_precedence/operator_precedence.rs new file mode 100644 index 0000000..17b4104 --- /dev/null +++ b/content/blog/operator_precedence/operator_precedence.rs @@ -0,0 +1,261 @@ +#![allow(dead_code)] + +#[derive(Clone, Debug, PartialEq)] +enum Operator { + Add, + Multiply, + BitwiseOr, +} + +#[derive(Clone, Debug, PartialEq)] +enum Precedence { + LeftBindsTighter, + RightBindsTighter, + Ambiguous, +} + +use Precedence::*; + +fn compare_precedence(left: &Operator, right: &Operator) -> Precedence { + match left { + Operator::Add => match right { + Operator::Add => return LeftBindsTighter, + Operator::Multiply => return RightBindsTighter, + Operator::BitwiseOr => return Ambiguous, + }, + Operator::Multiply => match right { + Operator::Add => return LeftBindsTighter, + Operator::Multiply => return LeftBindsTighter, + Operator::BitwiseOr => return Ambiguous, + }, + Operator::BitwiseOr => match right { + Operator::Add => return Ambiguous, + Operator::Multiply => return Ambiguous, + Operator::BitwiseOr => return RightBindsTighter, + }, + } +} + +#[cfg(test)] +mod test_precedence { + use super::*; + + #[test] + fn test_operator_precedence() { + let ops = vec![Operator::Add, Operator::Multiply, Operator::BitwiseOr]; + for a in ops.clone() { + for b in ops.clone() { + let ab = compare_precedence(&a, &b); + let ba = compare_precedence(&b, &a); + + if ab == Ambiguous { + assert_eq!(ba, Ambiguous); + } + if a != b && ab == LeftBindsTighter { + assert_eq!(ba, RightBindsTighter); + } + if a != b && ab == RightBindsTighter { + assert_eq!(ba, LeftBindsTighter); + } + + for c in ops.clone() { + let bc = compare_precedence(&b, &c); + let ac = compare_precedence(&a, &c); + + // transitive + if ab == LeftBindsTighter && bc == LeftBindsTighter { + assert_eq!(ac, LeftBindsTighter); + } + if ab == RightBindsTighter && bc == RightBindsTighter { + assert_eq!(ac, RightBindsTighter); + } + } + } + } + } +} + +#[derive(Clone, Debug, PartialEq)] +enum Token { + Number, + Add, + Multiply, + BitwiseOr, + OpenParen, + CloseParen, +} + +#[derive(Clone, Debug, PartialEq)] +struct BinaryOp { + op: Operator, + left: Box, + right: Box, +} + +#[derive(Clone, Debug)] +enum Expr { + Number, + BinaryOp(BinaryOp), +} + +impl PartialEq for Expr { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (Expr::Number, Expr::Number) => true, + (Expr::BinaryOp(a), Expr::BinaryOp(b)) => a == b, + _ => false, + } + } +} + +#[derive(Clone, Debug)] +struct Parser { + tokens: Vec, + position: usize, +} + +impl Parser { + fn new(tokens: &[Token]) -> Self { + Self { + tokens: tokens.to_vec(), + position: 0, + } + } + fn next_token(&mut self) -> Option { + if self.position >= self.tokens.len() { + return None; + } + let token = self.tokens[self.position].clone(); + self.position += 1; + Some(token) + } + fn parse_expr_inner(&mut self) -> Result { + let token = self.next_token().expect("Expected start of expression"); + match token { + Token::Number => { + return Ok(Expr::Number); + } + Token::OpenParen => { + let expr = self.parse_expr_outer(None)?; + let token = self.next_token().expect("Expected close paren"); + if token != Token::CloseParen { + return Err("Expected close paren".to_string()); + } + return Ok(expr); + } + _ => { + return Err(format!("Expected number or open paren, got {:?}", token)); + } + } + } + fn parse_expr_outer(&mut self, prev_op_o: Option) -> Result { + let mut left = self.parse_expr_inner()?; + loop { + let start = self.position; + let token = match self.next_token() { + Some(token) => token, + None => return Ok(left), + }; + let op = match token { + Token::Add => Operator::Add, + Token::Multiply => Operator::Multiply, + Token::BitwiseOr => Operator::BitwiseOr, + _ => { + self.position = start; + return Ok(left); + } + }; + let precedence = if let Some(ref prev_op) = prev_op_o { + compare_precedence(&prev_op, &op) + } else { + RightBindsTighter + }; + match precedence { + LeftBindsTighter => { + self.position = start; + return Ok(left); + } + RightBindsTighter => { + let right = self.parse_expr_outer(Some(op.clone()))?; + let new_left = Expr::BinaryOp(BinaryOp { + op, + left: Box::new(left), + right: Box::new(right), + }); + left = new_left; + } + Ambiguous => return Err("Ambiguous operator precedence".to_string()), + } + } + } + fn parse(tokens: &[Token]) -> Result { + let mut parser = Parser::new(tokens); + let expr = parser.parse_expr_outer(None)?; + if parser.position != parser.tokens.len() { + return Err("Expected end of expression".to_string()); + }; + Ok(expr) + } +} + +#[cfg(test)] +mod test_parser { + use super::Token::*; + use super::*; + + #[test] + fn test_multiply_precedence_over_add() { + assert_eq!( + Parser::parse(&vec![Number, Add, Number, Multiply, Number]), + Parser::parse(&vec![ + Number, Add, OpenParen, Number, Multiply, Number, CloseParen + ]) + ); + } + #[test] + fn test_parens_override_precedence() { + assert_eq!( + Parser::parse(&vec![ + OpenParen, Number, Add, Number, CloseParen, Multiply, Number + ]), + Ok(Expr::BinaryOp(BinaryOp { + op: Operator::Multiply, + left: Box::new(Expr::BinaryOp(BinaryOp { + op: Operator::Add, + left: Box::new(Expr::Number), + right: Box::new(Expr::Number), + })), + right: Box::new(Expr::Number), + })) + ); + } + #[test] + fn test_ambiguous_precedence_against_bitwise_or() { + assert_eq!( + Parser::parse(&vec![Number, Add, Number, BitwiseOr, Number]), + Err("Ambiguous operator precedence".to_string()) + ); + assert_eq!( + Parser::parse(&vec![Number, Multiply, Number, BitwiseOr, Number]), + Err("Ambiguous operator precedence".to_string()) + ); + } + #[test] + fn test_left_associative() { + assert_eq!( + Parser::parse(&vec![Number, Add, Number, Add, Number]), + Parser::parse(&vec![ + OpenParen, Number, Add, Number, CloseParen, Add, Number + ]) + ); + } + #[test] + fn test_right_associative() { + assert_eq!( + Parser::parse(&vec![Number, BitwiseOr, Number, BitwiseOr, Number]), + Parser::parse(&vec![ + Number, BitwiseOr, OpenParen, Number, BitwiseOr, Number, CloseParen + ]) + ); + } +} diff --git a/content/blog/why-hard.md b/content/blog/why-hard.md new file mode 100644 index 0000000..6080b2d --- /dev/null +++ b/content/blog/why-hard.md @@ -0,0 +1,247 @@ ++++ +title = "Why is Building a Compiler so Hard?" +date = 2024-12-21 +description = "Thoughts on why writing a compiler is surprisingly difficult." ++++ + +Last year, I've spent a few months experimenting with and contributing to various compilers. +I had great fun, but felt that the developer experience could be better. +The build systems were often hard-to-use, and the tooling was often complex enough for things like "jump to definition" to not work. +So that's why I started to write a new compiler framework a few months ago. +And to be clear, I'm not saying that the other frameworks are bad or that I am an expert compiler developer. +Sometimes it's just about saying "how hard can it be?" + +Now that I'm a few months into the project, I often find myself being surprised by how hard it actually is. +But it shouldn't be, right? +There are no side-effects like databases, file IO, or network requests. +You just read the files containing the source code, do some processing, and print the compiled code. +Everything happens inside memory. +Also, there are many great open source projects out there that I'm basing my code on. +So it should be easy. + +However, when working on a new feature or bug fix, I often find myself adding a test case and then having to think a few days about the problem before I feel like I have a good solution. +Next, implementing it is often like wading through mud. +I expect that this will become better with time because my brain will get used to it, but currently it's surprisingly hard. + +That's why I want to write down my thoughts now that I still have "fresh eyes". +This could be useful for myself to understand where the difficulties are so that I can improve the framework. +And maybe it will be interesting for others too. +So let's dive in. + +Remember that I said there are no side-effects? + +## Side-Effects Everywhere + +There are plenty of side-effects inside a compiler. +For example, take the following Python code: + +```python +def add_one(x): + y = 1 + return x + y +``` + +Now a good compiler would look at this code and rewrite it to: + +```python +def add_one(x): + return x + 1 +``` + +since this would avoid one addition operation. +But which steps would the compiler take to rewrite this code? +Assuming that we already parsed the code into some data structure, the steps would be something like: + +1. Look at `x + y`. +1. Find the definition of `y`. +1. Notice that `y` is a constant and thus that `y` in `x + y` can be replaced by `1`. +1. Replace `y` with in `x + y` by `1`. +1. Remove `y = 1` if nobody else is using `y`. + +Now there are two places where side-effects occur. +One is in step 2, where we find the definition of `y`. +Only if `y` is a constant, we can substitute the `y` with a constant. +Otherwise, we abort the rewrite. + +The other is in step 4, where we remove `y = 1`. +This can only happen if `y` is a variable that is no longer used. +If `y` is used somewhere else, we cannot remove the assignment. + +## Pointers Everywhere + +Another source of complexity is that we need pointers to navigate the data structure. +To see why, let's look at the data structure. + +When parsing this code, we create a data structure that looks something like this: + +```yaml +├── function: add_one(x) + ├── assignment: y = 1 + └── return: x + y +``` + +So we have some object that contains `add_one` with two children: `y = 1` and `return x + y`. +Next, we rewrite this code to this: + +```yaml +├── function: add_one(x) + └── return: x + 1 +``` + +Now when we are in step 1 of the rewrite listed above, we need to find the definition of `y`. +This means that we need to find the parent of `return x + y` in the data structure since only the parent knows about the sibling `y = 1`[^1]. +Hence, we need a pointer inside the `return x + y` object that points to its parent. +Or we need a pointer inside the `y` object that points to the definition of `y`. +In both cases, this pointer has to be set when creating the data structure and then updated during rewriting. +It's all not impossible, but it does add complexity. + +## Mutability Everywhere + +Related to the side-effects and pointers, we need to accept mutability to make the compiler fast. +Mutability makes the data structure harder to understand, because it's less clear what state the object is in at some point in time. +To explain why we need mutability, consider the data structure again but now for a function with 100 lines of code. +This would mean a data structure with 100 nodes. + +Then the rewrite shown above would make changes to only a few of these nodes. +Thus, if we would make everything immutable, we would need to copy over almost all the nodes. +This would be very inefficient. + +Relatedly, we need mutability in order to be able to set the pointer to the parent. +For example, when parsing the code, at some point we have parsed the function definition for `add_one` and start parsing the assignment `y = 1`: + +```yaml +├── function: add_one(x) + ├── assignment: y = 1 + ^ Imagine the parser is here. +``` + +And now we want to set the parent of the `y = 1` object to be the `add_one` object. +However, we weren't able to construct the `add_one` object yet since we are still parsing the children `y = 1` and `return x + y`. +So we have to create the object for `add_one` first without setting its children. +Then, we pass this unfinished `add_one` object to the parser of the children and set it as the parent. + +Once the parser is done parsing the children: + +```yaml +├── function: add_one(x) + ├── assignment: y = 1 + └── return: x + y + ^ Imagine the parser is here. +``` + +We can finally complete the `add_one` object by setting the children. + +## Imperative Code + +Next to the side-effects, mutability, and pointers, there is also the question of how to describe the rewrite rules. +Let's consider again this rewrite: + +```yaml +├── function: add_one(x) + ├── assignment: y = 1 + └── return: x + y +``` + +to this: + +```yaml +├── function: add_one(x) + └── return: x + 1 +``` + +Now how would we describe this rewrite in code? +Preferably, we would do this in a declarative way. +MLIR uses a pattern language to describe these kinds of rewrites. +For example, the rewrite for `addi(addi(x, c0), c1)` to `addi(x, c0 + c1)` is described as +[follows]( +https://github.com/llvm/llvm-project/blob/6c062afc2e6ed4329e1e14cb011913195a5356fa/mlir/lib/Dialect/Arith/IR/ArithCanonicalization.td#L42-L48): + +```cpp +def AddIAddConstant : + Pat<(Arith_AddIOp:$res + (Arith_AddIOp $x, (ConstantLikeMatcher APIntAttr:$c0), $ovf1), + (ConstantLikeMatcher APIntAttr:$c1), $ovf2), + (Arith_AddIOp $x, (Arith_ConstantOp (AddIntAttrs $res, $c0, $c1)), + (MergeOverflow $ovf1, $ovf2))>; +``` + +So then I guess our `add_one` rewrite would be something like this: + +```cpp +def AddOne : + Pat<(Add $x, (ConstantLikeMatcher APIntAttr:$c1)), + (Add $x, (Arith_ConstantOp (AddIntAttrs $res, $c0, $c1))>; +``` + +Now my usual problem with declarative code is that it works great until it doesn't. +There are always cases that cannot be expressed in declarative code. +And then you are left with handling the most complex cases in imperative code, while you have done all the easy cases in declarative code. +Also the codebase is then a mix of declarative and imperative code. +Maybe that's still better than having to do everything imperative. +I'm not sure yet. +It does appear complex, that's all I'm saying. + +The alternative is to write imperative code. +For example, we could write a rewrite rule that looks like this: + +```python +def rewrite(op: Add): + if op.rhs.definition is not None and if op.rhs.definition.op == Constant: + new_rhs = Arith_ConstantOp(op.rhs.definition.value) + op.rhs.replace(new_rhs) +``` + +This somehow is to me also not that easy to understand. +It's different code than for example: + +```python +def count_vowels(s): + vowels = "aeiou" + count = 0 + for char in s: + if char.lower() in vowels: + count += 1 + return count +``` + +Although `count_vowels` is longer than the rewrite above, I think it's much easier to understand. +I'm not sure what the reason is for this. +Maybe it's because of the pointers such as `op.rhs.definition`, all the non-standard data types such as `Arith_ConstantOp`, or because the operations (like `replace`) mutate things in the background? +Maybe it's just because my brain is not used to it yet. + +## Other Difficult Parts + +Other people have also noticed that certain parts are complex. +For example, A famous [post by ShipReq](http://web.archive.org/web/20210121042722/https://blog.shipreq.com/post/compilers_are_hard) observed that you need mutability for performance, which complicates things, and the (too) many combinations that are possible. +Furthermore, [Basile Starynkevitch on StackOverflow](https://softwareengineering.stackexchange.com/a/273711/324697) argues that the main difficulty is in implementing all the many optimizations which are necessary to make a competitive compiler. +Especially the middle-end optimizations (so after parsing and before going into target-specific optimizations). +Similarly, [m-in, SeatedInAnOffice, chickyban on Reddit](https://www.reddit.com/r/Compilers/comments/1hjkb49/why_is_building_a_compiler_so_hard) say that building a compiler is not necessarily the hard part, but build a production-grade compiler for real users with a messy language spec is. + +## A More Positive Note + +So why is writing a compiler so hard? +Currently, I think it's because of the side-effects, mutability, and pointers. + +To end on a more positive note, I want to mention that I think there are also some enjoyable aspects of writing a compiler. +One is that when you look at the compiler as a whole, it is deterministic and without side-effects. +Put differently, given the same input, the compiler will always produce the same output. +At the same time, the inputs and outputs are all in textual form. +This makes reproducing bugs and writing tests very easy. +This is in contrast to testing graphical user interfaces, where most of the tests cannot be (fully) automated. + +Another enjoyable aspect is that the problems are hard but fair. +If the program crashes, it's probably your fault. +You cannot blame other software, the internet provider, operating system, or the hard disk. + +So that's why I'll keep working on this framework. +It's hard, but fun. +Hopefully the biggest sources of complexity can be moved into the framework, so that other people don't have to deal with them. +Now if you after reading this became less interested in compilers, then that's fine. +If after reading this you became more interested, feel free to check out the [project on GitHub](https://github.com/rikhuijzer/xrcf). +Contributions as well as complaints are welcome! + +Okay now that this is written up, time for me to go back to writing code. + +[^1]: You could also decide to have each object know about its direct siblings, but then you would need to update the pointers when the siblings change. +When printing the data structure, you anyway need to know the children because you start printing at the root and then print recursively. +So that's why, currently, xrcf only has a pointer from the child to the parent and from the parent to the children. diff --git a/content/contributing.md b/content/contributing.md new file mode 100644 index 0000000..7cfce15 --- /dev/null +++ b/content/contributing.md @@ -0,0 +1,30 @@ ++++ +title = "Contributing" +page_template = "page.html" ++++ + +Everyone is welcome to contribute to transformrs. + +- Bugs can be reported via GitHub issues at [rikhuijzer/transformrs](https://github.com/rikhuijzer/transformrs/issues). +- Changes to the source code or documentation can be sent via GitHub pull requests to [rikhuijzer/transformrs](https://github.com/rikhuijzer/transformrs/pulls). +- Changes to this website can be sent via GitHub pull requests to [rikhuijzer/transformrs.org](https://github.com/rikhuijzer/transformrs.org/pulls). + +
+ +## Contributing to the Source Code + +As stated above, patches can be sent via GitHub PRs to [rikhuijzer/transformrs](https://github.com/rikhuijzer/transformrs/pulls). + +To develop locally, you can clone the repository and run + +```sh +$ cargo test +``` + +or if you have [`cargo-watch`](https://github.com/watchexec/cargo-watch) installed, you can run + +```sh +$ cargo watch -x test +``` + +to automatically run the tests when you make changes. diff --git a/content/theme.html b/content/theme.html new file mode 100644 index 0000000..c3169e8 --- /dev/null +++ b/content/theme.html @@ -0,0 +1,22 @@ +{% extends "themes.html" %} + +{% block title %}{{ page.title }} | Zola {% endblock title %} +{% block theme_content %} +
+ + +
+ +
+ {{page.content | safe}} +{% endblock theme_content %} \ No newline at end of file diff --git a/static/font/roboto-latin.woff2 b/static/font/roboto-latin.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..020729ef8d353ff843438008300bedee1f519380 GIT binary patch literal 15744 zcmV-`J%7S?Pew8T0RR9106l;J5&!@I0FHD306iK20RR9100000000000000000000 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 y{^U~631l%Viyw0%mK9y;eEd5A_pK02OCvA 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 mB~LX!Zf9Q613Dez*Md6P4CCdjC5J!+AlX~_e6{ +throw Error("map is read-only")}:n instanceof Set&&(n.add=n.clear=n.delete=()=>{ +throw Error("set is read-only") +}),Object.freeze(n),Object.getOwnPropertyNames(n).forEach((t=>{ +const a=n[t],i=typeof a;"object"!==i&&"function"!==i||Object.isFrozen(a)||e(a) +})),n}class n{constructor(e){ +void 0===e.data&&(e.data={}),this.data=e.data,this.isMatchIgnored=!1} +ignoreMatch(){this.isMatchIgnored=!0}}function t(e){ +return e.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'") +}function a(e,...n){const t=Object.create(null);for(const n in e)t[n]=e[n] +;return n.forEach((e=>{for(const n in e)t[n]=e[n]})),t}const i=e=>!!e.scope +;class r{constructor(e,n){ +this.buffer="",this.classPrefix=n.classPrefix,e.walk(this)}addText(e){ +this.buffer+=t(e)}openNode(e){if(!i(e))return;const n=((e,{prefix:n})=>{ +if(e.startsWith("language:"))return e.replace("language:","language-") +;if(e.includes(".")){const t=e.split(".") +;return[`${n}${t.shift()}`,...t.map(((e,n)=>`${e}${"_".repeat(n+1)}`))].join(" ") +}return`${n}${e}`})(e.scope,{prefix:this.classPrefix});this.span(n)} +closeNode(e){i(e)&&(this.buffer+="")}value(){return this.buffer}span(e){ +this.buffer+=``}}const s=(e={})=>{const n={children:[]} +;return Object.assign(n,e),n};class o{constructor(){ +this.rootNode=s(),this.stack=[this.rootNode]}get top(){ +return this.stack[this.stack.length-1]}get root(){return this.rootNode}add(e){ +this.top.children.push(e)}openNode(e){const n=s({scope:e}) +;this.add(n),this.stack.push(n)}closeNode(){ +if(this.stack.length>1)return this.stack.pop()}closeAllNodes(){ +for(;this.closeNode(););}toJSON(){return JSON.stringify(this.rootNode,null,4)} +walk(e){return this.constructor._walk(e,this.rootNode)}static _walk(e,n){ +return"string"==typeof n?e.addText(n):n.children&&(e.openNode(n), +n.children.forEach((n=>this._walk(e,n))),e.closeNode(n)),e}static _collapse(e){ +"string"!=typeof e&&e.children&&(e.children.every((e=>"string"==typeof e))?e.children=[e.children.join("")]:e.children.forEach((e=>{ +o._collapse(e)})))}}class l extends o{constructor(e){super(),this.options=e} +addText(e){""!==e&&this.add(e)}startScope(e){this.openNode(e)}endScope(){ +this.closeNode()}__addSublanguage(e,n){const t=e.root +;n&&(t.scope="language:"+n),this.add(t)}toHTML(){ +return new r(this,this.options).value()}finalize(){ +return this.closeAllNodes(),!0}}function c(e){ +return e?"string"==typeof e?e:e.source:null}function d(e){return b("(?=",e,")")} +function g(e){return b("(?:",e,")*")}function u(e){return b("(?:",e,")?")} +function b(...e){return e.map((e=>c(e))).join("")}function m(...e){const n=(e=>{ +const n=e[e.length-1] +;return"object"==typeof n&&n.constructor===Object?(e.splice(e.length-1,1),n):{} +})(e);return"("+(n.capture?"":"?:")+e.map((e=>c(e))).join("|")+")"} +function p(e){return RegExp(e.toString()+"|").exec("").length-1} +const _=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./ +;function h(e,{joinWith:n}){let t=0;return e.map((e=>{t+=1;const n=t +;let a=c(e),i="";for(;a.length>0;){const e=_.exec(a);if(!e){i+=a;break} +i+=a.substring(0,e.index), +a=a.substring(e.index+e[0].length),"\\"===e[0][0]&&e[1]?i+="\\"+(Number(e[1])+n):(i+=e[0], +"("===e[0]&&t++)}return i})).map((e=>`(${e})`)).join(n)} +const f="[a-zA-Z]\\w*",E="[a-zA-Z_]\\w*",y="\\b\\d+(\\.\\d+)?",N="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",w="\\b(0b[01]+)",v={ +begin:"\\\\[\\s\\S]",relevance:0},O={scope:"string",begin:"'",end:"'", +illegal:"\\n",contains:[v]},k={scope:"string",begin:'"',end:'"',illegal:"\\n", +contains:[v]},x=(e,n,t={})=>{const i=a({scope:"comment",begin:e,end:n, +contains:[]},t);i.contains.push({scope:"doctag", +begin:"[ ]*(?=(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):)", +end:/(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):/,excludeBegin:!0,relevance:0}) +;const r=m("I","a","is","so","us","to","at","if","in","it","on",/[A-Za-z]+['](d|ve|re|ll|t|s|n)/,/[A-Za-z]+[-][a-z]+/,/[A-Za-z][a-z]{2,}/) +;return i.contains.push({begin:b(/[ ]+/,"(",r,/[.]?[:]?([.][ ]|[ ])/,"){3}")}),i +},M=x("//","$"),S=x("/\\*","\\*/"),A=x("#","$");var C=Object.freeze({ +__proto__:null,APOS_STRING_MODE:O,BACKSLASH_ESCAPE:v,BINARY_NUMBER_MODE:{ +scope:"number",begin:w,relevance:0},BINARY_NUMBER_RE:w,COMMENT:x, +C_BLOCK_COMMENT_MODE:S,C_LINE_COMMENT_MODE:M,C_NUMBER_MODE:{scope:"number", +begin:N,relevance:0},C_NUMBER_RE:N,END_SAME_AS_BEGIN:e=>Object.assign(e,{ +"on:begin":(e,n)=>{n.data._beginMatch=e[1]},"on:end":(e,n)=>{ +n.data._beginMatch!==e[1]&&n.ignoreMatch()}}),HASH_COMMENT_MODE:A,IDENT_RE:f, +MATCH_NOTHING_RE:/\b\B/,METHOD_GUARD:{begin:"\\.\\s*"+E,relevance:0}, +NUMBER_MODE:{scope:"number",begin:y,relevance:0},NUMBER_RE:y, +PHRASAL_WORDS_MODE:{ +begin:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/ +},QUOTE_STRING_MODE:k,REGEXP_MODE:{scope:"regexp",begin:/\/(?=[^/\n]*\/)/, +end:/\/[gimuy]*/,contains:[v,{begin:/\[/,end:/\]/,relevance:0,contains:[v]}]}, +RE_STARTERS_RE:"!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~", +SHEBANG:(e={})=>{const n=/^#![ ]*\// +;return e.binary&&(e.begin=b(n,/.*\b/,e.binary,/\b.*/)),a({scope:"meta",begin:n, +end:/$/,relevance:0,"on:begin":(e,n)=>{0!==e.index&&n.ignoreMatch()}},e)}, +TITLE_MODE:{scope:"title",begin:f,relevance:0},UNDERSCORE_IDENT_RE:E, +UNDERSCORE_TITLE_MODE:{scope:"title",begin:E,relevance:0}});function T(e,n){ +"."===e.input[e.index-1]&&n.ignoreMatch()}function R(e,n){ +void 0!==e.className&&(e.scope=e.className,delete e.className)}function D(e,n){ +n&&e.beginKeywords&&(e.begin="\\b("+e.beginKeywords.split(" ").join("|")+")(?!\\.)(?=\\b|\\s)", +e.__beforeBegin=T,e.keywords=e.keywords||e.beginKeywords,delete e.beginKeywords, +void 0===e.relevance&&(e.relevance=0))}function I(e,n){ +Array.isArray(e.illegal)&&(e.illegal=m(...e.illegal))}function L(e,n){ +if(e.match){ +if(e.begin||e.end)throw Error("begin & end are not supported with match") +;e.begin=e.match,delete e.match}}function B(e,n){ +void 0===e.relevance&&(e.relevance=1)}const $=(e,n)=>{if(!e.beforeMatch)return +;if(e.starts)throw Error("beforeMatch cannot be used with starts") +;const t=Object.assign({},e);Object.keys(e).forEach((n=>{delete e[n] +})),e.keywords=t.keywords,e.begin=b(t.beforeMatch,d(t.begin)),e.starts={ +relevance:0,contains:[Object.assign(t,{endsParent:!0})] +},e.relevance=0,delete t.beforeMatch +},z=["of","and","for","in","not","or","if","then","parent","list","value"],F="keyword" +;function U(e,n,t=F){const a=Object.create(null) +;return"string"==typeof e?i(t,e.split(" ")):Array.isArray(e)?i(t,e):Object.keys(e).forEach((t=>{ +Object.assign(a,U(e[t],n,t))})),a;function i(e,t){ +n&&(t=t.map((e=>e.toLowerCase()))),t.forEach((n=>{const t=n.split("|") +;a[t[0]]=[e,j(t[0],t[1])]}))}}function j(e,n){ +return n?Number(n):(e=>z.includes(e.toLowerCase()))(e)?0:1}const P={},K=e=>{ +console.error(e)},H=(e,...n)=>{console.log("WARN: "+e,...n)},q=(e,n)=>{ +P[`${e}/${n}`]||(console.log(`Deprecated as of ${e}. ${n}`),P[`${e}/${n}`]=!0) +},G=Error();function Z(e,n,{key:t}){let a=0;const i=e[t],r={},s={} +;for(let e=1;e<=n.length;e++)s[e+a]=i[e],r[e+a]=!0,a+=p(n[e-1]) +;e[t]=s,e[t]._emit=r,e[t]._multi=!0}function W(e){(e=>{ +e.scope&&"object"==typeof e.scope&&null!==e.scope&&(e.beginScope=e.scope, +delete e.scope)})(e),"string"==typeof e.beginScope&&(e.beginScope={ +_wrap:e.beginScope}),"string"==typeof e.endScope&&(e.endScope={_wrap:e.endScope +}),(e=>{if(Array.isArray(e.begin)){ +if(e.skip||e.excludeBegin||e.returnBegin)throw K("skip, excludeBegin, returnBegin not compatible with beginScope: {}"), +G +;if("object"!=typeof e.beginScope||null===e.beginScope)throw K("beginScope must be object"), +G;Z(e,e.begin,{key:"beginScope"}),e.begin=h(e.begin,{joinWith:""})}})(e),(e=>{ +if(Array.isArray(e.end)){ +if(e.skip||e.excludeEnd||e.returnEnd)throw K("skip, excludeEnd, returnEnd not compatible with endScope: {}"), +G +;if("object"!=typeof e.endScope||null===e.endScope)throw K("endScope must be object"), +G;Z(e,e.end,{key:"endScope"}),e.end=h(e.end,{joinWith:""})}})(e)}function Q(e){ +function n(n,t){ +return RegExp(c(n),"m"+(e.case_insensitive?"i":"")+(e.unicodeRegex?"u":"")+(t?"g":"")) +}class t{constructor(){ +this.matchIndexes={},this.regexes=[],this.matchAt=1,this.position=0} +addRule(e,n){ +n.position=this.position++,this.matchIndexes[this.matchAt]=n,this.regexes.push([n,e]), +this.matchAt+=p(e)+1}compile(){0===this.regexes.length&&(this.exec=()=>null) +;const e=this.regexes.map((e=>e[1]));this.matcherRe=n(h(e,{joinWith:"|" +}),!0),this.lastIndex=0}exec(e){this.matcherRe.lastIndex=this.lastIndex +;const n=this.matcherRe.exec(e);if(!n)return null +;const t=n.findIndex(((e,n)=>n>0&&void 0!==e)),a=this.matchIndexes[t] +;return n.splice(0,t),Object.assign(n,a)}}class i{constructor(){ +this.rules=[],this.multiRegexes=[], +this.count=0,this.lastIndex=0,this.regexIndex=0}getMatcher(e){ +if(this.multiRegexes[e])return this.multiRegexes[e];const n=new t +;return this.rules.slice(e).forEach((([e,t])=>n.addRule(e,t))), +n.compile(),this.multiRegexes[e]=n,n}resumingScanAtSamePosition(){ +return 0!==this.regexIndex}considerAll(){this.regexIndex=0}addRule(e,n){ +this.rules.push([e,n]),"begin"===n.type&&this.count++}exec(e){ +const n=this.getMatcher(this.regexIndex);n.lastIndex=this.lastIndex +;let t=n.exec(e) +;if(this.resumingScanAtSamePosition())if(t&&t.index===this.lastIndex);else{ +const n=this.getMatcher(0);n.lastIndex=this.lastIndex+1,t=n.exec(e)} +return t&&(this.regexIndex+=t.position+1, +this.regexIndex===this.count&&this.considerAll()),t}} +if(e.compilerExtensions||(e.compilerExtensions=[]), +e.contains&&e.contains.includes("self"))throw Error("ERR: contains `self` is not supported at the top-level of a language. See documentation.") +;return e.classNameAliases=a(e.classNameAliases||{}),function t(r,s){const o=r +;if(r.isCompiled)return o +;[R,L,W,$].forEach((e=>e(r,s))),e.compilerExtensions.forEach((e=>e(r,s))), +r.__beforeBegin=null,[D,I,B].forEach((e=>e(r,s))),r.isCompiled=!0;let l=null +;return"object"==typeof r.keywords&&r.keywords.$pattern&&(r.keywords=Object.assign({},r.keywords), +l=r.keywords.$pattern, +delete r.keywords.$pattern),l=l||/\w+/,r.keywords&&(r.keywords=U(r.keywords,e.case_insensitive)), +o.keywordPatternRe=n(l,!0), +s&&(r.begin||(r.begin=/\B|\b/),o.beginRe=n(o.begin),r.end||r.endsWithParent||(r.end=/\B|\b/), +r.end&&(o.endRe=n(o.end)), +o.terminatorEnd=c(o.end)||"",r.endsWithParent&&s.terminatorEnd&&(o.terminatorEnd+=(r.end?"|":"")+s.terminatorEnd)), +r.illegal&&(o.illegalRe=n(r.illegal)), +r.contains||(r.contains=[]),r.contains=[].concat(...r.contains.map((e=>(e=>(e.variants&&!e.cachedVariants&&(e.cachedVariants=e.variants.map((n=>a(e,{ +variants:null},n)))),e.cachedVariants?e.cachedVariants:X(e)?a(e,{ +starts:e.starts?a(e.starts):null +}):Object.isFrozen(e)?a(e):e))("self"===e?r:e)))),r.contains.forEach((e=>{t(e,o) +})),r.starts&&t(r.starts,s),o.matcher=(e=>{const n=new i +;return e.contains.forEach((e=>n.addRule(e.begin,{rule:e,type:"begin" +}))),e.terminatorEnd&&n.addRule(e.terminatorEnd,{type:"end" +}),e.illegal&&n.addRule(e.illegal,{type:"illegal"}),n})(o),o}(e)}function X(e){ +return!!e&&(e.endsWithParent||X(e.starts))}class V extends Error{ +constructor(e,n){super(e),this.name="HTMLInjectionError",this.html=n}} +const J=t,Y=a,ee=Symbol("nomatch"),ne=t=>{ +const a=Object.create(null),i=Object.create(null),r=[];let s=!0 +;const o="Could not find the language '{}', did you forget to load/include a language module?",c={ +disableAutodetect:!0,name:"Plain text",contains:[]};let p={ +ignoreUnescapedHTML:!1,throwUnescapedHTML:!1,noHighlightRe:/^(no-?highlight)$/i, +languageDetectRe:/\blang(?:uage)?-([\w-]+)\b/i,classPrefix:"hljs-", +cssSelector:"pre code",languages:null,__emitter:l};function _(e){ +return p.noHighlightRe.test(e)}function h(e,n,t){let a="",i="" +;"object"==typeof n?(a=e, +t=n.ignoreIllegals,i=n.language):(q("10.7.0","highlight(lang, code, ...args) has been deprecated."), +q("10.7.0","Please use highlight(code, options) instead.\nhttps://github.com/highlightjs/highlight.js/issues/2277"), +i=e,a=n),void 0===t&&(t=!0);const r={code:a,language:i};x("before:highlight",r) +;const s=r.result?r.result:f(r.language,r.code,t) +;return s.code=r.code,x("after:highlight",s),s}function f(e,t,i,r){ +const l=Object.create(null);function c(){if(!x.keywords)return void S.addText(A) +;let e=0;x.keywordPatternRe.lastIndex=0;let n=x.keywordPatternRe.exec(A),t="" +;for(;n;){t+=A.substring(e,n.index) +;const i=w.case_insensitive?n[0].toLowerCase():n[0],r=(a=i,x.keywords[a]);if(r){ +const[e,a]=r +;if(S.addText(t),t="",l[i]=(l[i]||0)+1,l[i]<=7&&(C+=a),e.startsWith("_"))t+=n[0];else{ +const t=w.classNameAliases[e]||e;g(n[0],t)}}else t+=n[0] +;e=x.keywordPatternRe.lastIndex,n=x.keywordPatternRe.exec(A)}var a +;t+=A.substring(e),S.addText(t)}function d(){null!=x.subLanguage?(()=>{ +if(""===A)return;let e=null;if("string"==typeof x.subLanguage){ +if(!a[x.subLanguage])return void S.addText(A) +;e=f(x.subLanguage,A,!0,M[x.subLanguage]),M[x.subLanguage]=e._top +}else e=E(A,x.subLanguage.length?x.subLanguage:null) +;x.relevance>0&&(C+=e.relevance),S.__addSublanguage(e._emitter,e.language) +})():c(),A=""}function g(e,n){ +""!==e&&(S.startScope(n),S.addText(e),S.endScope())}function u(e,n){let t=1 +;const a=n.length-1;for(;t<=a;){if(!e._emit[t]){t++;continue} +const a=w.classNameAliases[e[t]]||e[t],i=n[t];a?g(i,a):(A=i,c(),A=""),t++}} +function b(e,n){ +return e.scope&&"string"==typeof e.scope&&S.openNode(w.classNameAliases[e.scope]||e.scope), +e.beginScope&&(e.beginScope._wrap?(g(A,w.classNameAliases[e.beginScope._wrap]||e.beginScope._wrap), +A=""):e.beginScope._multi&&(u(e.beginScope,n),A="")),x=Object.create(e,{parent:{ +value:x}}),x}function m(e,t,a){let i=((e,n)=>{const t=e&&e.exec(n) +;return t&&0===t.index})(e.endRe,a);if(i){if(e["on:end"]){const a=new n(e) +;e["on:end"](t,a),a.isMatchIgnored&&(i=!1)}if(i){ +for(;e.endsParent&&e.parent;)e=e.parent;return e}} +if(e.endsWithParent)return m(e.parent,t,a)}function _(e){ +return 0===x.matcher.regexIndex?(A+=e[0],1):(D=!0,0)}function h(e){ +const n=e[0],a=t.substring(e.index),i=m(x,e,a);if(!i)return ee;const r=x +;x.endScope&&x.endScope._wrap?(d(), +g(n,x.endScope._wrap)):x.endScope&&x.endScope._multi?(d(), +u(x.endScope,e)):r.skip?A+=n:(r.returnEnd||r.excludeEnd||(A+=n), +d(),r.excludeEnd&&(A=n));do{ +x.scope&&S.closeNode(),x.skip||x.subLanguage||(C+=x.relevance),x=x.parent +}while(x!==i.parent);return i.starts&&b(i.starts,e),r.returnEnd?0:n.length} +let y={};function N(a,r){const o=r&&r[0];if(A+=a,null==o)return d(),0 +;if("begin"===y.type&&"end"===r.type&&y.index===r.index&&""===o){ +if(A+=t.slice(r.index,r.index+1),!s){const n=Error(`0 width match regex (${e})`) +;throw n.languageName=e,n.badRule=y.rule,n}return 1} +if(y=r,"begin"===r.type)return(e=>{ +const t=e[0],a=e.rule,i=new n(a),r=[a.__beforeBegin,a["on:begin"]] +;for(const n of r)if(n&&(n(e,i),i.isMatchIgnored))return _(t) +;return a.skip?A+=t:(a.excludeBegin&&(A+=t), +d(),a.returnBegin||a.excludeBegin||(A=t)),b(a,e),a.returnBegin?0:t.length})(r) +;if("illegal"===r.type&&!i){ +const e=Error('Illegal lexeme "'+o+'" for mode "'+(x.scope||"")+'"') +;throw e.mode=x,e}if("end"===r.type){const e=h(r);if(e!==ee)return e} +if("illegal"===r.type&&""===o)return 1 +;if(R>1e5&&R>3*r.index)throw Error("potential infinite loop, way more iterations than matches") +;return A+=o,o.length}const w=v(e) +;if(!w)throw K(o.replace("{}",e)),Error('Unknown language: "'+e+'"') +;const O=Q(w);let k="",x=r||O;const M={},S=new p.__emitter(p);(()=>{const e=[] +;for(let n=x;n!==w;n=n.parent)n.scope&&e.unshift(n.scope) +;e.forEach((e=>S.openNode(e)))})();let A="",C=0,T=0,R=0,D=!1;try{ +if(w.__emitTokens)w.__emitTokens(t,S);else{for(x.matcher.considerAll();;){ +R++,D?D=!1:x.matcher.considerAll(),x.matcher.lastIndex=T +;const e=x.matcher.exec(t);if(!e)break;const n=N(t.substring(T,e.index),e) +;T=e.index+n}N(t.substring(T))}return S.finalize(),k=S.toHTML(),{language:e, +value:k,relevance:C,illegal:!1,_emitter:S,_top:x}}catch(n){ +if(n.message&&n.message.includes("Illegal"))return{language:e,value:J(t), +illegal:!0,relevance:0,_illegalBy:{message:n.message,index:T, +context:t.slice(T-100,T+100),mode:n.mode,resultSoFar:k},_emitter:S};if(s)return{ +language:e,value:J(t),illegal:!1,relevance:0,errorRaised:n,_emitter:S,_top:x} +;throw n}}function E(e,n){n=n||p.languages||Object.keys(a);const t=(e=>{ +const n={value:J(e),illegal:!1,relevance:0,_top:c,_emitter:new p.__emitter(p)} +;return n._emitter.addText(e),n})(e),i=n.filter(v).filter(k).map((n=>f(n,e,!1))) +;i.unshift(t);const r=i.sort(((e,n)=>{ +if(e.relevance!==n.relevance)return n.relevance-e.relevance +;if(e.language&&n.language){if(v(e.language).supersetOf===n.language)return 1 +;if(v(n.language).supersetOf===e.language)return-1}return 0})),[s,o]=r,l=s +;return l.secondBest=o,l}function y(e){let n=null;const t=(e=>{ +let n=e.className+" ";n+=e.parentNode?e.parentNode.className:"" +;const t=p.languageDetectRe.exec(n);if(t){const n=v(t[1]) +;return n||(H(o.replace("{}",t[1])), +H("Falling back to no-highlight mode for this block.",e)),n?t[1]:"no-highlight"} +return n.split(/\s+/).find((e=>_(e)||v(e)))})(e);if(_(t))return +;if(x("before:highlightElement",{el:e,language:t +}),e.dataset.highlighted)return void console.log("Element previously highlighted. To highlight again, first unset `dataset.highlighted`.",e) +;if(e.children.length>0&&(p.ignoreUnescapedHTML||(console.warn("One of your code blocks includes unescaped HTML. This is a potentially serious security risk."), +console.warn("https://github.com/highlightjs/highlight.js/wiki/security"), +console.warn("The element with unescaped HTML:"), +console.warn(e)),p.throwUnescapedHTML))throw new V("One of your code blocks includes unescaped HTML.",e.innerHTML) +;n=e;const a=n.textContent,r=t?h(a,{language:t,ignoreIllegals:!0}):E(a) +;e.innerHTML=r.value,e.dataset.highlighted="yes",((e,n,t)=>{const a=n&&i[n]||t +;e.classList.add("hljs"),e.classList.add("language-"+a) +})(e,t,r.language),e.result={language:r.language,re:r.relevance, +relevance:r.relevance},r.secondBest&&(e.secondBest={ +language:r.secondBest.language,relevance:r.secondBest.relevance +}),x("after:highlightElement",{el:e,result:r,text:a})}let N=!1;function w(){ +"loading"!==document.readyState?document.querySelectorAll(p.cssSelector).forEach(y):N=!0 +}function v(e){return e=(e||"").toLowerCase(),a[e]||a[i[e]]} +function O(e,{languageName:n}){"string"==typeof e&&(e=[e]),e.forEach((e=>{ +i[e.toLowerCase()]=n}))}function k(e){const n=v(e) +;return n&&!n.disableAutodetect}function x(e,n){const t=e;r.forEach((e=>{ +e[t]&&e[t](n)}))} +"undefined"!=typeof window&&window.addEventListener&&window.addEventListener("DOMContentLoaded",(()=>{ +N&&w()}),!1),Object.assign(t,{highlight:h,highlightAuto:E,highlightAll:w, +highlightElement:y, +highlightBlock:e=>(q("10.7.0","highlightBlock will be removed entirely in v12.0"), +q("10.7.0","Please use highlightElement now."),y(e)),configure:e=>{p=Y(p,e)}, +initHighlighting:()=>{ +w(),q("10.6.0","initHighlighting() deprecated. Use highlightAll() now.")}, +initHighlightingOnLoad:()=>{ +w(),q("10.6.0","initHighlightingOnLoad() deprecated. Use highlightAll() now.") +},registerLanguage:(e,n)=>{let i=null;try{i=n(t)}catch(n){ +if(K("Language definition for '{}' could not be registered.".replace("{}",e)), +!s)throw n;K(n),i=c} +i.name||(i.name=e),a[e]=i,i.rawDefinition=n.bind(null,t),i.aliases&&O(i.aliases,{ +languageName:e})},unregisterLanguage:e=>{delete a[e] +;for(const n of Object.keys(i))i[n]===e&&delete i[n]}, +listLanguages:()=>Object.keys(a),getLanguage:v,registerAliases:O, +autoDetection:k,inherit:Y,addPlugin:e=>{(e=>{ +e["before:highlightBlock"]&&!e["before:highlightElement"]&&(e["before:highlightElement"]=n=>{ +e["before:highlightBlock"](Object.assign({block:n.el},n)) +}),e["after:highlightBlock"]&&!e["after:highlightElement"]&&(e["after:highlightElement"]=n=>{ +e["after:highlightBlock"](Object.assign({block:n.el},n))})})(e),r.push(e)}, +removePlugin:e=>{const n=r.indexOf(e);-1!==n&&r.splice(n,1)}}),t.debugMode=()=>{ +s=!1},t.safeMode=()=>{s=!0},t.versionString="11.9.0",t.regex={concat:b, +lookahead:d,either:m,optional:u,anyNumberOfTimes:g} +;for(const n in C)"object"==typeof C[n]&&e(C[n]);return Object.assign(t,C),t +},te=ne({});te.newInstance=()=>ne({});var ae=te;const ie=e=>({IMPORTANT:{ +scope:"meta",begin:"!important"},BLOCK_COMMENT:e.C_BLOCK_COMMENT_MODE,HEXCOLOR:{ +scope:"number",begin:/#(([0-9a-fA-F]{3,4})|(([0-9a-fA-F]{2}){3,4}))\b/}, +FUNCTION_DISPATCH:{className:"built_in",begin:/[\w-]+(?=\()/}, +ATTRIBUTE_SELECTOR_MODE:{scope:"selector-attr",begin:/\[/,end:/\]/,illegal:"$", +contains:[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},CSS_NUMBER_MODE:{ +scope:"number", +begin:e.NUMBER_RE+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?", +relevance:0},CSS_VARIABLE:{className:"attr",begin:/--[A-Za-z_][A-Za-z0-9_-]*/} +}),re=["a","abbr","address","article","aside","audio","b","blockquote","body","button","canvas","caption","cite","code","dd","del","details","dfn","div","dl","dt","em","fieldset","figcaption","figure","footer","form","h1","h2","h3","h4","h5","h6","header","hgroup","html","i","iframe","img","input","ins","kbd","label","legend","li","main","mark","menu","nav","object","ol","p","q","quote","samp","section","span","strong","summary","sup","table","tbody","td","textarea","tfoot","th","thead","time","tr","ul","var","video"],se=["any-hover","any-pointer","aspect-ratio","color","color-gamut","color-index","device-aspect-ratio","device-height","device-width","display-mode","forced-colors","grid","height","hover","inverted-colors","monochrome","orientation","overflow-block","overflow-inline","pointer","prefers-color-scheme","prefers-contrast","prefers-reduced-motion","prefers-reduced-transparency","resolution","scan","scripting","update","width","min-width","max-width","min-height","max-height"],oe=["active","any-link","blank","checked","current","default","defined","dir","disabled","drop","empty","enabled","first","first-child","first-of-type","fullscreen","future","focus","focus-visible","focus-within","has","host","host-context","hover","indeterminate","in-range","invalid","is","lang","last-child","last-of-type","left","link","local-link","not","nth-child","nth-col","nth-last-child","nth-last-col","nth-last-of-type","nth-of-type","only-child","only-of-type","optional","out-of-range","past","placeholder-shown","read-only","read-write","required","right","root","scope","target","target-within","user-invalid","valid","visited","where"],le=["after","backdrop","before","cue","cue-region","first-letter","first-line","grammar-error","marker","part","placeholder","selection","slotted","spelling-error"],ce=["align-content","align-items","align-self","all","animation","animation-delay","animation-direction","animation-duration","animation-fill-mode","animation-iteration-count","animation-name","animation-play-state","animation-timing-function","backface-visibility","background","background-attachment","background-blend-mode","background-clip","background-color","background-image","background-origin","background-position","background-repeat","background-size","block-size","border","border-block","border-block-color","border-block-end","border-block-end-color","border-block-end-style","border-block-end-width","border-block-start","border-block-start-color","border-block-start-style","border-block-start-width","border-block-style","border-block-width","border-bottom","border-bottom-color","border-bottom-left-radius","border-bottom-right-radius","border-bottom-style","border-bottom-width","border-collapse","border-color","border-image","border-image-outset","border-image-repeat","border-image-slice","border-image-source","border-image-width","border-inline","border-inline-color","border-inline-end","border-inline-end-color","border-inline-end-style","border-inline-end-width","border-inline-start","border-inline-start-color","border-inline-start-style","border-inline-start-width","border-inline-style","border-inline-width","border-left","border-left-color","border-left-style","border-left-width","border-radius","border-right","border-right-color","border-right-style","border-right-width","border-spacing","border-style","border-top","border-top-color","border-top-left-radius","border-top-right-radius","border-top-style","border-top-width","border-width","bottom","box-decoration-break","box-shadow","box-sizing","break-after","break-before","break-inside","caption-side","caret-color","clear","clip","clip-path","clip-rule","color","column-count","column-fill","column-gap","column-rule","column-rule-color","column-rule-style","column-rule-width","column-span","column-width","columns","contain","content","content-visibility","counter-increment","counter-reset","cue","cue-after","cue-before","cursor","direction","display","empty-cells","filter","flex","flex-basis","flex-direction","flex-flow","flex-grow","flex-shrink","flex-wrap","float","flow","font","font-display","font-family","font-feature-settings","font-kerning","font-language-override","font-size","font-size-adjust","font-smoothing","font-stretch","font-style","font-synthesis","font-variant","font-variant-caps","font-variant-east-asian","font-variant-ligatures","font-variant-numeric","font-variant-position","font-variation-settings","font-weight","gap","glyph-orientation-vertical","grid","grid-area","grid-auto-columns","grid-auto-flow","grid-auto-rows","grid-column","grid-column-end","grid-column-start","grid-gap","grid-row","grid-row-end","grid-row-start","grid-template","grid-template-areas","grid-template-columns","grid-template-rows","hanging-punctuation","height","hyphens","icon","image-orientation","image-rendering","image-resolution","ime-mode","inline-size","isolation","justify-content","left","letter-spacing","line-break","line-height","list-style","list-style-image","list-style-position","list-style-type","margin","margin-block","margin-block-end","margin-block-start","margin-bottom","margin-inline","margin-inline-end","margin-inline-start","margin-left","margin-right","margin-top","marks","mask","mask-border","mask-border-mode","mask-border-outset","mask-border-repeat","mask-border-slice","mask-border-source","mask-border-width","mask-clip","mask-composite","mask-image","mask-mode","mask-origin","mask-position","mask-repeat","mask-size","mask-type","max-block-size","max-height","max-inline-size","max-width","min-block-size","min-height","min-inline-size","min-width","mix-blend-mode","nav-down","nav-index","nav-left","nav-right","nav-up","none","normal","object-fit","object-position","opacity","order","orphans","outline","outline-color","outline-offset","outline-style","outline-width","overflow","overflow-wrap","overflow-x","overflow-y","padding","padding-block","padding-block-end","padding-block-start","padding-bottom","padding-inline","padding-inline-end","padding-inline-start","padding-left","padding-right","padding-top","page-break-after","page-break-before","page-break-inside","pause","pause-after","pause-before","perspective","perspective-origin","pointer-events","position","quotes","resize","rest","rest-after","rest-before","right","row-gap","scroll-margin","scroll-margin-block","scroll-margin-block-end","scroll-margin-block-start","scroll-margin-bottom","scroll-margin-inline","scroll-margin-inline-end","scroll-margin-inline-start","scroll-margin-left","scroll-margin-right","scroll-margin-top","scroll-padding","scroll-padding-block","scroll-padding-block-end","scroll-padding-block-start","scroll-padding-bottom","scroll-padding-inline","scroll-padding-inline-end","scroll-padding-inline-start","scroll-padding-left","scroll-padding-right","scroll-padding-top","scroll-snap-align","scroll-snap-stop","scroll-snap-type","scrollbar-color","scrollbar-gutter","scrollbar-width","shape-image-threshold","shape-margin","shape-outside","speak","speak-as","src","tab-size","table-layout","text-align","text-align-all","text-align-last","text-combine-upright","text-decoration","text-decoration-color","text-decoration-line","text-decoration-style","text-emphasis","text-emphasis-color","text-emphasis-position","text-emphasis-style","text-indent","text-justify","text-orientation","text-overflow","text-rendering","text-shadow","text-transform","text-underline-position","top","transform","transform-box","transform-origin","transform-style","transition","transition-delay","transition-duration","transition-property","transition-timing-function","unicode-bidi","vertical-align","visibility","voice-balance","voice-duration","voice-family","voice-pitch","voice-range","voice-rate","voice-stress","voice-volume","white-space","widows","width","will-change","word-break","word-spacing","word-wrap","writing-mode","z-index"].reverse(),de=oe.concat(le) +;var ge="[0-9](_*[0-9])*",ue=`\\.(${ge})`,be="[0-9a-fA-F](_*[0-9a-fA-F])*",me={ +className:"number",variants:[{ +begin:`(\\b(${ge})((${ue})|\\.)?|(${ue}))[eE][+-]?(${ge})[fFdD]?\\b`},{ +begin:`\\b(${ge})((${ue})[fFdD]?\\b|\\.([fFdD]\\b)?)`},{ +begin:`(${ue})[fFdD]?\\b`},{begin:`\\b(${ge})[fFdD]\\b`},{ +begin:`\\b0[xX]((${be})\\.?|(${be})?\\.(${be}))[pP][+-]?(${ge})[fFdD]?\\b`},{ +begin:"\\b(0|[1-9](_*[0-9])*)[lL]?\\b"},{begin:`\\b0[xX](${be})[lL]?\\b`},{ +begin:"\\b0(_*[0-7])*[lL]?\\b"},{begin:"\\b0[bB][01](_*[01])*[lL]?\\b"}], +relevance:0};function pe(e,n,t){return-1===t?"":e.replace(n,(a=>pe(e,n,t-1)))} +const _e="[A-Za-z$_][0-9A-Za-z$_]*",he=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends"],fe=["true","false","null","undefined","NaN","Infinity"],Ee=["Object","Function","Boolean","Symbol","Math","Date","Number","BigInt","String","RegExp","Array","Float32Array","Float64Array","Int8Array","Uint8Array","Uint8ClampedArray","Int16Array","Int32Array","Uint16Array","Uint32Array","BigInt64Array","BigUint64Array","Set","Map","WeakSet","WeakMap","ArrayBuffer","SharedArrayBuffer","Atomics","DataView","JSON","Promise","Generator","GeneratorFunction","AsyncFunction","Reflect","Proxy","Intl","WebAssembly"],ye=["Error","EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"],Ne=["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],we=["arguments","this","super","console","window","document","localStorage","sessionStorage","module","global"],ve=[].concat(Ne,Ee,ye) +;function Oe(e){const n=e.regex,t=_e,a={begin:/<[A-Za-z0-9\\._:-]+/, +end:/\/[A-Za-z0-9\\._:-]+>|\/>/,isTrulyOpeningTag:(e,n)=>{ +const t=e[0].length+e.index,a=e.input[t] +;if("<"===a||","===a)return void n.ignoreMatch();let i +;">"===a&&(((e,{after:n})=>{const t="",M={ +match:[/const|var|let/,/\s+/,t,/\s*/,/=\s*/,/(async\s*)?/,n.lookahead(x)], +keywords:"async",className:{1:"keyword",3:"title.function"},contains:[f]} +;return{name:"JavaScript",aliases:["js","jsx","mjs","cjs"],keywords:i,exports:{ +PARAMS_CONTAINS:h,CLASS_REFERENCE:y},illegal:/#(?![$_A-z])/, +contains:[e.SHEBANG({label:"shebang",binary:"node",relevance:5}),{ +label:"use_strict",className:"meta",relevance:10, +begin:/^\s*['"]use (strict|asm)['"]/ +},e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,d,g,u,b,m,{match:/\$\d+/},l,y,{ +className:"attr",begin:t+n.lookahead(":"),relevance:0},M,{ +begin:"("+e.RE_STARTERS_RE+"|\\b(case|return|throw)\\b)\\s*", +keywords:"return throw case",relevance:0,contains:[m,e.REGEXP_MODE,{ +className:"function",begin:x,returnBegin:!0,end:"\\s*=>",contains:[{ +className:"params",variants:[{begin:e.UNDERSCORE_IDENT_RE,relevance:0},{ +className:null,begin:/\(\s*\)/,skip:!0},{begin:/\(/,end:/\)/,excludeBegin:!0, +excludeEnd:!0,keywords:i,contains:h}]}]},{begin:/,/,relevance:0},{match:/\s+/, +relevance:0},{variants:[{begin:"<>",end:""},{ +match:/<[A-Za-z0-9\\._:-]+\s*\/>/},{begin:a.begin, +"on:begin":a.isTrulyOpeningTag,end:a.end}],subLanguage:"xml",contains:[{ +begin:a.begin,end:a.end,skip:!0,contains:["self"]}]}]},N,{ +beginKeywords:"while if switch catch for"},{ +begin:"\\b(?!function)"+e.UNDERSCORE_IDENT_RE+"\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)\\s*\\{", +returnBegin:!0,label:"func.def",contains:[f,e.inherit(e.TITLE_MODE,{begin:t, +className:"title.function"})]},{match:/\.\.\./,relevance:0},O,{match:"\\$"+t, +relevance:0},{match:[/\bconstructor(?=\s*\()/],className:{1:"title.function"}, +contains:[f]},w,{relevance:0,match:/\b[A-Z][A-Z_0-9]+\b/, +className:"variable.constant"},E,k,{match:/\$[(.]/}]}} +const ke=e=>b(/\b/,e,/\w$/.test(e)?/\b/:/\B/),xe=["Protocol","Type"].map(ke),Me=["init","self"].map(ke),Se=["Any","Self"],Ae=["actor","any","associatedtype","async","await",/as\?/,/as!/,"as","borrowing","break","case","catch","class","consume","consuming","continue","convenience","copy","default","defer","deinit","didSet","distributed","do","dynamic","each","else","enum","extension","fallthrough",/fileprivate\(set\)/,"fileprivate","final","for","func","get","guard","if","import","indirect","infix",/init\?/,/init!/,"inout",/internal\(set\)/,"internal","in","is","isolated","nonisolated","lazy","let","macro","mutating","nonmutating",/open\(set\)/,"open","operator","optional","override","postfix","precedencegroup","prefix",/private\(set\)/,"private","protocol",/public\(set\)/,"public","repeat","required","rethrows","return","set","some","static","struct","subscript","super","switch","throws","throw",/try\?/,/try!/,"try","typealias",/unowned\(safe\)/,/unowned\(unsafe\)/,"unowned","var","weak","where","while","willSet"],Ce=["false","nil","true"],Te=["assignment","associativity","higherThan","left","lowerThan","none","right"],Re=["#colorLiteral","#column","#dsohandle","#else","#elseif","#endif","#error","#file","#fileID","#fileLiteral","#filePath","#function","#if","#imageLiteral","#keyPath","#line","#selector","#sourceLocation","#warning"],De=["abs","all","any","assert","assertionFailure","debugPrint","dump","fatalError","getVaList","isKnownUniquelyReferenced","max","min","numericCast","pointwiseMax","pointwiseMin","precondition","preconditionFailure","print","readLine","repeatElement","sequence","stride","swap","swift_unboxFromSwiftValueWithType","transcode","type","unsafeBitCast","unsafeDowncast","withExtendedLifetime","withUnsafeMutablePointer","withUnsafePointer","withVaList","withoutActuallyEscaping","zip"],Ie=m(/[/=\-+!*%<>&|^~?]/,/[\u00A1-\u00A7]/,/[\u00A9\u00AB]/,/[\u00AC\u00AE]/,/[\u00B0\u00B1]/,/[\u00B6\u00BB\u00BF\u00D7\u00F7]/,/[\u2016-\u2017]/,/[\u2020-\u2027]/,/[\u2030-\u203E]/,/[\u2041-\u2053]/,/[\u2055-\u205E]/,/[\u2190-\u23FF]/,/[\u2500-\u2775]/,/[\u2794-\u2BFF]/,/[\u2E00-\u2E7F]/,/[\u3001-\u3003]/,/[\u3008-\u3020]/,/[\u3030]/),Le=m(Ie,/[\u0300-\u036F]/,/[\u1DC0-\u1DFF]/,/[\u20D0-\u20FF]/,/[\uFE00-\uFE0F]/,/[\uFE20-\uFE2F]/),Be=b(Ie,Le,"*"),$e=m(/[a-zA-Z_]/,/[\u00A8\u00AA\u00AD\u00AF\u00B2-\u00B5\u00B7-\u00BA]/,/[\u00BC-\u00BE\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF]/,/[\u0100-\u02FF\u0370-\u167F\u1681-\u180D\u180F-\u1DBF]/,/[\u1E00-\u1FFF]/,/[\u200B-\u200D\u202A-\u202E\u203F-\u2040\u2054\u2060-\u206F]/,/[\u2070-\u20CF\u2100-\u218F\u2460-\u24FF\u2776-\u2793]/,/[\u2C00-\u2DFF\u2E80-\u2FFF]/,/[\u3004-\u3007\u3021-\u302F\u3031-\u303F\u3040-\uD7FF]/,/[\uF900-\uFD3D\uFD40-\uFDCF\uFDF0-\uFE1F\uFE30-\uFE44]/,/[\uFE47-\uFEFE\uFF00-\uFFFD]/),ze=m($e,/\d/,/[\u0300-\u036F\u1DC0-\u1DFF\u20D0-\u20FF\uFE20-\uFE2F]/),Fe=b($e,ze,"*"),Ue=b(/[A-Z]/,ze,"*"),je=["attached","autoclosure",b(/convention\(/,m("swift","block","c"),/\)/),"discardableResult","dynamicCallable","dynamicMemberLookup","escaping","freestanding","frozen","GKInspectable","IBAction","IBDesignable","IBInspectable","IBOutlet","IBSegueAction","inlinable","main","nonobjc","NSApplicationMain","NSCopying","NSManaged",b(/objc\(/,Fe,/\)/),"objc","objcMembers","propertyWrapper","requires_stored_property_inits","resultBuilder","Sendable","testable","UIApplicationMain","unchecked","unknown","usableFromInline","warn_unqualified_access"],Pe=["iOS","iOSApplicationExtension","macOS","macOSApplicationExtension","macCatalyst","macCatalystApplicationExtension","watchOS","watchOSApplicationExtension","tvOS","tvOSApplicationExtension","swift"] +;var Ke=Object.freeze({__proto__:null,grmr_bash:e=>{const n=e.regex,t={},a={ +begin:/\$\{/,end:/\}/,contains:["self",{begin:/:-/,contains:[t]}]} +;Object.assign(t,{className:"variable",variants:[{ +begin:n.concat(/\$[\w\d#@][\w\d_]*/,"(?![\\w\\d])(?![$])")},a]});const i={ +className:"subst",begin:/\$\(/,end:/\)/,contains:[e.BACKSLASH_ESCAPE]},r={ +begin:/<<-?\s*(?=\w+)/,starts:{contains:[e.END_SAME_AS_BEGIN({begin:/(\w+)/, +end:/(\w+)/,className:"string"})]}},s={className:"string",begin:/"/,end:/"/, +contains:[e.BACKSLASH_ESCAPE,t,i]};i.contains.push(s);const o={begin:/\$?\(\(/, +end:/\)\)/,contains:[{begin:/\d+#[0-9a-f]+/,className:"number"},e.NUMBER_MODE,t] +},l=e.SHEBANG({binary:"(fish|bash|zsh|sh|csh|ksh|tcsh|dash|scsh)",relevance:10 +}),c={className:"function",begin:/\w[\w\d_]*\s*\(\s*\)\s*\{/,returnBegin:!0, +contains:[e.inherit(e.TITLE_MODE,{begin:/\w[\w\d_]*/})],relevance:0};return{ +name:"Bash",aliases:["sh"],keywords:{$pattern:/\b[a-z][a-z0-9._-]+\b/, +keyword:["if","then","else","elif","fi","for","while","until","in","do","done","case","esac","function","select"], +literal:["true","false"], +built_in:["break","cd","continue","eval","exec","exit","export","getopts","hash","pwd","readonly","return","shift","test","times","trap","umask","unset","alias","bind","builtin","caller","command","declare","echo","enable","help","let","local","logout","mapfile","printf","read","readarray","source","type","typeset","ulimit","unalias","set","shopt","autoload","bg","bindkey","bye","cap","chdir","clone","comparguments","compcall","compctl","compdescribe","compfiles","compgroups","compquote","comptags","comptry","compvalues","dirs","disable","disown","echotc","echoti","emulate","fc","fg","float","functions","getcap","getln","history","integer","jobs","kill","limit","log","noglob","popd","print","pushd","pushln","rehash","sched","setcap","setopt","stat","suspend","ttyctl","unfunction","unhash","unlimit","unsetopt","vared","wait","whence","where","which","zcompile","zformat","zftp","zle","zmodload","zparseopts","zprof","zpty","zregexparse","zsocket","zstyle","ztcp","chcon","chgrp","chown","chmod","cp","dd","df","dir","dircolors","ln","ls","mkdir","mkfifo","mknod","mktemp","mv","realpath","rm","rmdir","shred","sync","touch","truncate","vdir","b2sum","base32","base64","cat","cksum","comm","csplit","cut","expand","fmt","fold","head","join","md5sum","nl","numfmt","od","paste","ptx","pr","sha1sum","sha224sum","sha256sum","sha384sum","sha512sum","shuf","sort","split","sum","tac","tail","tr","tsort","unexpand","uniq","wc","arch","basename","chroot","date","dirname","du","echo","env","expr","factor","groups","hostid","id","link","logname","nice","nohup","nproc","pathchk","pinky","printenv","printf","pwd","readlink","runcon","seq","sleep","stat","stdbuf","stty","tee","test","timeout","tty","uname","unlink","uptime","users","who","whoami","yes"] +},contains:[l,e.SHEBANG(),c,o,e.HASH_COMMENT_MODE,r,{match:/(\/[a-z._-]+)+/},s,{ +match:/\\"/},{className:"string",begin:/'/,end:/'/},{match:/\\'/},t]}}, +grmr_c:e=>{const n=e.regex,t=e.COMMENT("//","$",{contains:[{begin:/\\\n/}] +}),a="decltype\\(auto\\)",i="[a-zA-Z_]\\w*::",r="("+a+"|"+n.optional(i)+"[a-zA-Z_]\\w*"+n.optional("<[^<>]+>")+")",s={ +className:"type",variants:[{begin:"\\b[a-z\\d_]*_t\\b"},{ +match:/\batomic_[a-z]{3,6}\b/}]},o={className:"string",variants:[{ +begin:'(u8?|U|L)?"',end:'"',illegal:"\\n",contains:[e.BACKSLASH_ESCAPE]},{ +begin:"(u8?|U|L)?'(\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4,8}|[0-7]{3}|\\S)|.)", +end:"'",illegal:"."},e.END_SAME_AS_BEGIN({ +begin:/(?:u8?|U|L)?R"([^()\\ ]{0,16})\(/,end:/\)([^()\\ ]{0,16})"/})]},l={ +className:"number",variants:[{begin:"\\b(0b[01']+)"},{ +begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)((ll|LL|l|L)(u|U)?|(u|U)(ll|LL|l|L)?|f|F|b|B)" +},{ +begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)" +}],relevance:0},c={className:"meta",begin:/#\s*[a-z]+\b/,end:/$/,keywords:{ +keyword:"if else elif endif define undef warning error line pragma _Pragma ifdef ifndef include" +},contains:[{begin:/\\\n/,relevance:0},e.inherit(o,{className:"string"}),{ +className:"string",begin:/<.*?>/},t,e.C_BLOCK_COMMENT_MODE]},d={ +className:"title",begin:n.optional(i)+e.IDENT_RE,relevance:0 +},g=n.optional(i)+e.IDENT_RE+"\\s*\\(",u={ +keyword:["asm","auto","break","case","continue","default","do","else","enum","extern","for","fortran","goto","if","inline","register","restrict","return","sizeof","struct","switch","typedef","union","volatile","while","_Alignas","_Alignof","_Atomic","_Generic","_Noreturn","_Static_assert","_Thread_local","alignas","alignof","noreturn","static_assert","thread_local","_Pragma"], +type:["float","double","signed","unsigned","int","short","long","char","void","_Bool","_Complex","_Imaginary","_Decimal32","_Decimal64","_Decimal128","const","static","complex","bool","imaginary"], +literal:"true false NULL", +built_in:"std string wstring cin cout cerr clog stdin stdout stderr stringstream istringstream ostringstream auto_ptr deque list queue stack vector map set pair bitset multiset multimap unordered_set unordered_map unordered_multiset unordered_multimap priority_queue make_pair array shared_ptr abort terminate abs acos asin atan2 atan calloc ceil cosh cos exit exp fabs floor fmod fprintf fputs free frexp fscanf future isalnum isalpha iscntrl isdigit isgraph islower isprint ispunct isspace isupper isxdigit tolower toupper labs ldexp log10 log malloc realloc memchr memcmp memcpy memset modf pow printf putchar puts scanf sinh sin snprintf sprintf sqrt sscanf strcat strchr strcmp strcpy strcspn strlen strncat strncmp strncpy strpbrk strrchr strspn strstr tanh tan vfprintf vprintf vsprintf endl initializer_list unique_ptr" +},b=[c,s,t,e.C_BLOCK_COMMENT_MODE,l,o],m={variants:[{begin:/=/,end:/;/},{ +begin:/\(/,end:/\)/},{beginKeywords:"new throw return else",end:/;/}], +keywords:u,contains:b.concat([{begin:/\(/,end:/\)/,keywords:u, +contains:b.concat(["self"]),relevance:0}]),relevance:0},p={ +begin:"("+r+"[\\*&\\s]+)+"+g,returnBegin:!0,end:/[{;=]/,excludeEnd:!0, +keywords:u,illegal:/[^\w\s\*&:<>.]/,contains:[{begin:a,keywords:u,relevance:0},{ +begin:g,returnBegin:!0,contains:[e.inherit(d,{className:"title.function"})], +relevance:0},{relevance:0,match:/,/},{className:"params",begin:/\(/,end:/\)/, +keywords:u,relevance:0,contains:[t,e.C_BLOCK_COMMENT_MODE,o,l,s,{begin:/\(/, +end:/\)/,keywords:u,relevance:0,contains:["self",t,e.C_BLOCK_COMMENT_MODE,o,l,s] +}]},s,t,e.C_BLOCK_COMMENT_MODE,c]};return{name:"C",aliases:["h"],keywords:u, +disableAutodetect:!0,illegal:"=]/,contains:[{ +beginKeywords:"final class struct"},e.TITLE_MODE]}]),exports:{preprocessor:c, +strings:o,keywords:u}}},grmr_cpp:e=>{const n=e.regex,t=e.COMMENT("//","$",{ +contains:[{begin:/\\\n/}] +}),a="decltype\\(auto\\)",i="[a-zA-Z_]\\w*::",r="(?!struct)("+a+"|"+n.optional(i)+"[a-zA-Z_]\\w*"+n.optional("<[^<>]+>")+")",s={ +className:"type",begin:"\\b[a-z\\d_]*_t\\b"},o={className:"string",variants:[{ +begin:'(u8?|U|L)?"',end:'"',illegal:"\\n",contains:[e.BACKSLASH_ESCAPE]},{ +begin:"(u8?|U|L)?'(\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4,8}|[0-7]{3}|\\S)|.)", +end:"'",illegal:"."},e.END_SAME_AS_BEGIN({ +begin:/(?:u8?|U|L)?R"([^()\\ ]{0,16})\(/,end:/\)([^()\\ ]{0,16})"/})]},l={ +className:"number",variants:[{begin:"\\b(0b[01']+)"},{ +begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)((ll|LL|l|L)(u|U)?|(u|U)(ll|LL|l|L)?|f|F|b|B)" +},{ +begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)" +}],relevance:0},c={className:"meta",begin:/#\s*[a-z]+\b/,end:/$/,keywords:{ +keyword:"if else elif endif define undef warning error line pragma _Pragma ifdef ifndef include" +},contains:[{begin:/\\\n/,relevance:0},e.inherit(o,{className:"string"}),{ +className:"string",begin:/<.*?>/},t,e.C_BLOCK_COMMENT_MODE]},d={ +className:"title",begin:n.optional(i)+e.IDENT_RE,relevance:0 +},g=n.optional(i)+e.IDENT_RE+"\\s*\\(",u={ +type:["bool","char","char16_t","char32_t","char8_t","double","float","int","long","short","void","wchar_t","unsigned","signed","const","static"], +keyword:["alignas","alignof","and","and_eq","asm","atomic_cancel","atomic_commit","atomic_noexcept","auto","bitand","bitor","break","case","catch","class","co_await","co_return","co_yield","compl","concept","const_cast|10","consteval","constexpr","constinit","continue","decltype","default","delete","do","dynamic_cast|10","else","enum","explicit","export","extern","false","final","for","friend","goto","if","import","inline","module","mutable","namespace","new","noexcept","not","not_eq","nullptr","operator","or","or_eq","override","private","protected","public","reflexpr","register","reinterpret_cast|10","requires","return","sizeof","static_assert","static_cast|10","struct","switch","synchronized","template","this","thread_local","throw","transaction_safe","transaction_safe_dynamic","true","try","typedef","typeid","typename","union","using","virtual","volatile","while","xor","xor_eq"], +literal:["NULL","false","nullopt","nullptr","true"],built_in:["_Pragma"], +_type_hints:["any","auto_ptr","barrier","binary_semaphore","bitset","complex","condition_variable","condition_variable_any","counting_semaphore","deque","false_type","future","imaginary","initializer_list","istringstream","jthread","latch","lock_guard","multimap","multiset","mutex","optional","ostringstream","packaged_task","pair","promise","priority_queue","queue","recursive_mutex","recursive_timed_mutex","scoped_lock","set","shared_future","shared_lock","shared_mutex","shared_timed_mutex","shared_ptr","stack","string_view","stringstream","timed_mutex","thread","true_type","tuple","unique_lock","unique_ptr","unordered_map","unordered_multimap","unordered_multiset","unordered_set","variant","vector","weak_ptr","wstring","wstring_view"] +},b={className:"function.dispatch",relevance:0,keywords:{ +_hint:["abort","abs","acos","apply","as_const","asin","atan","atan2","calloc","ceil","cerr","cin","clog","cos","cosh","cout","declval","endl","exchange","exit","exp","fabs","floor","fmod","forward","fprintf","fputs","free","frexp","fscanf","future","invoke","isalnum","isalpha","iscntrl","isdigit","isgraph","islower","isprint","ispunct","isspace","isupper","isxdigit","labs","launder","ldexp","log","log10","make_pair","make_shared","make_shared_for_overwrite","make_tuple","make_unique","malloc","memchr","memcmp","memcpy","memset","modf","move","pow","printf","putchar","puts","realloc","scanf","sin","sinh","snprintf","sprintf","sqrt","sscanf","std","stderr","stdin","stdout","strcat","strchr","strcmp","strcpy","strcspn","strlen","strncat","strncmp","strncpy","strpbrk","strrchr","strspn","strstr","swap","tan","tanh","terminate","to_underlying","tolower","toupper","vfprintf","visit","vprintf","vsprintf"] +}, +begin:n.concat(/\b/,/(?!decltype)/,/(?!if)/,/(?!for)/,/(?!switch)/,/(?!while)/,e.IDENT_RE,n.lookahead(/(<[^<>]+>|)\s*\(/)) +},m=[b,c,s,t,e.C_BLOCK_COMMENT_MODE,l,o],p={variants:[{begin:/=/,end:/;/},{ +begin:/\(/,end:/\)/},{beginKeywords:"new throw return else",end:/;/}], +keywords:u,contains:m.concat([{begin:/\(/,end:/\)/,keywords:u, +contains:m.concat(["self"]),relevance:0}]),relevance:0},_={className:"function", +begin:"("+r+"[\\*&\\s]+)+"+g,returnBegin:!0,end:/[{;=]/,excludeEnd:!0, +keywords:u,illegal:/[^\w\s\*&:<>.]/,contains:[{begin:a,keywords:u,relevance:0},{ +begin:g,returnBegin:!0,contains:[d],relevance:0},{begin:/::/,relevance:0},{ +begin:/:/,endsWithParent:!0,contains:[o,l]},{relevance:0,match:/,/},{ +className:"params",begin:/\(/,end:/\)/,keywords:u,relevance:0, +contains:[t,e.C_BLOCK_COMMENT_MODE,o,l,s,{begin:/\(/,end:/\)/,keywords:u, +relevance:0,contains:["self",t,e.C_BLOCK_COMMENT_MODE,o,l,s]}] +},s,t,e.C_BLOCK_COMMENT_MODE,c]};return{name:"C++", +aliases:["cc","c++","h++","hpp","hh","hxx","cxx"],keywords:u,illegal:"",keywords:u,contains:["self",s]},{begin:e.IDENT_RE+"::",keywords:u},{ +match:[/\b(?:enum(?:\s+(?:class|struct))?|class|struct|union)/,/\s+/,/\w+/], +className:{1:"keyword",3:"title.class"}}])}},grmr_csharp:e=>{const n={ +keyword:["abstract","as","base","break","case","catch","class","const","continue","do","else","event","explicit","extern","finally","fixed","for","foreach","goto","if","implicit","in","interface","internal","is","lock","namespace","new","operator","out","override","params","private","protected","public","readonly","record","ref","return","scoped","sealed","sizeof","stackalloc","static","struct","switch","this","throw","try","typeof","unchecked","unsafe","using","virtual","void","volatile","while"].concat(["add","alias","and","ascending","async","await","by","descending","equals","from","get","global","group","init","into","join","let","nameof","not","notnull","on","or","orderby","partial","remove","select","set","unmanaged","value|0","var","when","where","with","yield"]), +built_in:["bool","byte","char","decimal","delegate","double","dynamic","enum","float","int","long","nint","nuint","object","sbyte","short","string","ulong","uint","ushort"], +literal:["default","false","null","true"]},t=e.inherit(e.TITLE_MODE,{ +begin:"[a-zA-Z](\\.?\\w)*"}),a={className:"number",variants:[{ +begin:"\\b(0b[01']+)"},{ +begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)(u|U|l|L|ul|UL|f|F|b|B)"},{ +begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)" +}],relevance:0},i={className:"string",begin:'@"',end:'"',contains:[{begin:'""'}] +},r=e.inherit(i,{illegal:/\n/}),s={className:"subst",begin:/\{/,end:/\}/, +keywords:n},o=e.inherit(s,{illegal:/\n/}),l={className:"string",begin:/\$"/, +end:'"',illegal:/\n/,contains:[{begin:/\{\{/},{begin:/\}\}/ +},e.BACKSLASH_ESCAPE,o]},c={className:"string",begin:/\$@"/,end:'"',contains:[{ +begin:/\{\{/},{begin:/\}\}/},{begin:'""'},s]},d=e.inherit(c,{illegal:/\n/, +contains:[{begin:/\{\{/},{begin:/\}\}/},{begin:'""'},o]}) +;s.contains=[c,l,i,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,a,e.C_BLOCK_COMMENT_MODE], +o.contains=[d,l,r,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,a,e.inherit(e.C_BLOCK_COMMENT_MODE,{ +illegal:/\n/})];const g={variants:[c,l,i,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE] +},u={begin:"<",end:">",contains:[{beginKeywords:"in out"},t] +},b=e.IDENT_RE+"(<"+e.IDENT_RE+"(\\s*,\\s*"+e.IDENT_RE+")*>)?(\\[\\])?",m={ +begin:"@"+e.IDENT_RE,relevance:0};return{name:"C#",aliases:["cs","c#"], +keywords:n,illegal:/::/,contains:[e.COMMENT("///","$",{returnBegin:!0, +contains:[{className:"doctag",variants:[{begin:"///",relevance:0},{ +begin:"\x3c!--|--\x3e"},{begin:""}]}] +}),e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{className:"meta",begin:"#", +end:"$",keywords:{ +keyword:"if else elif endif define undef warning error line region endregion pragma checksum" +}},g,a,{beginKeywords:"class interface",relevance:0,end:/[{;=]/, +illegal:/[^\s:,]/,contains:[{beginKeywords:"where class" +},t,u,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{beginKeywords:"namespace", +relevance:0,end:/[{;=]/,illegal:/[^\s:]/, +contains:[t,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{ +beginKeywords:"record",relevance:0,end:/[{;=]/,illegal:/[^\s:]/, +contains:[t,u,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{className:"meta", +begin:"^\\s*\\[(?=[\\w])",excludeBegin:!0,end:"\\]",excludeEnd:!0,contains:[{ +className:"string",begin:/"/,end:/"/}]},{ +beginKeywords:"new return throw await else",relevance:0},{className:"function", +begin:"("+b+"\\s+)+"+e.IDENT_RE+"\\s*(<[^=]+>\\s*)?\\(",returnBegin:!0, +end:/\s*[{;=]/,excludeEnd:!0,keywords:n,contains:[{ +beginKeywords:"public private protected static internal protected abstract async extern override unsafe virtual new sealed partial", +relevance:0},{begin:e.IDENT_RE+"\\s*(<[^=]+>\\s*)?\\(",returnBegin:!0, +contains:[e.TITLE_MODE,u],relevance:0},{match:/\(\)/},{className:"params", +begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:n,relevance:0, +contains:[g,a,e.C_BLOCK_COMMENT_MODE] +},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},m]}},grmr_css:e=>{ +const n=e.regex,t=ie(e),a=[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE];return{ +name:"CSS",case_insensitive:!0,illegal:/[=|'\$]/,keywords:{ +keyframePosition:"from to"},classNameAliases:{keyframePosition:"selector-tag"}, +contains:[t.BLOCK_COMMENT,{begin:/-(webkit|moz|ms|o)-(?=[a-z])/ +},t.CSS_NUMBER_MODE,{className:"selector-id",begin:/#[A-Za-z0-9_-]+/,relevance:0 +},{className:"selector-class",begin:"\\.[a-zA-Z-][a-zA-Z0-9_-]*",relevance:0 +},t.ATTRIBUTE_SELECTOR_MODE,{className:"selector-pseudo",variants:[{ +begin:":("+oe.join("|")+")"},{begin:":(:)?("+le.join("|")+")"}] +},t.CSS_VARIABLE,{className:"attribute",begin:"\\b("+ce.join("|")+")\\b"},{ +begin:/:/,end:/[;}{]/, +contains:[t.BLOCK_COMMENT,t.HEXCOLOR,t.IMPORTANT,t.CSS_NUMBER_MODE,...a,{ +begin:/(url|data-uri)\(/,end:/\)/,relevance:0,keywords:{built_in:"url data-uri" +},contains:[...a,{className:"string",begin:/[^)]/,endsWithParent:!0, +excludeEnd:!0}]},t.FUNCTION_DISPATCH]},{begin:n.lookahead(/@/),end:"[{;]", +relevance:0,illegal:/:/,contains:[{className:"keyword",begin:/@-?\w[\w]*(-\w+)*/ +},{begin:/\s/,endsWithParent:!0,excludeEnd:!0,relevance:0,keywords:{ +$pattern:/[a-z-]+/,keyword:"and or not only",attribute:se.join(" ")},contains:[{ +begin:/[a-z-]+(?=:)/,className:"attribute"},...a,t.CSS_NUMBER_MODE]}]},{ +className:"selector-tag",begin:"\\b("+re.join("|")+")\\b"}]}},grmr_diff:e=>{ +const n=e.regex;return{name:"Diff",aliases:["patch"],contains:[{ +className:"meta",relevance:10, +match:n.either(/^@@ +-\d+,\d+ +\+\d+,\d+ +@@/,/^\*\*\* +\d+,\d+ +\*\*\*\*$/,/^--- +\d+,\d+ +----$/) +},{className:"comment",variants:[{ +begin:n.either(/Index: /,/^index/,/={3,}/,/^-{3}/,/^\*{3} /,/^\+{3}/,/^diff --git/), +end:/$/},{match:/^\*{15}$/}]},{className:"addition",begin:/^\+/,end:/$/},{ +className:"deletion",begin:/^-/,end:/$/},{className:"addition",begin:/^!/, +end:/$/}]}},grmr_go:e=>{const n={ +keyword:["break","case","chan","const","continue","default","defer","else","fallthrough","for","func","go","goto","if","import","interface","map","package","range","return","select","struct","switch","type","var"], +type:["bool","byte","complex64","complex128","error","float32","float64","int8","int16","int32","int64","string","uint8","uint16","uint32","uint64","int","uint","uintptr","rune"], +literal:["true","false","iota","nil"], +built_in:["append","cap","close","complex","copy","imag","len","make","new","panic","print","println","real","recover","delete"] +};return{name:"Go",aliases:["golang"],keywords:n,illegal:"{const n=e.regex;return{name:"GraphQL",aliases:["gql"], +case_insensitive:!0,disableAutodetect:!1,keywords:{ +keyword:["query","mutation","subscription","type","input","schema","directive","interface","union","scalar","fragment","enum","on"], +literal:["true","false","null"]}, +contains:[e.HASH_COMMENT_MODE,e.QUOTE_STRING_MODE,e.NUMBER_MODE,{ +scope:"punctuation",match:/[.]{3}/,relevance:0},{scope:"punctuation", +begin:/[\!\(\)\:\=\[\]\{\|\}]{1}/,relevance:0},{scope:"variable",begin:/\$/, +end:/\W/,excludeEnd:!0,relevance:0},{scope:"meta",match:/@\w+/,excludeEnd:!0},{ +scope:"symbol",begin:n.concat(/[_A-Za-z][_0-9A-Za-z]*/,n.lookahead(/\s*:/)), +relevance:0}],illegal:[/[;<']/,/BEGIN/]}},grmr_ini:e=>{const n=e.regex,t={ +className:"number",relevance:0,variants:[{begin:/([+-]+)?[\d]+_[\d_]+/},{ +begin:e.NUMBER_RE}]},a=e.COMMENT();a.variants=[{begin:/;/,end:/$/},{begin:/#/, +end:/$/}];const i={className:"variable",variants:[{begin:/\$[\w\d"][\w\d_]*/},{ +begin:/\$\{(.*?)\}/}]},r={className:"literal", +begin:/\bon|off|true|false|yes|no\b/},s={className:"string", +contains:[e.BACKSLASH_ESCAPE],variants:[{begin:"'''",end:"'''",relevance:10},{ +begin:'"""',end:'"""',relevance:10},{begin:'"',end:'"'},{begin:"'",end:"'"}] +},o={begin:/\[/,end:/\]/,contains:[a,r,i,s,t,"self"],relevance:0 +},l=n.either(/[A-Za-z0-9_-]+/,/"(\\"|[^"])*"/,/'[^']*'/);return{ +name:"TOML, also INI",aliases:["toml"],case_insensitive:!0,illegal:/\S/, +contains:[a,{className:"section",begin:/\[+/,end:/\]+/},{ +begin:n.concat(l,"(\\s*\\.\\s*",l,")*",n.lookahead(/\s*=\s*[^#\s]/)), +className:"attr",starts:{end:/$/,contains:[a,o,r,i,s,t]}}]}},grmr_java:e=>{ +const n=e.regex,t="[\xc0-\u02b8a-zA-Z_$][\xc0-\u02b8a-zA-Z_$0-9]*",a=t+pe("(?:<"+t+"~~~(?:\\s*,\\s*"+t+"~~~)*>)?",/~~~/g,2),i={ +keyword:["synchronized","abstract","private","var","static","if","const ","for","while","strictfp","finally","protected","import","native","final","void","enum","else","break","transient","catch","instanceof","volatile","case","assert","package","default","public","try","switch","continue","throws","protected","public","private","module","requires","exports","do","sealed","yield","permits"], +literal:["false","true","null"], +type:["char","boolean","long","float","int","byte","short","double"], +built_in:["super","this"]},r={className:"meta",begin:"@"+t,contains:[{ +begin:/\(/,end:/\)/,contains:["self"]}]},s={className:"params",begin:/\(/, +end:/\)/,keywords:i,relevance:0,contains:[e.C_BLOCK_COMMENT_MODE],endsParent:!0} +;return{name:"Java",aliases:["jsp"],keywords:i,illegal:/<\/|#/, +contains:[e.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{begin:/\w+@/, +relevance:0},{className:"doctag",begin:"@[A-Za-z]+"}]}),{ +begin:/import java\.[a-z]+\./,keywords:"import",relevance:2 +},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{begin:/"""/,end:/"""/, +className:"string",contains:[e.BACKSLASH_ESCAPE] +},e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,{ +match:[/\b(?:class|interface|enum|extends|implements|new)/,/\s+/,t],className:{ +1:"keyword",3:"title.class"}},{match:/non-sealed/,scope:"keyword"},{ +begin:[n.concat(/(?!else)/,t),/\s+/,t,/\s+/,/=(?!=)/],className:{1:"type", +3:"variable",5:"operator"}},{begin:[/record/,/\s+/,t],className:{1:"keyword", +3:"title.class"},contains:[s,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{ +beginKeywords:"new throw return else",relevance:0},{ +begin:["(?:"+a+"\\s+)",e.UNDERSCORE_IDENT_RE,/\s*(?=\()/],className:{ +2:"title.function"},keywords:i,contains:[{className:"params",begin:/\(/, +end:/\)/,keywords:i,relevance:0, +contains:[r,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,me,e.C_BLOCK_COMMENT_MODE] +},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},me,r]}},grmr_javascript:Oe, +grmr_json:e=>{const n=["true","false","null"],t={scope:"literal", +beginKeywords:n.join(" ")};return{name:"JSON",keywords:{literal:n},contains:[{ +className:"attr",begin:/"(\\.|[^\\"\r\n])*"(?=\s*:)/,relevance:1.01},{ +match:/[{}[\],:]/,className:"punctuation",relevance:0 +},e.QUOTE_STRING_MODE,t,e.C_NUMBER_MODE,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE], +illegal:"\\S"}},grmr_kotlin:e=>{const n={ +keyword:"abstract as val var vararg get set class object open private protected public noinline crossinline dynamic final enum if else do while for when throw try catch finally import package is in fun override companion reified inline lateinit init interface annotation data sealed internal infix operator out by constructor super tailrec where const inner suspend typealias external expect actual", +built_in:"Byte Short Char Int Long Boolean Float Double Void Unit Nothing", +literal:"true false null"},t={className:"symbol",begin:e.UNDERSCORE_IDENT_RE+"@" +},a={className:"subst",begin:/\$\{/,end:/\}/,contains:[e.C_NUMBER_MODE]},i={ +className:"variable",begin:"\\$"+e.UNDERSCORE_IDENT_RE},r={className:"string", +variants:[{begin:'"""',end:'"""(?=[^"])',contains:[i,a]},{begin:"'",end:"'", +illegal:/\n/,contains:[e.BACKSLASH_ESCAPE]},{begin:'"',end:'"',illegal:/\n/, +contains:[e.BACKSLASH_ESCAPE,i,a]}]};a.contains.push(r);const s={ +className:"meta", +begin:"@(?:file|property|field|get|set|receiver|param|setparam|delegate)\\s*:(?:\\s*"+e.UNDERSCORE_IDENT_RE+")?" +},o={className:"meta",begin:"@"+e.UNDERSCORE_IDENT_RE,contains:[{begin:/\(/, +end:/\)/,contains:[e.inherit(r,{className:"string"}),"self"]}] +},l=me,c=e.COMMENT("/\\*","\\*/",{contains:[e.C_BLOCK_COMMENT_MODE]}),d={ +variants:[{className:"type",begin:e.UNDERSCORE_IDENT_RE},{begin:/\(/,end:/\)/, +contains:[]}]},g=d;return g.variants[1].contains=[d],d.variants[1].contains=[g], +{name:"Kotlin",aliases:["kt","kts"],keywords:n, +contains:[e.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{className:"doctag", +begin:"@[A-Za-z]+"}]}),e.C_LINE_COMMENT_MODE,c,{className:"keyword", +begin:/\b(break|continue|return|this)\b/,starts:{contains:[{className:"symbol", +begin:/@\w+/}]}},t,s,o,{className:"function",beginKeywords:"fun",end:"[(]|$", +returnBegin:!0,excludeEnd:!0,keywords:n,relevance:5,contains:[{ +begin:e.UNDERSCORE_IDENT_RE+"\\s*\\(",returnBegin:!0,relevance:0, +contains:[e.UNDERSCORE_TITLE_MODE]},{className:"type",begin://, +keywords:"reified",relevance:0},{className:"params",begin:/\(/,end:/\)/, +endsParent:!0,keywords:n,relevance:0,contains:[{begin:/:/,end:/[=,\/]/, +endsWithParent:!0,contains:[d,e.C_LINE_COMMENT_MODE,c],relevance:0 +},e.C_LINE_COMMENT_MODE,c,s,o,r,e.C_NUMBER_MODE]},c]},{ +begin:[/class|interface|trait/,/\s+/,e.UNDERSCORE_IDENT_RE],beginScope:{ +3:"title.class"},keywords:"class interface trait",end:/[:\{(]|$/,excludeEnd:!0, +illegal:"extends implements",contains:[{ +beginKeywords:"public protected internal private constructor" +},e.UNDERSCORE_TITLE_MODE,{className:"type",begin://,excludeBegin:!0, +excludeEnd:!0,relevance:0},{className:"type",begin:/[,:]\s*/,end:/[<\(,){\s]|$/, +excludeBegin:!0,returnEnd:!0},s,o]},r,{className:"meta",begin:"^#!/usr/bin/env", +end:"$",illegal:"\n"},l]}},grmr_less:e=>{ +const n=ie(e),t=de,a="[\\w-]+",i="("+a+"|@\\{"+a+"\\})",r=[],s=[],o=e=>({ +className:"string",begin:"~?"+e+".*?"+e}),l=(e,n,t)=>({className:e,begin:n, +relevance:t}),c={$pattern:/[a-z-]+/,keyword:"and or not only", +attribute:se.join(" ")},d={begin:"\\(",end:"\\)",contains:s,keywords:c, +relevance:0} +;s.push(e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,o("'"),o('"'),n.CSS_NUMBER_MODE,{ +begin:"(url|data-uri)\\(",starts:{className:"string",end:"[\\)\\n]", +excludeEnd:!0} +},n.HEXCOLOR,d,l("variable","@@?"+a,10),l("variable","@\\{"+a+"\\}"),l("built_in","~?`[^`]*?`"),{ +className:"attribute",begin:a+"\\s*:",end:":",returnBegin:!0,excludeEnd:!0 +},n.IMPORTANT,{beginKeywords:"and not"},n.FUNCTION_DISPATCH);const g=s.concat({ +begin:/\{/,end:/\}/,contains:r}),u={beginKeywords:"when",endsWithParent:!0, +contains:[{beginKeywords:"and not"}].concat(s)},b={begin:i+"\\s*:", +returnBegin:!0,end:/[;}]/,relevance:0,contains:[{begin:/-(webkit|moz|ms|o)-/ +},n.CSS_VARIABLE,{className:"attribute",begin:"\\b("+ce.join("|")+")\\b", +end:/(?=:)/,starts:{endsWithParent:!0,illegal:"[<=$]",relevance:0,contains:s}}] +},m={className:"keyword", +begin:"@(import|media|charset|font-face|(-[a-z]+-)?keyframes|supports|document|namespace|page|viewport|host)\\b", +starts:{end:"[;{}]",keywords:c,returnEnd:!0,contains:s,relevance:0}},p={ +className:"variable",variants:[{begin:"@"+a+"\\s*:",relevance:15},{begin:"@"+a +}],starts:{end:"[;}]",returnEnd:!0,contains:g}},_={variants:[{ +begin:"[\\.#:&\\[>]",end:"[;{}]"},{begin:i,end:/\{/}],returnBegin:!0, +returnEnd:!0,illegal:"[<='$\"]",relevance:0, +contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,u,l("keyword","all\\b"),l("variable","@\\{"+a+"\\}"),{ +begin:"\\b("+re.join("|")+")\\b",className:"selector-tag" +},n.CSS_NUMBER_MODE,l("selector-tag",i,0),l("selector-id","#"+i),l("selector-class","\\."+i,0),l("selector-tag","&",0),n.ATTRIBUTE_SELECTOR_MODE,{ +className:"selector-pseudo",begin:":("+oe.join("|")+")"},{ +className:"selector-pseudo",begin:":(:)?("+le.join("|")+")"},{begin:/\(/, +end:/\)/,relevance:0,contains:g},{begin:"!important"},n.FUNCTION_DISPATCH]},h={ +begin:a+":(:)?"+`(${t.join("|")})`,returnBegin:!0,contains:[_]} +;return r.push(e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,m,p,h,b,_,u,n.FUNCTION_DISPATCH), +{name:"Less",case_insensitive:!0,illegal:"[=>'/<($\"]",contains:r}}, +grmr_lua:e=>{const n="\\[=*\\[",t="\\]=*\\]",a={begin:n,end:t,contains:["self"] +},i=[e.COMMENT("--(?!"+n+")","$"),e.COMMENT("--"+n,t,{contains:[a],relevance:10 +})];return{name:"Lua",keywords:{$pattern:e.UNDERSCORE_IDENT_RE, +literal:"true false nil", +keyword:"and break do else elseif end for goto if in local not or repeat return then until while", +built_in:"_G _ENV _VERSION __index __newindex __mode __call __metatable __tostring __len __gc __add __sub __mul __div __mod __pow __concat __unm __eq __lt __le assert collectgarbage dofile error getfenv getmetatable ipairs load loadfile loadstring module next pairs pcall print rawequal rawget rawset require select setfenv setmetatable tonumber tostring type unpack xpcall arg self coroutine resume yield status wrap create running debug getupvalue debug sethook getmetatable gethook setmetatable setlocal traceback setfenv getinfo setupvalue getlocal getregistry getfenv io lines write close flush open output type read stderr stdin input stdout popen tmpfile math log max acos huge ldexp pi cos tanh pow deg tan cosh sinh random randomseed frexp ceil floor rad abs sqrt modf asin min mod fmod log10 atan2 exp sin atan os exit setlocale date getenv difftime remove time clock tmpname rename execute package preload loadlib loaded loaders cpath config path seeall string sub upper len gfind rep find match char dump gmatch reverse byte format gsub lower table setn insert getn foreachi maxn foreach concat sort remove" +},contains:i.concat([{className:"function",beginKeywords:"function",end:"\\)", +contains:[e.inherit(e.TITLE_MODE,{ +begin:"([_a-zA-Z]\\w*\\.)*([_a-zA-Z]\\w*:)?[_a-zA-Z]\\w*"}),{className:"params", +begin:"\\(",endsWithParent:!0,contains:i}].concat(i) +},e.C_NUMBER_MODE,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,{className:"string", +begin:n,end:t,contains:[a],relevance:5}])}},grmr_makefile:e=>{const n={ +className:"variable",variants:[{begin:"\\$\\("+e.UNDERSCORE_IDENT_RE+"\\)", +contains:[e.BACKSLASH_ESCAPE]},{begin:/\$[@%{ +const n={begin:/<\/?[A-Za-z_]/,end:">",subLanguage:"xml",relevance:0},t={ +variants:[{begin:/\[.+?\]\[.*?\]/,relevance:0},{ +begin:/\[.+?\]\(((data|javascript|mailto):|(?:http|ftp)s?:\/\/).*?\)/, +relevance:2},{ +begin:e.regex.concat(/\[.+?\]\(/,/[A-Za-z][A-Za-z0-9+.-]*/,/:\/\/.*?\)/), +relevance:2},{begin:/\[.+?\]\([./?&#].*?\)/,relevance:1},{ +begin:/\[.*?\]\(.*?\)/,relevance:0}],returnBegin:!0,contains:[{match:/\[(?=\])/ +},{className:"string",relevance:0,begin:"\\[",end:"\\]",excludeBegin:!0, +returnEnd:!0},{className:"link",relevance:0,begin:"\\]\\(",end:"\\)", +excludeBegin:!0,excludeEnd:!0},{className:"symbol",relevance:0,begin:"\\]\\[", +end:"\\]",excludeBegin:!0,excludeEnd:!0}]},a={className:"strong",contains:[], +variants:[{begin:/_{2}(?!\s)/,end:/_{2}/},{begin:/\*{2}(?!\s)/,end:/\*{2}/}] +},i={className:"emphasis",contains:[],variants:[{begin:/\*(?![*\s])/,end:/\*/},{ +begin:/_(?![_\s])/,end:/_/,relevance:0}]},r=e.inherit(a,{contains:[] +}),s=e.inherit(i,{contains:[]});a.contains.push(s),i.contains.push(r) +;let o=[n,t];return[a,i,r,s].forEach((e=>{e.contains=e.contains.concat(o) +})),o=o.concat(a,i),{name:"Markdown",aliases:["md","mkdown","mkd"],contains:[{ +className:"section",variants:[{begin:"^#{1,6}",end:"$",contains:o},{ +begin:"(?=^.+?\\n[=-]{2,}$)",contains:[{begin:"^[=-]*$"},{begin:"^",end:"\\n", +contains:o}]}]},n,{className:"bullet",begin:"^[ \t]*([*+-]|(\\d+\\.))(?=\\s+)", +end:"\\s+",excludeEnd:!0},a,i,{className:"quote",begin:"^>\\s+",contains:o, +end:"$"},{className:"code",variants:[{begin:"(`{3,})[^`](.|\\n)*?\\1`*[ ]*"},{ +begin:"(~{3,})[^~](.|\\n)*?\\1~*[ ]*"},{begin:"```",end:"```+[ ]*$"},{ +begin:"~~~",end:"~~~+[ ]*$"},{begin:"`.+?`"},{begin:"(?=^( {4}|\\t))", +contains:[{begin:"^( {4}|\\t)",end:"(\\n)$"}],relevance:0}]},{ +begin:"^[-\\*]{3,}",end:"$"},t,{begin:/^\[[^\n]+\]:/,returnBegin:!0,contains:[{ +className:"symbol",begin:/\[/,end:/\]/,excludeBegin:!0,excludeEnd:!0},{ +className:"link",begin:/:\s*/,end:/$/,excludeBegin:!0}]}]}},grmr_objectivec:e=>{ +const n=/[a-zA-Z@][a-zA-Z0-9_]*/,t={$pattern:n, +keyword:["@interface","@class","@protocol","@implementation"]};return{ +name:"Objective-C",aliases:["mm","objc","obj-c","obj-c++","objective-c++"], +keywords:{"variable.language":["this","super"],$pattern:n, +keyword:["while","export","sizeof","typedef","const","struct","for","union","volatile","static","mutable","if","do","return","goto","enum","else","break","extern","asm","case","default","register","explicit","typename","switch","continue","inline","readonly","assign","readwrite","self","@synchronized","id","typeof","nonatomic","IBOutlet","IBAction","strong","weak","copy","in","out","inout","bycopy","byref","oneway","__strong","__weak","__block","__autoreleasing","@private","@protected","@public","@try","@property","@end","@throw","@catch","@finally","@autoreleasepool","@synthesize","@dynamic","@selector","@optional","@required","@encode","@package","@import","@defs","@compatibility_alias","__bridge","__bridge_transfer","__bridge_retained","__bridge_retain","__covariant","__contravariant","__kindof","_Nonnull","_Nullable","_Null_unspecified","__FUNCTION__","__PRETTY_FUNCTION__","__attribute__","getter","setter","retain","unsafe_unretained","nonnull","nullable","null_unspecified","null_resettable","class","instancetype","NS_DESIGNATED_INITIALIZER","NS_UNAVAILABLE","NS_REQUIRES_SUPER","NS_RETURNS_INNER_POINTER","NS_INLINE","NS_AVAILABLE","NS_DEPRECATED","NS_ENUM","NS_OPTIONS","NS_SWIFT_UNAVAILABLE","NS_ASSUME_NONNULL_BEGIN","NS_ASSUME_NONNULL_END","NS_REFINED_FOR_SWIFT","NS_SWIFT_NAME","NS_SWIFT_NOTHROW","NS_DURING","NS_HANDLER","NS_ENDHANDLER","NS_VALUERETURN","NS_VOIDRETURN"], +literal:["false","true","FALSE","TRUE","nil","YES","NO","NULL"], +built_in:["dispatch_once_t","dispatch_queue_t","dispatch_sync","dispatch_async","dispatch_once"], +type:["int","float","char","unsigned","signed","short","long","double","wchar_t","unichar","void","bool","BOOL","id|0","_Bool"] +},illegal:"/,end:/$/,illegal:"\\n" +},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{className:"class", +begin:"("+t.keyword.join("|")+")\\b",end:/(\{|$)/,excludeEnd:!0,keywords:t, +contains:[e.UNDERSCORE_TITLE_MODE]},{begin:"\\."+e.UNDERSCORE_IDENT_RE, +relevance:0}]}},grmr_perl:e=>{const n=e.regex,t=/[dualxmsipngr]{0,12}/,a={ +$pattern:/[\w.]+/, +keyword:"abs accept alarm and atan2 bind binmode bless break caller chdir chmod chomp chop chown chr chroot close closedir connect continue cos crypt dbmclose dbmopen defined delete die do dump each else elsif endgrent endhostent endnetent endprotoent endpwent endservent eof eval exec exists exit exp fcntl fileno flock for foreach fork format formline getc getgrent getgrgid getgrnam gethostbyaddr gethostbyname gethostent getlogin getnetbyaddr getnetbyname getnetent getpeername getpgrp getpriority getprotobyname getprotobynumber getprotoent getpwent getpwnam getpwuid getservbyname getservbyport getservent getsockname getsockopt given glob gmtime goto grep gt hex if index int ioctl join keys kill last lc lcfirst length link listen local localtime log lstat lt ma map mkdir msgctl msgget msgrcv msgsnd my ne next no not oct open opendir or ord our pack package pipe pop pos print printf prototype push q|0 qq quotemeta qw qx rand read readdir readline readlink readpipe recv redo ref rename require reset return reverse rewinddir rindex rmdir say scalar seek seekdir select semctl semget semop send setgrent sethostent setnetent setpgrp setpriority setprotoent setpwent setservent setsockopt shift shmctl shmget shmread shmwrite shutdown sin sleep socket socketpair sort splice split sprintf sqrt srand stat state study sub substr symlink syscall sysopen sysread sysseek system syswrite tell telldir tie tied time times tr truncate uc ucfirst umask undef unless unlink unpack unshift untie until use utime values vec wait waitpid wantarray warn when while write x|0 xor y|0" +},i={className:"subst",begin:"[$@]\\{",end:"\\}",keywords:a},r={begin:/->\{/, +end:/\}/},s={variants:[{begin:/\$\d/},{ +begin:n.concat(/[$%@](\^\w\b|#\w+(::\w+)*|\{\w+\}|\w+(::\w*)*)/,"(?![A-Za-z])(?![@$%])") +},{begin:/[$%@][^\s\w{]/,relevance:0}] +},o=[e.BACKSLASH_ESCAPE,i,s],l=[/!/,/\//,/\|/,/\?/,/'/,/"/,/#/],c=(e,a,i="\\1")=>{ +const r="\\1"===i?i:n.concat(i,a) +;return n.concat(n.concat("(?:",e,")"),a,/(?:\\.|[^\\\/])*?/,r,/(?:\\.|[^\\\/])*?/,i,t) +},d=(e,a,i)=>n.concat(n.concat("(?:",e,")"),a,/(?:\\.|[^\\\/])*?/,i,t),g=[s,e.HASH_COMMENT_MODE,e.COMMENT(/^=\w/,/=cut/,{ +endsWithParent:!0}),r,{className:"string",contains:o,variants:[{ +begin:"q[qwxr]?\\s*\\(",end:"\\)",relevance:5},{begin:"q[qwxr]?\\s*\\[", +end:"\\]",relevance:5},{begin:"q[qwxr]?\\s*\\{",end:"\\}",relevance:5},{ +begin:"q[qwxr]?\\s*\\|",end:"\\|",relevance:5},{begin:"q[qwxr]?\\s*<",end:">", +relevance:5},{begin:"qw\\s+q",end:"q",relevance:5},{begin:"'",end:"'", +contains:[e.BACKSLASH_ESCAPE]},{begin:'"',end:'"'},{begin:"`",end:"`", +contains:[e.BACKSLASH_ESCAPE]},{begin:/\{\w+\}/,relevance:0},{ +begin:"-?\\w+\\s*=>",relevance:0}]},{className:"number", +begin:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b", +relevance:0},{ +begin:"(\\/\\/|"+e.RE_STARTERS_RE+"|\\b(split|return|print|reverse|grep)\\b)\\s*", +keywords:"split return print reverse grep",relevance:0, +contains:[e.HASH_COMMENT_MODE,{className:"regexp",variants:[{ +begin:c("s|tr|y",n.either(...l,{capture:!0}))},{begin:c("s|tr|y","\\(","\\)")},{ +begin:c("s|tr|y","\\[","\\]")},{begin:c("s|tr|y","\\{","\\}")}],relevance:2},{ +className:"regexp",variants:[{begin:/(m|qr)\/\//,relevance:0},{ +begin:d("(?:m|qr)?",/\//,/\//)},{begin:d("m|qr",n.either(...l,{capture:!0 +}),/\1/)},{begin:d("m|qr",/\(/,/\)/)},{begin:d("m|qr",/\[/,/\]/)},{ +begin:d("m|qr",/\{/,/\}/)}]}]},{className:"function",beginKeywords:"sub", +end:"(\\s*\\(.*?\\))?[;{]",excludeEnd:!0,relevance:5,contains:[e.TITLE_MODE]},{ +begin:"-\\w\\b",relevance:0},{begin:"^__DATA__$",end:"^__END__$", +subLanguage:"mojolicious",contains:[{begin:"^@@.*",end:"$",className:"comment"}] +}];return i.contains=g,r.contains=g,{name:"Perl",aliases:["pl","pm"],keywords:a, +contains:g}},grmr_php:e=>{ +const n=e.regex,t=/(?![A-Za-z0-9])(?![$])/,a=n.concat(/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/,t),i=n.concat(/(\\?[A-Z][a-z0-9_\x7f-\xff]+|\\?[A-Z]+(?=[A-Z][a-z0-9_\x7f-\xff])){1,}/,t),r={ +scope:"variable",match:"\\$+"+a},s={scope:"subst",variants:[{begin:/\$\w+/},{ +begin:/\{\$/,end:/\}/}]},o=e.inherit(e.APOS_STRING_MODE,{illegal:null +}),l="[ \t\n]",c={scope:"string",variants:[e.inherit(e.QUOTE_STRING_MODE,{ +illegal:null,contains:e.QUOTE_STRING_MODE.contains.concat(s)}),o,{ +begin:/<<<[ \t]*(?:(\w+)|"(\w+)")\n/,end:/[ \t]*(\w+)\b/, +contains:e.QUOTE_STRING_MODE.contains.concat(s),"on:begin":(e,n)=>{ +n.data._beginMatch=e[1]||e[2]},"on:end":(e,n)=>{ +n.data._beginMatch!==e[1]&&n.ignoreMatch()}},e.END_SAME_AS_BEGIN({ +begin:/<<<[ \t]*'(\w+)'\n/,end:/[ \t]*(\w+)\b/})]},d={scope:"number",variants:[{ +begin:"\\b0[bB][01]+(?:_[01]+)*\\b"},{begin:"\\b0[oO][0-7]+(?:_[0-7]+)*\\b"},{ +begin:"\\b0[xX][\\da-fA-F]+(?:_[\\da-fA-F]+)*\\b"},{ +begin:"(?:\\b\\d+(?:_\\d+)*(\\.(?:\\d+(?:_\\d+)*))?|\\B\\.\\d+)(?:[eE][+-]?\\d+)?" +}],relevance:0 +},g=["false","null","true"],u=["__CLASS__","__DIR__","__FILE__","__FUNCTION__","__COMPILER_HALT_OFFSET__","__LINE__","__METHOD__","__NAMESPACE__","__TRAIT__","die","echo","exit","include","include_once","print","require","require_once","array","abstract","and","as","binary","bool","boolean","break","callable","case","catch","class","clone","const","continue","declare","default","do","double","else","elseif","empty","enddeclare","endfor","endforeach","endif","endswitch","endwhile","enum","eval","extends","final","finally","float","for","foreach","from","global","goto","if","implements","instanceof","insteadof","int","integer","interface","isset","iterable","list","match|0","mixed","new","never","object","or","private","protected","public","readonly","real","return","string","switch","throw","trait","try","unset","use","var","void","while","xor","yield"],b=["Error|0","AppendIterator","ArgumentCountError","ArithmeticError","ArrayIterator","ArrayObject","AssertionError","BadFunctionCallException","BadMethodCallException","CachingIterator","CallbackFilterIterator","CompileError","Countable","DirectoryIterator","DivisionByZeroError","DomainException","EmptyIterator","ErrorException","Exception","FilesystemIterator","FilterIterator","GlobIterator","InfiniteIterator","InvalidArgumentException","IteratorIterator","LengthException","LimitIterator","LogicException","MultipleIterator","NoRewindIterator","OutOfBoundsException","OutOfRangeException","OuterIterator","OverflowException","ParentIterator","ParseError","RangeException","RecursiveArrayIterator","RecursiveCachingIterator","RecursiveCallbackFilterIterator","RecursiveDirectoryIterator","RecursiveFilterIterator","RecursiveIterator","RecursiveIteratorIterator","RecursiveRegexIterator","RecursiveTreeIterator","RegexIterator","RuntimeException","SeekableIterator","SplDoublyLinkedList","SplFileInfo","SplFileObject","SplFixedArray","SplHeap","SplMaxHeap","SplMinHeap","SplObjectStorage","SplObserver","SplPriorityQueue","SplQueue","SplStack","SplSubject","SplTempFileObject","TypeError","UnderflowException","UnexpectedValueException","UnhandledMatchError","ArrayAccess","BackedEnum","Closure","Fiber","Generator","Iterator","IteratorAggregate","Serializable","Stringable","Throwable","Traversable","UnitEnum","WeakReference","WeakMap","Directory","__PHP_Incomplete_Class","parent","php_user_filter","self","static","stdClass"],m={ +keyword:u,literal:(e=>{const n=[];return e.forEach((e=>{ +n.push(e),e.toLowerCase()===e?n.push(e.toUpperCase()):n.push(e.toLowerCase()) +})),n})(g),built_in:b},p=e=>e.map((e=>e.replace(/\|\d+$/,""))),_={variants:[{ +match:[/new/,n.concat(l,"+"),n.concat("(?!",p(b).join("\\b|"),"\\b)"),i],scope:{ +1:"keyword",4:"title.class"}}]},h=n.concat(a,"\\b(?!\\()"),f={variants:[{ +match:[n.concat(/::/,n.lookahead(/(?!class\b)/)),h],scope:{2:"variable.constant" +}},{match:[/::/,/class/],scope:{2:"variable.language"}},{ +match:[i,n.concat(/::/,n.lookahead(/(?!class\b)/)),h],scope:{1:"title.class", +3:"variable.constant"}},{match:[i,n.concat("::",n.lookahead(/(?!class\b)/))], +scope:{1:"title.class"}},{match:[i,/::/,/class/],scope:{1:"title.class", +3:"variable.language"}}]},E={scope:"attr", +match:n.concat(a,n.lookahead(":"),n.lookahead(/(?!::)/))},y={relevance:0, +begin:/\(/,end:/\)/,keywords:m,contains:[E,r,f,e.C_BLOCK_COMMENT_MODE,c,d,_] +},N={relevance:0, +match:[/\b/,n.concat("(?!fn\\b|function\\b|",p(u).join("\\b|"),"|",p(b).join("\\b|"),"\\b)"),a,n.concat(l,"*"),n.lookahead(/(?=\()/)], +scope:{3:"title.function.invoke"},contains:[y]};y.contains.push(N) +;const w=[E,f,e.C_BLOCK_COMMENT_MODE,c,d,_];return{case_insensitive:!1, +keywords:m,contains:[{begin:n.concat(/#\[\s*/,i),beginScope:"meta",end:/]/, +endScope:"meta",keywords:{literal:g,keyword:["new","array"]},contains:[{ +begin:/\[/,end:/]/,keywords:{literal:g,keyword:["new","array"]}, +contains:["self",...w]},...w,{scope:"meta",match:i}] +},e.HASH_COMMENT_MODE,e.COMMENT("//","$"),e.COMMENT("/\\*","\\*/",{contains:[{ +scope:"doctag",match:"@[A-Za-z]+"}]}),{match:/__halt_compiler\(\);/, +keywords:"__halt_compiler",starts:{scope:"comment",end:e.MATCH_NOTHING_RE, +contains:[{match:/\?>/,scope:"meta",endsParent:!0}]}},{scope:"meta",variants:[{ +begin:/<\?php/,relevance:10},{begin:/<\?=/},{begin:/<\?/,relevance:.1},{ +begin:/\?>/}]},{scope:"variable.language",match:/\$this\b/},r,N,f,{ +match:[/const/,/\s/,a],scope:{1:"keyword",3:"variable.constant"}},_,{ +scope:"function",relevance:0,beginKeywords:"fn function",end:/[;{]/, +excludeEnd:!0,illegal:"[$%\\[]",contains:[{beginKeywords:"use" +},e.UNDERSCORE_TITLE_MODE,{begin:"=>",endsParent:!0},{scope:"params", +begin:"\\(",end:"\\)",excludeBegin:!0,excludeEnd:!0,keywords:m, +contains:["self",r,f,e.C_BLOCK_COMMENT_MODE,c,d]}]},{scope:"class",variants:[{ +beginKeywords:"enum",illegal:/[($"]/},{beginKeywords:"class interface trait", +illegal:/[:($"]/}],relevance:0,end:/\{/,excludeEnd:!0,contains:[{ +beginKeywords:"extends implements"},e.UNDERSCORE_TITLE_MODE]},{ +beginKeywords:"namespace",relevance:0,end:";",illegal:/[.']/, +contains:[e.inherit(e.UNDERSCORE_TITLE_MODE,{scope:"title.class"})]},{ +beginKeywords:"use",relevance:0,end:";",contains:[{ +match:/\b(as|const|function)\b/,scope:"keyword"},e.UNDERSCORE_TITLE_MODE]},c,d]} +},grmr_php_template:e=>({name:"PHP template",subLanguage:"xml",contains:[{ +begin:/<\?(php|=)?/,end:/\?>/,subLanguage:"php",contains:[{begin:"/\\*", +end:"\\*/",skip:!0},{begin:'b"',end:'"',skip:!0},{begin:"b'",end:"'",skip:!0 +},e.inherit(e.APOS_STRING_MODE,{illegal:null,className:null,contains:null, +skip:!0}),e.inherit(e.QUOTE_STRING_MODE,{illegal:null,className:null, +contains:null,skip:!0})]}]}),grmr_plaintext:e=>({name:"Plain text", +aliases:["text","txt"],disableAutodetect:!0}),grmr_python:e=>{ +const n=e.regex,t=/[\p{XID_Start}_]\p{XID_Continue}*/u,a=["and","as","assert","async","await","break","case","class","continue","def","del","elif","else","except","finally","for","from","global","if","import","in","is","lambda","match","nonlocal|10","not","or","pass","raise","return","try","while","with","yield"],i={ +$pattern:/[A-Za-z]\w+|__\w+__/,keyword:a, +built_in:["__import__","abs","all","any","ascii","bin","bool","breakpoint","bytearray","bytes","callable","chr","classmethod","compile","complex","delattr","dict","dir","divmod","enumerate","eval","exec","filter","float","format","frozenset","getattr","globals","hasattr","hash","help","hex","id","input","int","isinstance","issubclass","iter","len","list","locals","map","max","memoryview","min","next","object","oct","open","ord","pow","print","property","range","repr","reversed","round","set","setattr","slice","sorted","staticmethod","str","sum","super","tuple","type","vars","zip"], +literal:["__debug__","Ellipsis","False","None","NotImplemented","True"], +type:["Any","Callable","Coroutine","Dict","List","Literal","Generic","Optional","Sequence","Set","Tuple","Type","Union"] +},r={className:"meta",begin:/^(>>>|\.\.\.) /},s={className:"subst",begin:/\{/, +end:/\}/,keywords:i,illegal:/#/},o={begin:/\{\{/,relevance:0},l={ +className:"string",contains:[e.BACKSLASH_ESCAPE],variants:[{ +begin:/([uU]|[bB]|[rR]|[bB][rR]|[rR][bB])?'''/,end:/'''/, +contains:[e.BACKSLASH_ESCAPE,r],relevance:10},{ +begin:/([uU]|[bB]|[rR]|[bB][rR]|[rR][bB])?"""/,end:/"""/, +contains:[e.BACKSLASH_ESCAPE,r],relevance:10},{ +begin:/([fF][rR]|[rR][fF]|[fF])'''/,end:/'''/, +contains:[e.BACKSLASH_ESCAPE,r,o,s]},{begin:/([fF][rR]|[rR][fF]|[fF])"""/, +end:/"""/,contains:[e.BACKSLASH_ESCAPE,r,o,s]},{begin:/([uU]|[rR])'/,end:/'/, +relevance:10},{begin:/([uU]|[rR])"/,end:/"/,relevance:10},{ +begin:/([bB]|[bB][rR]|[rR][bB])'/,end:/'/},{begin:/([bB]|[bB][rR]|[rR][bB])"/, +end:/"/},{begin:/([fF][rR]|[rR][fF]|[fF])'/,end:/'/, +contains:[e.BACKSLASH_ESCAPE,o,s]},{begin:/([fF][rR]|[rR][fF]|[fF])"/,end:/"/, +contains:[e.BACKSLASH_ESCAPE,o,s]},e.APOS_STRING_MODE,e.QUOTE_STRING_MODE] +},c="[0-9](_?[0-9])*",d=`(\\b(${c}))?\\.(${c})|\\b(${c})\\.`,g="\\b|"+a.join("|"),u={ +className:"number",relevance:0,variants:[{ +begin:`(\\b(${c})|(${d}))[eE][+-]?(${c})[jJ]?(?=${g})`},{begin:`(${d})[jJ]?`},{ +begin:`\\b([1-9](_?[0-9])*|0+(_?0)*)[lLjJ]?(?=${g})`},{ +begin:`\\b0[bB](_?[01])+[lL]?(?=${g})`},{begin:`\\b0[oO](_?[0-7])+[lL]?(?=${g})` +},{begin:`\\b0[xX](_?[0-9a-fA-F])+[lL]?(?=${g})`},{begin:`\\b(${c})[jJ](?=${g})` +}]},b={className:"comment",begin:n.lookahead(/# type:/),end:/$/,keywords:i, +contains:[{begin:/# type:/},{begin:/#/,end:/\b\B/,endsWithParent:!0}]},m={ +className:"params",variants:[{className:"",begin:/\(\s*\)/,skip:!0},{begin:/\(/, +end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:i, +contains:["self",r,u,l,e.HASH_COMMENT_MODE]}]};return s.contains=[l,u,r],{ +name:"Python",aliases:["py","gyp","ipython"],unicodeRegex:!0,keywords:i, +illegal:/(<\/|\?)|=>/,contains:[r,u,{begin:/\bself\b/},{beginKeywords:"if", +relevance:0},l,b,e.HASH_COMMENT_MODE,{match:[/\bdef/,/\s+/,t],scope:{ +1:"keyword",3:"title.function"},contains:[m]},{variants:[{ +match:[/\bclass/,/\s+/,t,/\s*/,/\(\s*/,t,/\s*\)/]},{match:[/\bclass/,/\s+/,t]}], +scope:{1:"keyword",3:"title.class",6:"title.class.inherited"}},{ +className:"meta",begin:/^[\t ]*@/,end:/(?=#)|$/,contains:[u,m,l]}]}}, +grmr_python_repl:e=>({aliases:["pycon"],contains:[{className:"meta.prompt", +starts:{end:/ |$/,starts:{end:"$",subLanguage:"python"}},variants:[{ +begin:/^>>>(?=[ ]|$)/},{begin:/^\.\.\.(?=[ ]|$)/}]}]}),grmr_r:e=>{ +const n=e.regex,t=/(?:(?:[a-zA-Z]|\.[._a-zA-Z])[._a-zA-Z0-9]*)|\.(?!\d)/,a=n.either(/0[xX][0-9a-fA-F]+\.[0-9a-fA-F]*[pP][+-]?\d+i?/,/0[xX][0-9a-fA-F]+(?:[pP][+-]?\d+)?[Li]?/,/(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?[Li]?/),i=/[=!<>:]=|\|\||&&|:::?|<-|<<-|->>|->|\|>|[-+*\/?!$&|:<=>@^~]|\*\*/,r=n.either(/[()]/,/[{}]/,/\[\[/,/[[\]]/,/\\/,/,/) +;return{name:"R",keywords:{$pattern:t, +keyword:"function if in break next repeat else for while", +literal:"NULL NA TRUE FALSE Inf NaN NA_integer_|10 NA_real_|10 NA_character_|10 NA_complex_|10", +built_in:"LETTERS letters month.abb month.name pi T F abs acos acosh all any anyNA Arg as.call as.character as.complex as.double as.environment as.integer as.logical as.null.default as.numeric as.raw asin asinh atan atanh attr attributes baseenv browser c call ceiling class Conj cos cosh cospi cummax cummin cumprod cumsum digamma dim dimnames emptyenv exp expression floor forceAndCall gamma gc.time globalenv Im interactive invisible is.array is.atomic is.call is.character is.complex is.double is.environment is.expression is.finite is.function is.infinite is.integer is.language is.list is.logical is.matrix is.na is.name is.nan is.null is.numeric is.object is.pairlist is.raw is.recursive is.single is.symbol lazyLoadDBfetch length lgamma list log max min missing Mod names nargs nzchar oldClass on.exit pos.to.env proc.time prod quote range Re rep retracemem return round seq_along seq_len seq.int sign signif sin sinh sinpi sqrt standardGeneric substitute sum switch tan tanh tanpi tracemem trigamma trunc unclass untracemem UseMethod xtfrm" +},contains:[e.COMMENT(/#'/,/$/,{contains:[{scope:"doctag",match:/@examples/, +starts:{end:n.lookahead(n.either(/\n^#'\s*(?=@[a-zA-Z]+)/,/\n^(?!#')/)), +endsParent:!0}},{scope:"doctag",begin:"@param",end:/$/,contains:[{ +scope:"variable",variants:[{match:t},{match:/`(?:\\.|[^`\\])+`/}],endsParent:!0 +}]},{scope:"doctag",match:/@[a-zA-Z]+/},{scope:"keyword",match:/\\[a-zA-Z]+/}] +}),e.HASH_COMMENT_MODE,{scope:"string",contains:[e.BACKSLASH_ESCAPE], +variants:[e.END_SAME_AS_BEGIN({begin:/[rR]"(-*)\(/,end:/\)(-*)"/ +}),e.END_SAME_AS_BEGIN({begin:/[rR]"(-*)\{/,end:/\}(-*)"/ +}),e.END_SAME_AS_BEGIN({begin:/[rR]"(-*)\[/,end:/\](-*)"/ +}),e.END_SAME_AS_BEGIN({begin:/[rR]'(-*)\(/,end:/\)(-*)'/ +}),e.END_SAME_AS_BEGIN({begin:/[rR]'(-*)\{/,end:/\}(-*)'/ +}),e.END_SAME_AS_BEGIN({begin:/[rR]'(-*)\[/,end:/\](-*)'/}),{begin:'"',end:'"', +relevance:0},{begin:"'",end:"'",relevance:0}]},{relevance:0,variants:[{scope:{ +1:"operator",2:"number"},match:[i,a]},{scope:{1:"operator",2:"number"}, +match:[/%[^%]*%/,a]},{scope:{1:"punctuation",2:"number"},match:[r,a]},{scope:{ +2:"number"},match:[/[^a-zA-Z0-9._]|^/,a]}]},{scope:{3:"operator"}, +match:[t,/\s+/,/<-/,/\s+/]},{scope:"operator",relevance:0,variants:[{match:i},{ +match:/%[^%]*%/}]},{scope:"punctuation",relevance:0,match:r},{begin:"`",end:"`", +contains:[{begin:/\\./}]}]}},grmr_ruby:e=>{ +const n=e.regex,t="([a-zA-Z_]\\w*[!?=]?|[-+~]@|<<|>>|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?)",a=n.either(/\b([A-Z]+[a-z0-9]+)+/,/\b([A-Z]+[a-z0-9]+)+[A-Z]+/),i=n.concat(a,/(::\w+)*/),r={ +"variable.constant":["__FILE__","__LINE__","__ENCODING__"], +"variable.language":["self","super"], +keyword:["alias","and","begin","BEGIN","break","case","class","defined","do","else","elsif","end","END","ensure","for","if","in","module","next","not","or","redo","require","rescue","retry","return","then","undef","unless","until","when","while","yield","include","extend","prepend","public","private","protected","raise","throw"], +built_in:["proc","lambda","attr_accessor","attr_reader","attr_writer","define_method","private_constant","module_function"], +literal:["true","false","nil"]},s={className:"doctag",begin:"@[A-Za-z]+"},o={ +begin:"#<",end:">"},l=[e.COMMENT("#","$",{contains:[s] +}),e.COMMENT("^=begin","^=end",{contains:[s],relevance:10 +}),e.COMMENT("^__END__",e.MATCH_NOTHING_RE)],c={className:"subst",begin:/#\{/, +end:/\}/,keywords:r},d={className:"string",contains:[e.BACKSLASH_ESCAPE,c], +variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/`/,end:/`/},{ +begin:/%[qQwWx]?\(/,end:/\)/},{begin:/%[qQwWx]?\[/,end:/\]/},{ +begin:/%[qQwWx]?\{/,end:/\}/},{begin:/%[qQwWx]?/},{begin:/%[qQwWx]?\//, +end:/\//},{begin:/%[qQwWx]?%/,end:/%/},{begin:/%[qQwWx]?-/,end:/-/},{ +begin:/%[qQwWx]?\|/,end:/\|/},{begin:/\B\?(\\\d{1,3})/},{ +begin:/\B\?(\\x[A-Fa-f0-9]{1,2})/},{begin:/\B\?(\\u\{?[A-Fa-f0-9]{1,6}\}?)/},{ +begin:/\B\?(\\M-\\C-|\\M-\\c|\\c\\M-|\\M-|\\C-\\M-)[\x20-\x7e]/},{ +begin:/\B\?\\(c|C-)[\x20-\x7e]/},{begin:/\B\?\\?\S/},{ +begin:n.concat(/<<[-~]?'?/,n.lookahead(/(\w+)(?=\W)[^\n]*\n(?:[^\n]*\n)*?\s*\1\b/)), +contains:[e.END_SAME_AS_BEGIN({begin:/(\w+)/,end:/(\w+)/, +contains:[e.BACKSLASH_ESCAPE,c]})]}]},g="[0-9](_?[0-9])*",u={className:"number", +relevance:0,variants:[{ +begin:`\\b([1-9](_?[0-9])*|0)(\\.(${g}))?([eE][+-]?(${g})|r)?i?\\b`},{ +begin:"\\b0[dD][0-9](_?[0-9])*r?i?\\b"},{begin:"\\b0[bB][0-1](_?[0-1])*r?i?\\b" +},{begin:"\\b0[oO][0-7](_?[0-7])*r?i?\\b"},{ +begin:"\\b0[xX][0-9a-fA-F](_?[0-9a-fA-F])*r?i?\\b"},{ +begin:"\\b0(_?[0-7])+r?i?\\b"}]},b={variants:[{match:/\(\)/},{ +className:"params",begin:/\(/,end:/(?=\))/,excludeBegin:!0,endsParent:!0, +keywords:r}]},m=[d,{variants:[{match:[/class\s+/,i,/\s+<\s+/,i]},{ +match:[/\b(class|module)\s+/,i]}],scope:{2:"title.class", +4:"title.class.inherited"},keywords:r},{match:[/(include|extend)\s+/,i],scope:{ +2:"title.class"},keywords:r},{relevance:0,match:[i,/\.new[. (]/],scope:{ +1:"title.class"}},{relevance:0,match:/\b[A-Z][A-Z_0-9]+\b/, +className:"variable.constant"},{relevance:0,match:a,scope:"title.class"},{ +match:[/def/,/\s+/,t],scope:{1:"keyword",3:"title.function"},contains:[b]},{ +begin:e.IDENT_RE+"::"},{className:"symbol", +begin:e.UNDERSCORE_IDENT_RE+"(!|\\?)?:",relevance:0},{className:"symbol", +begin:":(?!\\s)",contains:[d,{begin:t}],relevance:0},u,{className:"variable", +begin:"(\\$\\W)|((\\$|@@?)(\\w+))(?=[^@$?])(?![A-Za-z])(?![@$?'])"},{ +className:"params",begin:/\|/,end:/\|/,excludeBegin:!0,excludeEnd:!0, +relevance:0,keywords:r},{begin:"("+e.RE_STARTERS_RE+"|unless)\\s*", +keywords:"unless",contains:[{className:"regexp",contains:[e.BACKSLASH_ESCAPE,c], +illegal:/\n/,variants:[{begin:"/",end:"/[a-z]*"},{begin:/%r\{/,end:/\}[a-z]*/},{ +begin:"%r\\(",end:"\\)[a-z]*"},{begin:"%r!",end:"![a-z]*"},{begin:"%r\\[", +end:"\\][a-z]*"}]}].concat(o,l),relevance:0}].concat(o,l) +;c.contains=m,b.contains=m;const p=[{begin:/^\s*=>/,starts:{end:"$",contains:m} +},{className:"meta.prompt", +begin:"^([>?]>|[\\w#]+\\(\\w+\\):\\d+:\\d+[>*]|(\\w+-)?\\d+\\.\\d+\\.\\d+(p\\d+)?[^\\d][^>]+>)(?=[ ])", +starts:{end:"$",keywords:r,contains:m}}];return l.unshift(o),{name:"Ruby", +aliases:["rb","gemspec","podspec","thor","irb"],keywords:r,illegal:/\/\*/, +contains:[e.SHEBANG({binary:"ruby"})].concat(p).concat(l).concat(m)}}, +grmr_rust:e=>{const n=e.regex,t={className:"title.function.invoke",relevance:0, +begin:n.concat(/\b/,/(?!let|for|while|if|else|match\b)/,e.IDENT_RE,n.lookahead(/\s*\(/)) +},a="([ui](8|16|32|64|128|size)|f(32|64))?",i=["drop ","Copy","Send","Sized","Sync","Drop","Fn","FnMut","FnOnce","ToOwned","Clone","Debug","PartialEq","PartialOrd","Eq","Ord","AsRef","AsMut","Into","From","Default","Iterator","Extend","IntoIterator","DoubleEndedIterator","ExactSizeIterator","SliceConcatExt","ToString","assert!","assert_eq!","bitflags!","bytes!","cfg!","col!","concat!","concat_idents!","debug_assert!","debug_assert_eq!","env!","eprintln!","panic!","file!","format!","format_args!","include_bytes!","include_str!","line!","local_data_key!","module_path!","option_env!","print!","println!","select!","stringify!","try!","unimplemented!","unreachable!","vec!","write!","writeln!","macro_rules!","assert_ne!","debug_assert_ne!"],r=["i8","i16","i32","i64","i128","isize","u8","u16","u32","u64","u128","usize","f32","f64","str","char","bool","Box","Option","Result","String","Vec"] +;return{name:"Rust",aliases:["rs"],keywords:{$pattern:e.IDENT_RE+"!?",type:r, +keyword:["abstract","as","async","await","become","box","break","const","continue","crate","do","dyn","else","enum","extern","false","final","fn","for","if","impl","in","let","loop","macro","match","mod","move","mut","override","priv","pub","ref","return","self","Self","static","struct","super","trait","true","try","type","typeof","unsafe","unsized","use","virtual","where","while","yield"], +literal:["true","false","Some","None","Ok","Err"],built_in:i},illegal:""},t]}}, +grmr_scss:e=>{const n=ie(e),t=le,a=oe,i="@[a-z-]+",r={className:"variable", +begin:"(\\$[a-zA-Z-][a-zA-Z0-9_-]*)\\b",relevance:0};return{name:"SCSS", +case_insensitive:!0,illegal:"[=/|']", +contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,n.CSS_NUMBER_MODE,{ +className:"selector-id",begin:"#[A-Za-z0-9_-]+",relevance:0},{ +className:"selector-class",begin:"\\.[A-Za-z0-9_-]+",relevance:0 +},n.ATTRIBUTE_SELECTOR_MODE,{className:"selector-tag", +begin:"\\b("+re.join("|")+")\\b",relevance:0},{className:"selector-pseudo", +begin:":("+a.join("|")+")"},{className:"selector-pseudo", +begin:":(:)?("+t.join("|")+")"},r,{begin:/\(/,end:/\)/, +contains:[n.CSS_NUMBER_MODE]},n.CSS_VARIABLE,{className:"attribute", +begin:"\\b("+ce.join("|")+")\\b"},{ +begin:"\\b(whitespace|wait|w-resize|visible|vertical-text|vertical-ideographic|uppercase|upper-roman|upper-alpha|underline|transparent|top|thin|thick|text|text-top|text-bottom|tb-rl|table-header-group|table-footer-group|sw-resize|super|strict|static|square|solid|small-caps|separate|se-resize|scroll|s-resize|rtl|row-resize|ridge|right|repeat|repeat-y|repeat-x|relative|progress|pointer|overline|outside|outset|oblique|nowrap|not-allowed|normal|none|nw-resize|no-repeat|no-drop|newspaper|ne-resize|n-resize|move|middle|medium|ltr|lr-tb|lowercase|lower-roman|lower-alpha|loose|list-item|line|line-through|line-edge|lighter|left|keep-all|justify|italic|inter-word|inter-ideograph|inside|inset|inline|inline-block|inherit|inactive|ideograph-space|ideograph-parenthesis|ideograph-numeric|ideograph-alpha|horizontal|hidden|help|hand|groove|fixed|ellipsis|e-resize|double|dotted|distribute|distribute-space|distribute-letter|distribute-all-lines|disc|disabled|default|decimal|dashed|crosshair|collapse|col-resize|circle|char|center|capitalize|break-word|break-all|bottom|both|bolder|bold|block|bidi-override|below|baseline|auto|always|all-scroll|absolute|table|table-cell)\\b" +},{begin:/:/,end:/[;}{]/,relevance:0, +contains:[n.BLOCK_COMMENT,r,n.HEXCOLOR,n.CSS_NUMBER_MODE,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,n.IMPORTANT,n.FUNCTION_DISPATCH] +},{begin:"@(page|font-face)",keywords:{$pattern:i,keyword:"@page @font-face"}},{ +begin:"@",end:"[{;]",returnBegin:!0,keywords:{$pattern:/[a-z-]+/, +keyword:"and or not only",attribute:se.join(" ")},contains:[{begin:i, +className:"keyword"},{begin:/[a-z-]+(?=:)/,className:"attribute" +},r,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,n.HEXCOLOR,n.CSS_NUMBER_MODE] +},n.FUNCTION_DISPATCH]}},grmr_shell:e=>({name:"Shell Session", +aliases:["console","shellsession"],contains:[{className:"meta.prompt", +begin:/^\s{0,3}[/~\w\d[\]()@-]*[>%$#][ ]?/,starts:{end:/[^\\](?=\s*$)/, +subLanguage:"bash"}}]}),grmr_sql:e=>{ +const n=e.regex,t=e.COMMENT("--","$"),a=["true","false","unknown"],i=["bigint","binary","blob","boolean","char","character","clob","date","dec","decfloat","decimal","float","int","integer","interval","nchar","nclob","national","numeric","real","row","smallint","time","timestamp","varchar","varying","varbinary"],r=["abs","acos","array_agg","asin","atan","avg","cast","ceil","ceiling","coalesce","corr","cos","cosh","count","covar_pop","covar_samp","cume_dist","dense_rank","deref","element","exp","extract","first_value","floor","json_array","json_arrayagg","json_exists","json_object","json_objectagg","json_query","json_table","json_table_primitive","json_value","lag","last_value","lead","listagg","ln","log","log10","lower","max","min","mod","nth_value","ntile","nullif","percent_rank","percentile_cont","percentile_disc","position","position_regex","power","rank","regr_avgx","regr_avgy","regr_count","regr_intercept","regr_r2","regr_slope","regr_sxx","regr_sxy","regr_syy","row_number","sin","sinh","sqrt","stddev_pop","stddev_samp","substring","substring_regex","sum","tan","tanh","translate","translate_regex","treat","trim","trim_array","unnest","upper","value_of","var_pop","var_samp","width_bucket"],s=["create table","insert into","primary key","foreign key","not null","alter table","add constraint","grouping sets","on overflow","character set","respect nulls","ignore nulls","nulls first","nulls last","depth first","breadth first"],o=r,l=["abs","acos","all","allocate","alter","and","any","are","array","array_agg","array_max_cardinality","as","asensitive","asin","asymmetric","at","atan","atomic","authorization","avg","begin","begin_frame","begin_partition","between","bigint","binary","blob","boolean","both","by","call","called","cardinality","cascaded","case","cast","ceil","ceiling","char","char_length","character","character_length","check","classifier","clob","close","coalesce","collate","collect","column","commit","condition","connect","constraint","contains","convert","copy","corr","corresponding","cos","cosh","count","covar_pop","covar_samp","create","cross","cube","cume_dist","current","current_catalog","current_date","current_default_transform_group","current_path","current_role","current_row","current_schema","current_time","current_timestamp","current_path","current_role","current_transform_group_for_type","current_user","cursor","cycle","date","day","deallocate","dec","decimal","decfloat","declare","default","define","delete","dense_rank","deref","describe","deterministic","disconnect","distinct","double","drop","dynamic","each","element","else","empty","end","end_frame","end_partition","end-exec","equals","escape","every","except","exec","execute","exists","exp","external","extract","false","fetch","filter","first_value","float","floor","for","foreign","frame_row","free","from","full","function","fusion","get","global","grant","group","grouping","groups","having","hold","hour","identity","in","indicator","initial","inner","inout","insensitive","insert","int","integer","intersect","intersection","interval","into","is","join","json_array","json_arrayagg","json_exists","json_object","json_objectagg","json_query","json_table","json_table_primitive","json_value","lag","language","large","last_value","lateral","lead","leading","left","like","like_regex","listagg","ln","local","localtime","localtimestamp","log","log10","lower","match","match_number","match_recognize","matches","max","member","merge","method","min","minute","mod","modifies","module","month","multiset","national","natural","nchar","nclob","new","no","none","normalize","not","nth_value","ntile","null","nullif","numeric","octet_length","occurrences_regex","of","offset","old","omit","on","one","only","open","or","order","out","outer","over","overlaps","overlay","parameter","partition","pattern","per","percent","percent_rank","percentile_cont","percentile_disc","period","portion","position","position_regex","power","precedes","precision","prepare","primary","procedure","ptf","range","rank","reads","real","recursive","ref","references","referencing","regr_avgx","regr_avgy","regr_count","regr_intercept","regr_r2","regr_slope","regr_sxx","regr_sxy","regr_syy","release","result","return","returns","revoke","right","rollback","rollup","row","row_number","rows","running","savepoint","scope","scroll","search","second","seek","select","sensitive","session_user","set","show","similar","sin","sinh","skip","smallint","some","specific","specifictype","sql","sqlexception","sqlstate","sqlwarning","sqrt","start","static","stddev_pop","stddev_samp","submultiset","subset","substring","substring_regex","succeeds","sum","symmetric","system","system_time","system_user","table","tablesample","tan","tanh","then","time","timestamp","timezone_hour","timezone_minute","to","trailing","translate","translate_regex","translation","treat","trigger","trim","trim_array","true","truncate","uescape","union","unique","unknown","unnest","update","upper","user","using","value","values","value_of","var_pop","var_samp","varbinary","varchar","varying","versioning","when","whenever","where","width_bucket","window","with","within","without","year","add","asc","collation","desc","final","first","last","view"].filter((e=>!r.includes(e))),c={ +begin:n.concat(/\b/,n.either(...o),/\s*\(/),relevance:0,keywords:{built_in:o}} +;return{name:"SQL",case_insensitive:!0,illegal:/[{}]|<\//,keywords:{ +$pattern:/\b[\w\.]+/,keyword:((e,{exceptions:n,when:t}={})=>{const a=t +;return n=n||[],e.map((e=>e.match(/\|\d+$/)||n.includes(e)?e:a(e)?e+"|0":e)) +})(l,{when:e=>e.length<3}),literal:a,type:i, +built_in:["current_catalog","current_date","current_default_transform_group","current_path","current_role","current_schema","current_transform_group_for_type","current_user","session_user","system_time","system_user","current_time","localtime","current_timestamp","localtimestamp"] +},contains:[{begin:n.either(...s),relevance:0,keywords:{$pattern:/[\w\.]+/, +keyword:l.concat(s),literal:a,type:i}},{className:"type", +begin:n.either("double precision","large object","with timezone","without timezone") +},c,{className:"variable",begin:/@[a-z0-9][a-z0-9_]*/},{className:"string", +variants:[{begin:/'/,end:/'/,contains:[{begin:/''/}]}]},{begin:/"/,end:/"/, +contains:[{begin:/""/}]},e.C_NUMBER_MODE,e.C_BLOCK_COMMENT_MODE,t,{ +className:"operator",begin:/[-+*/=%^~]|&&?|\|\|?|!=?|<(?:=>?|<|>)?|>[>=]?/, +relevance:0}]}},grmr_swift:e=>{const n={match:/\s+/,relevance:0 +},t=e.COMMENT("/\\*","\\*/",{contains:["self"]}),a=[e.C_LINE_COMMENT_MODE,t],i={ +match:[/\./,m(...xe,...Me)],className:{2:"keyword"}},r={match:b(/\./,m(...Ae)), +relevance:0},s=Ae.filter((e=>"string"==typeof e)).concat(["_|0"]),o={variants:[{ +className:"keyword", +match:m(...Ae.filter((e=>"string"!=typeof e)).concat(Se).map(ke),...Me)}]},l={ +$pattern:m(/\b\w+/,/#\w+/),keyword:s.concat(Re),literal:Ce},c=[i,r,o],g=[{ +match:b(/\./,m(...De)),relevance:0},{className:"built_in", +match:b(/\b/,m(...De),/(?=\()/)}],u={match:/->/,relevance:0},p=[u,{ +className:"operator",relevance:0,variants:[{match:Be},{match:`\\.(\\.|${Le})+`}] +}],_="([0-9]_*)+",h="([0-9a-fA-F]_*)+",f={className:"number",relevance:0, +variants:[{match:`\\b(${_})(\\.(${_}))?([eE][+-]?(${_}))?\\b`},{ +match:`\\b0x(${h})(\\.(${h}))?([pP][+-]?(${_}))?\\b`},{match:/\b0o([0-7]_*)+\b/ +},{match:/\b0b([01]_*)+\b/}]},E=(e="")=>({className:"subst",variants:[{ +match:b(/\\/,e,/[0\\tnr"']/)},{match:b(/\\/,e,/u\{[0-9a-fA-F]{1,8}\}/)}] +}),y=(e="")=>({className:"subst",match:b(/\\/,e,/[\t ]*(?:[\r\n]|\r\n)/) +}),N=(e="")=>({className:"subst",label:"interpol",begin:b(/\\/,e,/\(/),end:/\)/ +}),w=(e="")=>({begin:b(e,/"""/),end:b(/"""/,e),contains:[E(e),y(e),N(e)] +}),v=(e="")=>({begin:b(e,/"/),end:b(/"/,e),contains:[E(e),N(e)]}),O={ +className:"string", +variants:[w(),w("#"),w("##"),w("###"),v(),v("#"),v("##"),v("###")] +},k=[e.BACKSLASH_ESCAPE,{begin:/\[/,end:/\]/,relevance:0, +contains:[e.BACKSLASH_ESCAPE]}],x={begin:/\/[^\s](?=[^/\n]*\/)/,end:/\//, +contains:k},M=e=>{const n=b(e,/\//),t=b(/\//,e);return{begin:n,end:t, +contains:[...k,{scope:"comment",begin:`#(?!.*${t})`,end:/$/}]}},S={ +scope:"regexp",variants:[M("###"),M("##"),M("#"),x]},A={match:b(/`/,Fe,/`/) +},C=[A,{className:"variable",match:/\$\d+/},{className:"variable", +match:`\\$${ze}+`}],T=[{match:/(@|#(un)?)available/,scope:"keyword",starts:{ +contains:[{begin:/\(/,end:/\)/,keywords:Pe,contains:[...p,f,O]}]}},{ +scope:"keyword",match:b(/@/,m(...je))},{scope:"meta",match:b(/@/,Fe)}],R={ +match:d(/\b[A-Z]/),relevance:0,contains:[{className:"type", +match:b(/(AV|CA|CF|CG|CI|CL|CM|CN|CT|MK|MP|MTK|MTL|NS|SCN|SK|UI|WK|XC)/,ze,"+") +},{className:"type",match:Ue,relevance:0},{match:/[?!]+/,relevance:0},{ +match:/\.\.\./,relevance:0},{match:b(/\s+&\s+/,d(Ue)),relevance:0}]},D={ +begin://,keywords:l,contains:[...a,...c,...T,u,R]};R.contains.push(D) +;const I={begin:/\(/,end:/\)/,relevance:0,keywords:l,contains:["self",{ +match:b(Fe,/\s*:/),keywords:"_|0",relevance:0 +},...a,S,...c,...g,...p,f,O,...C,...T,R]},L={begin://, +keywords:"repeat each",contains:[...a,R]},B={begin:/\(/,end:/\)/,keywords:l, +contains:[{begin:m(d(b(Fe,/\s*:/)),d(b(Fe,/\s+/,Fe,/\s*:/))),end:/:/, +relevance:0,contains:[{className:"keyword",match:/\b_\b/},{className:"params", +match:Fe}]},...a,...c,...p,f,O,...T,R,I],endsParent:!0,illegal:/["']/},$={ +match:[/(func|macro)/,/\s+/,m(A.match,Fe,Be)],className:{1:"keyword", +3:"title.function"},contains:[L,B,n],illegal:[/\[/,/%/]},z={ +match:[/\b(?:subscript|init[?!]?)/,/\s*(?=[<(])/],className:{1:"keyword"}, +contains:[L,B,n],illegal:/\[|%/},F={match:[/operator/,/\s+/,Be],className:{ +1:"keyword",3:"title"}},U={begin:[/precedencegroup/,/\s+/,Ue],className:{ +1:"keyword",3:"title"},contains:[R],keywords:[...Te,...Ce],end:/}/} +;for(const e of O.variants){const n=e.contains.find((e=>"interpol"===e.label)) +;n.keywords=l;const t=[...c,...g,...p,f,O,...C];n.contains=[...t,{begin:/\(/, +end:/\)/,contains:["self",...t]}]}return{name:"Swift",keywords:l, +contains:[...a,$,z,{beginKeywords:"struct protocol class extension enum actor", +end:"\\{",excludeEnd:!0,keywords:l,contains:[e.inherit(e.TITLE_MODE,{ +className:"title.class",begin:/[A-Za-z$_][\u00C0-\u02B80-9A-Za-z$_]*/}),...c] +},F,U,{beginKeywords:"import",end:/$/,contains:[...a],relevance:0 +},S,...c,...g,...p,f,O,...C,...T,R,I]}},grmr_typescript:e=>{ +const n=Oe(e),t=_e,a=["any","void","number","boolean","string","object","never","symbol","bigint","unknown"],i={ +beginKeywords:"namespace",end:/\{/,excludeEnd:!0, +contains:[n.exports.CLASS_REFERENCE]},r={beginKeywords:"interface",end:/\{/, +excludeEnd:!0,keywords:{keyword:"interface extends",built_in:a}, +contains:[n.exports.CLASS_REFERENCE]},s={$pattern:_e, +keyword:he.concat(["type","namespace","interface","public","private","protected","implements","declare","abstract","readonly","enum","override"]), +literal:fe,built_in:ve.concat(a),"variable.language":we},o={className:"meta", +begin:"@"+t},l=(e,n,t)=>{const a=e.contains.findIndex((e=>e.label===n)) +;if(-1===a)throw Error("can not find mode to replace");e.contains.splice(a,1,t)} +;return Object.assign(n.keywords,s), +n.exports.PARAMS_CONTAINS.push(o),n.contains=n.contains.concat([o,i,r]), +l(n,"shebang",e.SHEBANG()),l(n,"use_strict",{className:"meta",relevance:10, +begin:/^\s*['"]use strict['"]/ +}),n.contains.find((e=>"func.def"===e.label)).relevance=0,Object.assign(n,{ +name:"TypeScript",aliases:["ts","tsx","mts","cts"]}),n},grmr_vbnet:e=>{ +const n=e.regex,t=/\d{1,2}\/\d{1,2}\/\d{4}/,a=/\d{4}-\d{1,2}-\d{1,2}/,i=/(\d|1[012])(:\d+){0,2} *(AM|PM)/,r=/\d{1,2}(:\d{1,2}){1,2}/,s={ +className:"literal",variants:[{begin:n.concat(/# */,n.either(a,t),/ *#/)},{ +begin:n.concat(/# */,r,/ *#/)},{begin:n.concat(/# */,i,/ *#/)},{ +begin:n.concat(/# */,n.either(a,t),/ +/,n.either(i,r),/ *#/)}] +},o=e.COMMENT(/'''/,/$/,{contains:[{className:"doctag",begin:/<\/?/,end:/>/}] +}),l=e.COMMENT(null,/$/,{variants:[{begin:/'/},{begin:/([\t ]|^)REM(?=\s)/}]}) +;return{name:"Visual Basic .NET",aliases:["vb"],case_insensitive:!0, +classNameAliases:{label:"symbol"},keywords:{ +keyword:"addhandler alias aggregate ansi as async assembly auto binary by byref byval call case catch class compare const continue custom declare default delegate dim distinct do each equals else elseif end enum erase error event exit explicit finally for friend from function get global goto group handles if implements imports in inherits interface into iterator join key let lib loop me mid module mustinherit mustoverride mybase myclass namespace narrowing new next notinheritable notoverridable of off on operator option optional order overloads overridable overrides paramarray partial preserve private property protected public raiseevent readonly redim removehandler resume return select set shadows shared skip static step stop structure strict sub synclock take text then throw to try unicode until using when where while widening with withevents writeonly yield", +built_in:"addressof and andalso await directcast gettype getxmlnamespace is isfalse isnot istrue like mod nameof new not or orelse trycast typeof xor cbool cbyte cchar cdate cdbl cdec cint clng cobj csbyte cshort csng cstr cuint culng cushort", +type:"boolean byte char date decimal double integer long object sbyte short single string uinteger ulong ushort", +literal:"true false nothing"}, +illegal:"//|\\{|\\}|endif|gosub|variant|wend|^\\$ ",contains:[{ +className:"string",begin:/"(""|[^/n])"C\b/},{className:"string",begin:/"/, +end:/"/,illegal:/\n/,contains:[{begin:/""/}]},s,{className:"number",relevance:0, +variants:[{begin:/\b\d[\d_]*((\.[\d_]+(E[+-]?[\d_]+)?)|(E[+-]?[\d_]+))[RFD@!#]?/ +},{begin:/\b\d[\d_]*((U?[SIL])|[%&])?/},{begin:/&H[\dA-F_]+((U?[SIL])|[%&])?/},{ +begin:/&O[0-7_]+((U?[SIL])|[%&])?/},{begin:/&B[01_]+((U?[SIL])|[%&])?/}]},{ +className:"label",begin:/^\w+:/},o,l,{className:"meta", +begin:/[\t ]*#(const|disable|else|elseif|enable|end|externalsource|if|region)\b/, +end:/$/,keywords:{ +keyword:"const disable else elseif enable end externalsource if region then"}, +contains:[l]}]}},grmr_wasm:e=>{e.regex;const n=e.COMMENT(/\(;/,/;\)/) +;return n.contains.push("self"),{name:"WebAssembly",keywords:{$pattern:/[\w.]+/, +keyword:["anyfunc","block","br","br_if","br_table","call","call_indirect","data","drop","elem","else","end","export","func","global.get","global.set","local.get","local.set","local.tee","get_global","get_local","global","if","import","local","loop","memory","memory.grow","memory.size","module","mut","nop","offset","param","result","return","select","set_global","set_local","start","table","tee_local","then","type","unreachable"] +},contains:[e.COMMENT(/;;/,/$/),n,{match:[/(?:offset|align)/,/\s*/,/=/], +className:{1:"keyword",3:"operator"}},{className:"variable",begin:/\$[\w_]+/},{ +match:/(\((?!;)|\))+/,className:"punctuation",relevance:0},{ +begin:[/(?:func|call|call_indirect)/,/\s+/,/\$[^\s)]+/],className:{1:"keyword", +3:"title.function"}},e.QUOTE_STRING_MODE,{match:/(i32|i64|f32|f64)(?!\.)/, +className:"type"},{className:"keyword", +match:/\b(f32|f64|i32|i64)(?:\.(?:abs|add|and|ceil|clz|const|convert_[su]\/i(?:32|64)|copysign|ctz|demote\/f64|div(?:_[su])?|eqz?|extend_[su]\/i32|floor|ge(?:_[su])?|gt(?:_[su])?|le(?:_[su])?|load(?:(?:8|16|32)_[su])?|lt(?:_[su])?|max|min|mul|nearest|neg?|or|popcnt|promote\/f32|reinterpret\/[fi](?:32|64)|rem_[su]|rot[lr]|shl|shr_[su]|store(?:8|16|32)?|sqrt|sub|trunc(?:_[su]\/f(?:32|64))?|wrap\/i64|xor))\b/ +},{className:"number",relevance:0, +match:/[+-]?\b(?:\d(?:_?\d)*(?:\.\d(?:_?\d)*)?(?:[eE][+-]?\d(?:_?\d)*)?|0x[\da-fA-F](?:_?[\da-fA-F])*(?:\.[\da-fA-F](?:_?[\da-fA-D])*)?(?:[pP][+-]?\d(?:_?\d)*)?)\b|\binf\b|\bnan(?::0x[\da-fA-F](?:_?[\da-fA-D])*)?\b/ +}]}},grmr_xml:e=>{ +const n=e.regex,t=n.concat(/[\p{L}_]/u,n.optional(/[\p{L}0-9_.-]*:/u),/[\p{L}0-9_.-]*/u),a={ +className:"symbol",begin:/&[a-z]+;|&#[0-9]+;|&#x[a-f0-9]+;/},i={begin:/\s/, +contains:[{className:"keyword",begin:/#?[a-z_][a-z1-9_-]+/,illegal:/\n/}] +},r=e.inherit(i,{begin:/\(/,end:/\)/}),s=e.inherit(e.APOS_STRING_MODE,{ +className:"string"}),o=e.inherit(e.QUOTE_STRING_MODE,{className:"string"}),l={ +endsWithParent:!0,illegal:/`]+/}]}]}]};return{ +name:"HTML, XML", +aliases:["html","xhtml","rss","atom","xjb","xsd","xsl","plist","wsf","svg"], +case_insensitive:!0,unicodeRegex:!0,contains:[{className:"meta",begin://,relevance:10,contains:[i,o,s,r,{begin:/\[/,end:/\]/,contains:[{ +className:"meta",begin://,contains:[i,r,o,s]}]}] +},e.COMMENT(//,{relevance:10}),{begin://, +relevance:10},a,{className:"meta",end:/\?>/,variants:[{begin:/<\?xml/, +relevance:10,contains:[o]},{begin:/<\?[a-z][a-z0-9]+/}]},{className:"tag", +begin:/)/,end:/>/,keywords:{name:"style"},contains:[l],starts:{ +end:/<\/style>/,returnEnd:!0,subLanguage:["css","xml"]}},{className:"tag", +begin:/)/,end:/>/,keywords:{name:"script"},contains:[l],starts:{ +end:/<\/script>/,returnEnd:!0,subLanguage:["javascript","handlebars","xml"]}},{ +className:"tag",begin:/<>|<\/>/},{className:"tag", +begin:n.concat(//,/>/,/\s/)))), +end:/\/?>/,contains:[{className:"name",begin:t,relevance:0,starts:l}]},{ +className:"tag",begin:n.concat(/<\//,n.lookahead(n.concat(t,/>/))),contains:[{ +className:"name",begin:t,relevance:0},{begin:/>/,relevance:0,endsParent:!0}]}]} +},grmr_yaml:e=>{ +const n="true false yes no null",t="[\\w#;/?:@&=+$,.~*'()[\\]]+",a={ +className:"string",relevance:0,variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/ +},{begin:/\S+/}],contains:[e.BACKSLASH_ESCAPE,{className:"template-variable", +variants:[{begin:/\{\{/,end:/\}\}/},{begin:/%\{/,end:/\}/}]}]},i=e.inherit(a,{ +variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/[^\s,{}[\]]+/}]}),r={ +end:",",endsWithParent:!0,excludeEnd:!0,keywords:n,relevance:0},s={begin:/\{/, +end:/\}/,contains:[r],illegal:"\\n",relevance:0},o={begin:"\\[",end:"\\]", +contains:[r],illegal:"\\n",relevance:0},l=[{className:"attr",variants:[{ +begin:"\\w[\\w :\\/.-]*:(?=[ \t]|$)"},{begin:'"\\w[\\w :\\/.-]*":(?=[ \t]|$)'},{ +begin:"'\\w[\\w :\\/.-]*':(?=[ \t]|$)"}]},{className:"meta",begin:"^---\\s*$", +relevance:10},{className:"string", +begin:"[\\|>]([1-9]?[+-])?[ ]*\\n( +)[^ ][^\\n]*\\n(\\2[^\\n]+\\n?)*"},{ +begin:"<%[%=-]?",end:"[%-]?%>",subLanguage:"ruby",excludeBegin:!0,excludeEnd:!0, +relevance:0},{className:"type",begin:"!\\w+!"+t},{className:"type", +begin:"!<"+t+">"},{className:"type",begin:"!"+t},{className:"type",begin:"!!"+t +},{className:"meta",begin:"&"+e.UNDERSCORE_IDENT_RE+"$"},{className:"meta", +begin:"\\*"+e.UNDERSCORE_IDENT_RE+"$"},{className:"bullet",begin:"-(?=[ ]|$)", +relevance:0},e.HASH_COMMENT_MODE,{beginKeywords:n,keywords:{literal:n}},{ +className:"number", +begin:"\\b[0-9]{4}(-[0-9][0-9]){0,2}([Tt \\t][0-9][0-9]?(:[0-9][0-9]){2})?(\\.[0-9]*)?([ \\t])*(Z|[-+][0-9][0-9]?(:[0-9][0-9])?)?\\b" +},{className:"number",begin:e.C_NUMBER_RE+"\\b",relevance:0},s,o,a],c=[...l] +;return c.pop(),c.push(i),r.contains=c,{name:"YAML",case_insensitive:!0, +aliases:["yml"],contains:l}}});const He=ae;for(const e of Object.keys(Ke)){ +const n=e.replace("grmr_","").replace("_","-");He.registerLanguage(n,Ke[e])} +return He}() +;"object"==typeof exports&&"undefined"!=typeof module&&(module.exports=hljs); \ No newline at end of file diff --git a/static/style.css b/static/style.css new file mode 100644 index 0000000..24146d4 --- /dev/null +++ b/static/style.css @@ -0,0 +1,384 @@ +:root { + --dark-blue: hsl(214, 93%, 42%); + --light-blue: hsl(214, 93%, 78%); + --blue: hsl(214, 93%, 62%); + --light-gray: #f2f2f2; + --gray: #808080; + --xrcf-title: #333; + --black: #000000; + --flame: #fa7a02; + --medium: 18px; + --text: #000000; + --background: hsl(230, 60%, 98%); + --code-background: hsl(230, 60%, 98%); + --code-border: #dbdbdb; + --border: #eaeaea; + --blue-gradient: linear-gradient(to right, var(--dark-blue), var(--blue)); +} + +@media (prefers-color-scheme: dark) { + :root { + --blue-gradient: linear-gradient(to right, var(--blue), var(--light-blue)); + --background: hsl(0, 0%, 20%); + --border: #4e4e4e; + --code-background: hsl(0, 0%, 5%); + --code-border: #0e0e0e; + --text: #ddd; + --xrcf-title: #d1cdcd; + } +} + +@font-face { + font-family: 'Source Code Pro'; + font-style: normal; + font-weight: 400; + src: url(/font/source-code-pro-latin.woff2) format('woff2'); +} + +@font-face { + font-family: 'Roboto'; + font-style: normal; + font-weight: 400; + src: url(/font/roboto-latin.woff2) format('woff2'); +} + +body { + font-family: 'Roboto', sans-serif; + line-height: 150%; + background-color: var(--background); + color: var(--text); +} + +h1, h2, h3, h4, h5, h6 { + font-family: sans-serif; + line-height: 120%; +} + +.content { + margin: 6px; + padding-top: 60px; + padding-bottom: 20px; +} + +a { + text-decoration: underline; + color: var(--blue); +} + +.menu { + margin: 0px; + padding-bottom: 10px; + height: 30px; + border-bottom: 1px solid var(--border); + width: 100%; + background-color: var(--background); + position: fixed; + top: 0; + left: 0; +} + +.menu a { + text-align: center; + padding-left: 16px; + padding-right: 16px; + text-decoration: none; +} + +.menu-title { + margin-top: 10px; + margin-left: 30px; +} + +.menu-title a { + color: var(--xrcf-title); +} + +hr { + margin-top: 40px; + margin-bottom: 40px; + border: 1px solid var(--border); +} + +code { + font-size: 15px; +} + +.page-foot { + margin-top: 100px; + font-size: 11px; + line-height: 140%; + text-align: left; +} + +.page-foot th { + vertical-align: top; +} + +.page-head { + margin-top: 40px; +} + +.page-date { + font-size: 14px; + margin-bottom: 40px; +} + +.center { + margin-left: auto; + margin-right: auto; +} + +.hero { + align-items: center; + margin-top: 70px; + margin-bottom: 70px; + text-align: center; + font-size: 3.7vh; +} + +.subhero { + font-size: 2.7vh; + padding-bottom: 60px; + line-height: 1.2em; + margin-bottom: 70px; + text-align: center; + max-width: 45ch; +} + +.left { + float: left; +} + +.right { + float: right; + padding-right: 10px; +} + +input#menu { + display: none; +} + +.menu-content { + max-height: 0; + overflow: hidden; +} + +.menu-content ul { + margin: 0px; +} + +.menu-content li { + list-style: none; + margin: 20px; + margin-left: 0px; +} + +input:checked ~ .menu-content { + max-height: 100%; + border-left: 1px solid var(--border); + background-color: var(--background); + position: fixed; + right: 0px; + top: 41px; + height: 100%; +} + +.menu-button { + position: fixed; + right: 4px; + top: 4px; + font-size: 24px; +} + +input:checked ~ .right { + float: left; +} + +@media (min-width: 700px) { + body { + font-size: 20px; + } + + li { + font-size: var(--medium); + margin: 0.2em 0; + } + + .content { + margin: auto !important; + max-width: 780px; + padding: 10px; + padding-top: 90px; + padding-bottom: 60px; + margin-top: 40px; + margin-bottom: 80px; + } + + .medium { + font-size: var(--medium); + line-height: 100%; + } + + .menu-title { + padding-top: 8px; + padding-left: 20px; + font-weight: 800; + } + + .menu-content { + margin-top: 10px; + margin-right: 90px !important; + } + + .menu { + height: 50px; + } + + .menu-svg { + position: fixed; + top: 10px; + right: 30px; + } + + .menu a { + font-size: 22px; + } + + .menu label { + display: none; + } + + .menu-content { + max-height: 100%; + margin-right: 20px; + } + + .menu-content li { + display: inline-block; + margin: 0px; + padding-top: 7px; + padding-bottom: 10px; + } + .menu-svg li { + position: absolute; + top: 10px; + } +} + +ul.articles { + list-style-type: none; + padding: unset; +} + +ul.articles li { + display: flex; + margin: 8px; +} + +ul.articles li span { + flex: 0 0 130px; +} + +ul { + margin: 0.7em; +} + +li p { + margin: 0; +} + +code { + font-family: 'Source Code Pro', monospace; + font-variant-ligatures: none; + background-color: var(--code-background) !important; + white-space: nowrap; + padding: 0.15em; + padding-left: 0.3em; + padding-right: 0.3em; + border-radius: 5px; +} + +/* Don't set this for code in general because that would put inline code on a newline. */ +pre code { + background-color: var(--code-background); + font-size: 14px; + border: 1px solid var(--code-border); + display: block; + overflow: auto; + white-space: pre; + word-wrap: normal; + line-height: 140%; + padding-top: 0.8em; + padding-left: 0.8em; + padding-right: 0.8em; + padding-bottom: 0.8em; +} + +table { + line-height: 1em; + margin-left: auto; + margin-right: auto; + border-collapse: collapse; + text-align: left; + margin-bottom: 1.5em; +} + +tr:first-of-type { + /* background: #eae9f4; */ + border-top: 2px solid rgba(0, 0, 0, 0.2); + border-right: none; +} + +tr:last-of-type { + border-bottom: 2px solid rgba(0, 0, 0, 0.2); +} + +tr:first-of-type>th { + text-align: center; +} + +tr, th, td { + padding: 8px; + border: 1px solid var(--border); + text-align: left !important; +} + +table tbody tr td { + border:1px solid var(--border); +} + +.background-info { + padding-top: 20px; + padding-bottom: 20px; + font-weight: 700; +} + +.emphasize { + background: var(--blue-gradient); + -webkit-background-clip: text; + background-clip: text; + -webkit-text-fill-color: transparent; +} + +.footnote-definition { + margin-top: 2em; + margin-left: 1em; + display: inline-block; +} + +.footnote-definition-label { + font-size: 0.7em; +} + +.footnote-definition-label::before { + content: "Footnote "; +} + +.footnote-definition-label::after { + content: ":"; + font-size: 0.7em; +} + +.footnote-definition p { + display: inline; +} diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..7a67af7 --- /dev/null +++ b/templates/index.html @@ -0,0 +1,60 @@ + + + + + + + {% block title %}{{ config.title }}{% endblock title %} + + + + + + + + + + + +
+ {% block content %} + {{ section.content | safe }} + + {% endblock content %} +
+ + diff --git a/templates/page.html b/templates/page.html new file mode 100644 index 0000000..3bcf920 --- /dev/null +++ b/templates/page.html @@ -0,0 +1,8 @@ +{% extends "index.html" %} + +{% block title %}{{ page.title }} | {{ super() }} {% endblock title %} +{% block content %} +

{{page.title}}

+ + {{page.content | safe}} +{% endblock content %} \ No newline at end of file diff --git a/templates/post.html b/templates/post.html new file mode 100644 index 0000000..0edc254 --- /dev/null +++ b/templates/post.html @@ -0,0 +1,8 @@ +{% extends "index.html" %} + +{% block title %}{{ page.title }} | {{ super() }} {% endblock title %} +{% block content %} +

{{page.title}}

+ + {{page.content | safe}} +{% endblock content %} diff --git a/templates/posts.html b/templates/posts.html new file mode 100644 index 0000000..abfbfcc --- /dev/null +++ b/templates/posts.html @@ -0,0 +1,19 @@ +{% extends "index.html" %} + +{% block content %} +
+ {% for page in section.pages %} + {{ page.date }} + +

+ {{ page.title }} +

+
+ {{ page.description | safe }} +
+
+ read more ► +
+ {% endfor %} +
+{% endblock content %} diff --git a/templates/section.html b/templates/section.html new file mode 100644 index 0000000..5792dec --- /dev/null +++ b/templates/section.html @@ -0,0 +1,8 @@ +{% extends "index.html" %} + +{% block title %}{{ super() }} | {{ section.title }} {% endblock title %} + +{% block content %} +

{{ section.title }}

+{{ section.content | safe }} +{% endblock content %} diff --git a/templates/shortcodes/operator_precedence.html b/templates/shortcodes/operator_precedence.html new file mode 100644 index 0000000..ddc8be3 --- /dev/null +++ b/templates/shortcodes/operator_precedence.html @@ -0,0 +1 @@ +{% set code = load_data(path="content/blog/operator_precedence/operator_precedence.rs") %}{{ code | safe }} From 8d205e342cc009fb97266effedf91c3c8189147f Mon Sep 17 00:00:00 2001 From: Rik Huijzer Date: Tue, 4 Feb 2025 17:45:37 +0100 Subject: [PATCH 2/5] Update --- config.toml | 10 +- content/_index.md | 92 +++--- content/blog/basic-arnoldc.md | 272 ------------------ content/blog/iterators.md | 131 --------- content/blog/operator-precedence.md | 22 -- content/blog/operator_precedence/Cargo.toml | 9 - .../operator_precedence.rs | 261 ----------------- content/blog/why-hard.md | 247 ---------------- content/blog/why.md | 6 + content/contributing.md | 3 + templates/index.html | 8 +- 11 files changed, 67 insertions(+), 994 deletions(-) delete mode 100644 content/blog/basic-arnoldc.md delete mode 100644 content/blog/iterators.md delete mode 100644 content/blog/operator-precedence.md delete mode 100644 content/blog/operator_precedence/Cargo.toml delete mode 100644 content/blog/operator_precedence/operator_precedence.rs delete mode 100644 content/blog/why-hard.md create mode 100644 content/blog/why.md diff --git a/config.toml b/config.toml index 19f49a4..39e332a 100644 --- a/config.toml +++ b/config.toml @@ -1,12 +1,12 @@ -title = "xrcf" -description = "eXtensible and Reusable Compiler Framework" +title = "transformrs" +description = "An interface for AI API providers" default_language = "en" -base_url = "https://xrcf.org" +base_url = "https://transformrs.org" -author = "xrcf project authors" +author = "transformrs project authors" generate_sitemap = true generate_robots_txt = false minify_html = false # Probably need to remove _css from .gitignore if enabling. -generate_feeds = false \ No newline at end of file +generate_feeds = false diff --git a/content/_index.md b/content/_index.md index de1ced5..b8c19e2 100644 --- a/content/_index.md +++ b/content/_index.md @@ -4,50 +4,58 @@ title = "Home"
-

- Aim: A modern library for building production-grade compilers +

+ transformrs is an interface to multiple AI APIs providers.

-
- Building compilers doesn't have to be rocket science. - This open-source library aims to be easy to build and easy to understand while, thanks to Rust, remaining performant, reliable, and productive. -
- -
-

Status

-
- -In the long term, the aim for xrcf is to allow building compilers that can compile any programming language to any target architecture. - -In the near term, the aim is to use xrcf to build a fully functional compiler that can compile the ArnoldC language to an executable. -To see the compiler in action, see the [walkthrough](/blog/basic-arnoldc). -ArnoldC is just a test case. -If xrcf can handle it well, then it will be useful for other compiler projects too. - -
-

Lowering to CPU

-
- -In the table below, a checkmark ✅ means that at least one implementation exists which can lower the construct to code that can be executed on the CPU (via LLVM). - -Construct | MLIR | LLVM dialect | LLVM IR ---- | --- | --- | --- -functions | ✅ | ✅ | ✅ -add | ✅ | ✅ | ✅ -print | ✅ | ✅ | ✅ -if else | ✅ | ✅ | ✅ -for loop | | | -while loop | | | -... | | | - -For example, this table means that print can be lowered from MLIR to the LLVM dialect and then to LLVM IR. -This means that to get your own `print` operation to run on the CPU, you only need to convert your own `print` operation to MLIR. -From there, xrcf can be used to run your code on the CPU. - -
-

Lowering to GPU

-
+The examples below are based on the tests in the [repository](https://github.com/rikhuijzer/transformrs/tree/main/tests). +Many tests run repeatedly against the actual APIs to ensure that the library works as expected. + +## Examples + +First, set your API key either in an `.env` file or as an environment variable. +For example, for DeepInfra, set `DEEPINFRA_KEY` in `.env`: + +```env +DEEPINFRA_KEY= +``` + +Then, you can use the API as follows. + +### Chat Completion + +```rust +use transformrs::openai; +use transformrs::Message; +use transformrs::Provider; + +fn main() { + let messages = vec![ + Message { + role: "system".to_string(), + content: "You are a helpful assistant.".to_string(), + }, + Message { + role: "user".to_string(), + content: "This is a test. Please respond with 'hello world'.".to_string(), + }, + ]; + let keys = transformrs::load_keys(".env"); + let key = keys.for_provider(&Provider::DeepInfra).unwrap(); + let model = "meta-llama/Llama-3.3-70B-Instruct"; + // Using the OpenAI-compatible API for chat completions. + let resp = openai::chat_completion(&key, model, &messages) + .await + .unwrap(); + println!("{}", resp.choices[0].message.content); +} +``` + +This will print: + +``` +hello world +``` -High on the priority list. diff --git a/content/blog/basic-arnoldc.md b/content/blog/basic-arnoldc.md deleted file mode 100644 index 19130b1..0000000 --- a/content/blog/basic-arnoldc.md +++ /dev/null @@ -1,272 +0,0 @@ -+++ -title = "A New Compiler for the Arnold Schwarzenegger Language" -date = 2024-12-02 -description = "As an example of how to use xrcf to write a compiler, there is now a basic ArnoldC compiler in the repository." -+++ - -
- -This is a blog post about the eXtensible Reusable Compiler Framework (xrcf). -I've always wanted to write a compiler, but found the task daunting. -I didn't know how to handle all the details such as lexing, parsing, type checking, and error handling. -That's why I'm building xrcf. -This projects handles the details so you can focus on building your own compiler. - -
- - -Since the release of version 0.4, there is now a basic ArnoldC compiler in the repository. -This ArnoldC compiler is a test case for the compiler framework. -If the framework can handle this language well, then it will be useful for other languages too. -The full code for the compiler can be found [here](https://github.com/rikhuijzer/xrcf/tree/v0.5.0/arnoldc). - -In this blog post, I will show how the compiler can be used to generate fast code for the CPU. -To follow along, you can either clone the repository and run: -```sh -$ cargo install --path arnoldc -``` -Or download the `arnoldc` binary from the [release page](https://github.com/rikhuijzer/xrcf/releases/tag/v0.5.0). - -The [ArnoldC language](https://github.com/lhartikk/ArnoldC) is based on one-liners from Arnold Schwarzenegger movies. -This is what a valid "Hello, World!" program looks like: - -```arnoldc -IT'S SHOWTIME -TALK TO THE HAND "Hello, World!\n" -YOU HAVE BEEN TERMINATED -``` - -Here, `IT'S SHOWTIME` means "begin main", `TALK TO THE HAND` means "print", and `YOU HAVE BEEN TERMINATED` means "end main". - -Before we use the compiler, let's see whether the installation was successful: - -```sh -$ arnoldc --help -``` - -This should print: - -```text -A compiler for the ArnoldC language - -Usage: arnoldc [OPTIONS] [INPUT] - -Arguments: - [INPUT] The input file (- is interpreted as stdin) [default: -] - -Options: - --convert-scf-to-cf Convert structured control flow (scf) operations to cf - --convert-cf-to-llvm Convert control flow (cf) operations to LLVM - --convert-experimental-to-mlir Convert experimental operations to MLIR - --convert-func-to-llvm Convert function operations to LLVM - --convert-mlir-to-llvmir Convert MLIR to LLVM IR - --print-ir-before-all Print the IR before each pass - --convert-arnold-to-mlir Convert ArnoldC operations to MLIR - --compile Compile the code - --debug Print debug information - -h, --help Print help - -V, --version Print version -``` - -To compile ArnoldC, let's create a file called `hello.arnoldc` with the hello world program: - -```arnoldc -IT'S SHOWTIME -TALK TO THE HAND "Hello, World!\n" -YOU HAVE BEEN TERMINATED -``` - -Next, let's see what the compiler generates when we run the `--convert-arnold-to-mlir` pass: - -```sh -$ arnoldc --convert-arnold-to-mlir hello.arnoldc -``` - -This prints: - -```mlir -module { - func.func @main() -> i32 { - experimental.printf("Hello, World!\0A") - %0 = arith.constant 0 : i32 - return %0 : i32 - } -} -``` - -What this shows is that the compiler has converted the ArnoldC code to [MLIR](https://mlir.llvm.org/). -It also added a 0 return value to the `main` function. -This ensures that the program will return a 0 status code, which is the convention for programs that didn't crash. - -Although this MLIR code looks nice (or at least more so than ArnoldC), let's get it to run. -To do so, convert the MLIR code to LLVM IR by running all the required passes in order: - -```sh -$ arnoldc \ - --convert-arnold-to-mlir \ - --convert-experimental-to-mlir \ - --convert-scf-to-cf \ - --convert-cf-to-llvm \ - --convert-func-to-llvm \ - --convert-mlir-to-llvmir \ - hello.arnoldc -``` - -Think of each of these passes as a set of transformations. -For example, the `--convert-arnold-to-mlir` pass transforms: -```arnoldc -TALK TO THE HAND "Hello, World!\n" -``` -to -```mlir -experimental.printf("Hello, World!\0A") -``` - -The command with all passes applied prints the following LLVM IR: - -```llvm -; ModuleID = 'LLVMDialectModule' -source_filename = "LLVMDialectModule" - -declare i32 @printf(ptr) -define i32 @main() { - %3 = alloca i8, i16 15, align 1 - store [15 x i8] c"Hello, World!\0A\00", ptr %3, align 1 - %4 = call i32 @printf(ptr %3) - ret i32 0 -} - -!llvm.module.flags = !{!0} - -!0 = !{i32 2, !"Debug Info Version", i32 3} -``` - -Remembering these passes and in the order in which to run them is cumbersome, so it is also possible to use the `compile` flag, which is a wrapper around the above command and produces the same result: - -```sh -$ arnoldc --compile hello.arnoldc -``` - -To run our compiled code, we can use the LLVM interpreter via the `lli` command. -`lli` executes programs written in the LLVM bitcode format. -This executable is part of the LLVM project, so it can usually be installed via the package manager. -For example, on MacOS, `brew install llvm`. - -Enough talk, let's run the code! - -```sh -$ arnoldc --compile hello.arnoldc | lli -Hello, World! -``` - -Or produce a native executable: - -```sh -$ arnoldc --compile hello.arnoldc | llc -filetype=obj -o hello.o -$ clang hello.o -o hello -$ ./hello -Hello, World! -``` - -Although the compiler is still far from complete (see [status](/#status) for details), there is one more thing we can do. -We can print a variable: - -```arnoldc -IT'S SHOWTIME - -HEY CHRISTMAS TREE x -YOU SET US UP @NO PROBLEMO - -TALK TO THE HAND "x: " -TALK TO THE HAND x - -YOU HAVE BEEN TERMINATED -``` -This should print: - -```text -x: 1 -``` -because `HEY CHRISTMAS TREE x` is equivalent to what in Python would be `x =` and `@NO PROBLEMO` in ArnoldC is equivalent to the boolean `True`. - -Let's see what the compiler generates. -To get readable code, we do only the `--convert-arnold-to-mlir` pass: - -```sh -$ arnoldc --convert-arnold-to-mlir print.arnoldc -``` - -This prints: - -```mlir -module { - func.func @main() -> i32 { - %x = arith.constant 1 : i16 - experimental.printf("x: ") - experimental.printf("%d", %x) - %0 = arith.constant 0 : i32 - return %0 : i32 - } -} -``` - -Which returns the expected value: - -```sh -$ arnoldc --compile print.arnoldc | lli -x: 1 -``` - -As a final example, let's see what happens if we write invalid code: - -```arnoldc -IT'S SHOWTIME -TALK "Hello, World!" -YOU HAVE BEEN TERMINATED -``` - -If we now run the compiler, it will fail with a clear error message: - -```sh -$ arnoldc --compile invalid.arnoldc -thread 'main' panicked at arnoldc/src/main.rs:67:60: -called `Result::unwrap()` on an `Err` value: - ---- -0 | ITS SHOWTIME { -1 | TALK "Hello, World!\n" - ^ Unknown operation: TALK ---- -``` - -As is expected when building a compiler, the error message point to the exact location of the error with a description of the problem. - -This concludes the walkthrough, or as Arnold would say: - -```text -YOU HAVE BEEN TERMINATED -``` - -## Next Steps - -To learn how to build your own compiler, see the files inside the [`arnoldc` directory](https://github.com/rikhuijzer/xrcf/tree/v0.5.0/arnoldc). -It is split into three parts: - -1. `src/main.rs` defines the command line interface. -1. `src/arnold.rs` specifies how to parse the ArnoldC code (convert the text to data structures). -1. `src/arnold_to_mlir.rs` contains the `--convert-arnold-to-mlir` pass, which converts the ArnoldC code to MLIR. - -All other passes such as `--convert-func-to-llvm` are implemented in the `xrcf` crate. - -If you want to contribute to the compiler framework, see [contributing](/contributing). - -Although the compiler framework is not yet feature complete, if you want to build your own compiler, here are some modern compiler projects that could serve as inspiration: - -- [jax](https://github.com/jax-ml/jax): A Python library for accelerator-oriented computing -- [tvm](https://tvm.apache.org/): A end to end machine learning compiler framework for CPUs, GPUs, and accelerators. -- [torch-mlir](https://github.com/llvm/torch-mlir): Compiles PyTorch to MLIR. -- [Flang](https://flang.llvm.org/docs/): A LLVM-based Fortran compiler. - -Or you could build a compiler for a different movie star. -Or your favorite tensor processing unit. -It's up to you. diff --git a/content/blog/iterators.md b/content/blog/iterators.md deleted file mode 100644 index c92a063..0000000 --- a/content/blog/iterators.md +++ /dev/null @@ -1,131 +0,0 @@ -+++ -title = "Why are Iterators so Common in Rust?" -date = 2024-12-20 -description = "In some languages, iterators are nice but people tell you to not overuse them. In Rust, iterators are everywhere. Why is that?" -+++ - -Iterators are a pretty cool concept. -For example, in Python, you can write: - -```python -values = [1, 2, 3] - -for i in range(len(values)): - values[i] += 1 - -print(values) -``` - -This prints -``` -[2, 3, 4] -``` - -With iterators, this can be written as: - -```python -values = [1, 2, 3] - -values = list(map(lambda x: x + 1, values)) - -print(values) -``` - -This is of course pretty cool. - -So a few years ago when I was programming in Python, I obviously went overboard with this and used iterators everywhere. -However, a more senior developer told me to not overuse them. -He said that iterators are nice, but often people find it easier to read loops. -And since code is read more than it is written, writers should just accept the inconvenience of loops. - -In Julia, iterators are also pretty common, but should also generally not be overused. -I don't know what the current state is, but last year the main reason in Julia was that iterators were much more difficult for the compiler to optimize. -It could do it, but if you wanted fast compilation times, you should just use loops so that's what I did. - -However, in Rust, iterators are everywhere and I didn't get why. -For example, this code from [`wasmtime`](https://github.com/bytecodealliance/wasmtime) looks like pretty standard Rust: - -```rust -let stdin_exist = options - .files - .iter() - .find(|file| *file == Path::new("-")) - .is_some(); -``` - -But why? -I wouldn't say that this code is particularly easy to read for most people. -I'm not saying that this is bad, but just that it will take most people a bit of time to get used to using iterators like this. - -So after a bit of searching online, I found three performance arguments that people are making. -One argument is that iterators can be optimized better by the compiler. -A hand-wavy argument for this is that since the compiler is responsible for a large part of the code, it knows more about the guarantees and thus can remove more bounds checks. - -Another argument is that iterators are a zero-cost abstraction anyway. -Some blog posts even show that iterators often produce exactly the same LLVM IR as loops. -I guess this depends a bit on the situation, but it does sound promising. - -The third performance argument is that iterators are lazy. -So while you can also decide to make a loop lazy by pre-emptively breaking out of it, iterators are a bit more convenient. - -But today a reason hit me that I think is not often mentioned. -Iterators are a workaround for when Rust frees variables too early. -Let me show an example. - -```rust -fn main() { - let text = String::from("Hello, world!").split(", "); - - for word in text { - println!("{}", word); - } -} -``` - -If you try to compile this code (`rustc tmp.rs`), you will get an error. -It says that `String::from("Hello, world!")` "creates a temporary value which is freed while still in use". - -One way to fix this is to make a separate variable: - -```rust -fn main() { - let text = String::from("Hello, world!"); - let text = text.split(", "); - - for word in text { - println!("{}", word); - } -} -``` - -It took me way too long to understand why this is happening. -The problem here is that `String::from("Hello, world!")` returns a `String` and `split` returns a `&str`. -This means that `split` does not create a new object, but instead it returns a reference to the original object. -This is very efficient, but problematic for us here for another fact about the compiler: -Rust frees objects which are not referenced at the end of the line! - -So what I mean here is that `String::from("Hello, world!")` is dropped before the loop starts. -This is why the compiler gives an error. -`split` gave us a reference to the original object, but the original object is dropped before we use the reference. -If we write a separate variable, the compiler does decide to keep the original object alive until the end of the function. - -So here is where iterators come in. -With iterators, we can just chain everything to keep it all one one line: - -```rust -fn main() { - String::from("Hello, world!").split(", ").for_each(|word| { - println!("{}", word); - }); -} -``` - -which prints - -``` -Hello -world! -``` - -This, I think, is why iterators are everywhere in Rust. -But maybe I'm wrong, let me know if you disagree. diff --git a/content/blog/operator-precedence.md b/content/blog/operator-precedence.md deleted file mode 100644 index dcfb781..0000000 --- a/content/blog/operator-precedence.md +++ /dev/null @@ -1,22 +0,0 @@ -+++ -title = "Better Operator Precedence in Rust" -date = 2025-01-14 -description = "A Rust rewrite of Jamie Bandon's Pratt parser with operator comparisons" -+++ - -Jamie Brandon wrote [an example parser](https://www.scattered-thoughts.net/writing/better-operator-precedence/) that fixes some problems that Pratt parsers have (see also [matklad](https://matklad.github.io/2020/04/13/simple-but-powerful-pratt-parsing) for a very nice writeup on Pratt parsers). -The idea in Jamie's version is to compare operators directly. - -The example by Jamie was written in Zig. -However, I'm trying to embed it inside xrcf, which is written in Rust. -Having to translate both the Zig language and the algorithm at the same time is a bit too confusing for me. -So here is Jamie's algorithm rewritten in Rust: - -```rust -{{ operator_precedence() }} -``` - -These tests pass with Rust 1.84.0. - -A full cargo project is available at -. diff --git a/content/blog/operator_precedence/Cargo.toml b/content/blog/operator_precedence/Cargo.toml deleted file mode 100644 index 3851112..0000000 --- a/content/blog/operator_precedence/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "operator_precedence" -version = "0.1.0" -edition = "2021" - -[[test]] -name = "my_test" -path = "operator_precedence.rs" - diff --git a/content/blog/operator_precedence/operator_precedence.rs b/content/blog/operator_precedence/operator_precedence.rs deleted file mode 100644 index 17b4104..0000000 --- a/content/blog/operator_precedence/operator_precedence.rs +++ /dev/null @@ -1,261 +0,0 @@ -#![allow(dead_code)] - -#[derive(Clone, Debug, PartialEq)] -enum Operator { - Add, - Multiply, - BitwiseOr, -} - -#[derive(Clone, Debug, PartialEq)] -enum Precedence { - LeftBindsTighter, - RightBindsTighter, - Ambiguous, -} - -use Precedence::*; - -fn compare_precedence(left: &Operator, right: &Operator) -> Precedence { - match left { - Operator::Add => match right { - Operator::Add => return LeftBindsTighter, - Operator::Multiply => return RightBindsTighter, - Operator::BitwiseOr => return Ambiguous, - }, - Operator::Multiply => match right { - Operator::Add => return LeftBindsTighter, - Operator::Multiply => return LeftBindsTighter, - Operator::BitwiseOr => return Ambiguous, - }, - Operator::BitwiseOr => match right { - Operator::Add => return Ambiguous, - Operator::Multiply => return Ambiguous, - Operator::BitwiseOr => return RightBindsTighter, - }, - } -} - -#[cfg(test)] -mod test_precedence { - use super::*; - - #[test] - fn test_operator_precedence() { - let ops = vec![Operator::Add, Operator::Multiply, Operator::BitwiseOr]; - for a in ops.clone() { - for b in ops.clone() { - let ab = compare_precedence(&a, &b); - let ba = compare_precedence(&b, &a); - - if ab == Ambiguous { - assert_eq!(ba, Ambiguous); - } - if a != b && ab == LeftBindsTighter { - assert_eq!(ba, RightBindsTighter); - } - if a != b && ab == RightBindsTighter { - assert_eq!(ba, LeftBindsTighter); - } - - for c in ops.clone() { - let bc = compare_precedence(&b, &c); - let ac = compare_precedence(&a, &c); - - // transitive - if ab == LeftBindsTighter && bc == LeftBindsTighter { - assert_eq!(ac, LeftBindsTighter); - } - if ab == RightBindsTighter && bc == RightBindsTighter { - assert_eq!(ac, RightBindsTighter); - } - } - } - } - } -} - -#[derive(Clone, Debug, PartialEq)] -enum Token { - Number, - Add, - Multiply, - BitwiseOr, - OpenParen, - CloseParen, -} - -#[derive(Clone, Debug, PartialEq)] -struct BinaryOp { - op: Operator, - left: Box, - right: Box, -} - -#[derive(Clone, Debug)] -enum Expr { - Number, - BinaryOp(BinaryOp), -} - -impl PartialEq for Expr { - fn eq(&self, other: &Self) -> bool { - match (self, other) { - (Expr::Number, Expr::Number) => true, - (Expr::BinaryOp(a), Expr::BinaryOp(b)) => a == b, - _ => false, - } - } -} - -#[derive(Clone, Debug)] -struct Parser { - tokens: Vec, - position: usize, -} - -impl Parser { - fn new(tokens: &[Token]) -> Self { - Self { - tokens: tokens.to_vec(), - position: 0, - } - } - fn next_token(&mut self) -> Option { - if self.position >= self.tokens.len() { - return None; - } - let token = self.tokens[self.position].clone(); - self.position += 1; - Some(token) - } - fn parse_expr_inner(&mut self) -> Result { - let token = self.next_token().expect("Expected start of expression"); - match token { - Token::Number => { - return Ok(Expr::Number); - } - Token::OpenParen => { - let expr = self.parse_expr_outer(None)?; - let token = self.next_token().expect("Expected close paren"); - if token != Token::CloseParen { - return Err("Expected close paren".to_string()); - } - return Ok(expr); - } - _ => { - return Err(format!("Expected number or open paren, got {:?}", token)); - } - } - } - fn parse_expr_outer(&mut self, prev_op_o: Option) -> Result { - let mut left = self.parse_expr_inner()?; - loop { - let start = self.position; - let token = match self.next_token() { - Some(token) => token, - None => return Ok(left), - }; - let op = match token { - Token::Add => Operator::Add, - Token::Multiply => Operator::Multiply, - Token::BitwiseOr => Operator::BitwiseOr, - _ => { - self.position = start; - return Ok(left); - } - }; - let precedence = if let Some(ref prev_op) = prev_op_o { - compare_precedence(&prev_op, &op) - } else { - RightBindsTighter - }; - match precedence { - LeftBindsTighter => { - self.position = start; - return Ok(left); - } - RightBindsTighter => { - let right = self.parse_expr_outer(Some(op.clone()))?; - let new_left = Expr::BinaryOp(BinaryOp { - op, - left: Box::new(left), - right: Box::new(right), - }); - left = new_left; - } - Ambiguous => return Err("Ambiguous operator precedence".to_string()), - } - } - } - fn parse(tokens: &[Token]) -> Result { - let mut parser = Parser::new(tokens); - let expr = parser.parse_expr_outer(None)?; - if parser.position != parser.tokens.len() { - return Err("Expected end of expression".to_string()); - }; - Ok(expr) - } -} - -#[cfg(test)] -mod test_parser { - use super::Token::*; - use super::*; - - #[test] - fn test_multiply_precedence_over_add() { - assert_eq!( - Parser::parse(&vec![Number, Add, Number, Multiply, Number]), - Parser::parse(&vec![ - Number, Add, OpenParen, Number, Multiply, Number, CloseParen - ]) - ); - } - #[test] - fn test_parens_override_precedence() { - assert_eq!( - Parser::parse(&vec![ - OpenParen, Number, Add, Number, CloseParen, Multiply, Number - ]), - Ok(Expr::BinaryOp(BinaryOp { - op: Operator::Multiply, - left: Box::new(Expr::BinaryOp(BinaryOp { - op: Operator::Add, - left: Box::new(Expr::Number), - right: Box::new(Expr::Number), - })), - right: Box::new(Expr::Number), - })) - ); - } - #[test] - fn test_ambiguous_precedence_against_bitwise_or() { - assert_eq!( - Parser::parse(&vec![Number, Add, Number, BitwiseOr, Number]), - Err("Ambiguous operator precedence".to_string()) - ); - assert_eq!( - Parser::parse(&vec![Number, Multiply, Number, BitwiseOr, Number]), - Err("Ambiguous operator precedence".to_string()) - ); - } - #[test] - fn test_left_associative() { - assert_eq!( - Parser::parse(&vec![Number, Add, Number, Add, Number]), - Parser::parse(&vec![ - OpenParen, Number, Add, Number, CloseParen, Add, Number - ]) - ); - } - #[test] - fn test_right_associative() { - assert_eq!( - Parser::parse(&vec![Number, BitwiseOr, Number, BitwiseOr, Number]), - Parser::parse(&vec![ - Number, BitwiseOr, OpenParen, Number, BitwiseOr, Number, CloseParen - ]) - ); - } -} diff --git a/content/blog/why-hard.md b/content/blog/why-hard.md deleted file mode 100644 index 6080b2d..0000000 --- a/content/blog/why-hard.md +++ /dev/null @@ -1,247 +0,0 @@ -+++ -title = "Why is Building a Compiler so Hard?" -date = 2024-12-21 -description = "Thoughts on why writing a compiler is surprisingly difficult." -+++ - -Last year, I've spent a few months experimenting with and contributing to various compilers. -I had great fun, but felt that the developer experience could be better. -The build systems were often hard-to-use, and the tooling was often complex enough for things like "jump to definition" to not work. -So that's why I started to write a new compiler framework a few months ago. -And to be clear, I'm not saying that the other frameworks are bad or that I am an expert compiler developer. -Sometimes it's just about saying "how hard can it be?" - -Now that I'm a few months into the project, I often find myself being surprised by how hard it actually is. -But it shouldn't be, right? -There are no side-effects like databases, file IO, or network requests. -You just read the files containing the source code, do some processing, and print the compiled code. -Everything happens inside memory. -Also, there are many great open source projects out there that I'm basing my code on. -So it should be easy. - -However, when working on a new feature or bug fix, I often find myself adding a test case and then having to think a few days about the problem before I feel like I have a good solution. -Next, implementing it is often like wading through mud. -I expect that this will become better with time because my brain will get used to it, but currently it's surprisingly hard. - -That's why I want to write down my thoughts now that I still have "fresh eyes". -This could be useful for myself to understand where the difficulties are so that I can improve the framework. -And maybe it will be interesting for others too. -So let's dive in. - -Remember that I said there are no side-effects? - -## Side-Effects Everywhere - -There are plenty of side-effects inside a compiler. -For example, take the following Python code: - -```python -def add_one(x): - y = 1 - return x + y -``` - -Now a good compiler would look at this code and rewrite it to: - -```python -def add_one(x): - return x + 1 -``` - -since this would avoid one addition operation. -But which steps would the compiler take to rewrite this code? -Assuming that we already parsed the code into some data structure, the steps would be something like: - -1. Look at `x + y`. -1. Find the definition of `y`. -1. Notice that `y` is a constant and thus that `y` in `x + y` can be replaced by `1`. -1. Replace `y` with in `x + y` by `1`. -1. Remove `y = 1` if nobody else is using `y`. - -Now there are two places where side-effects occur. -One is in step 2, where we find the definition of `y`. -Only if `y` is a constant, we can substitute the `y` with a constant. -Otherwise, we abort the rewrite. - -The other is in step 4, where we remove `y = 1`. -This can only happen if `y` is a variable that is no longer used. -If `y` is used somewhere else, we cannot remove the assignment. - -## Pointers Everywhere - -Another source of complexity is that we need pointers to navigate the data structure. -To see why, let's look at the data structure. - -When parsing this code, we create a data structure that looks something like this: - -```yaml -├── function: add_one(x) - ├── assignment: y = 1 - └── return: x + y -``` - -So we have some object that contains `add_one` with two children: `y = 1` and `return x + y`. -Next, we rewrite this code to this: - -```yaml -├── function: add_one(x) - └── return: x + 1 -``` - -Now when we are in step 1 of the rewrite listed above, we need to find the definition of `y`. -This means that we need to find the parent of `return x + y` in the data structure since only the parent knows about the sibling `y = 1`[^1]. -Hence, we need a pointer inside the `return x + y` object that points to its parent. -Or we need a pointer inside the `y` object that points to the definition of `y`. -In both cases, this pointer has to be set when creating the data structure and then updated during rewriting. -It's all not impossible, but it does add complexity. - -## Mutability Everywhere - -Related to the side-effects and pointers, we need to accept mutability to make the compiler fast. -Mutability makes the data structure harder to understand, because it's less clear what state the object is in at some point in time. -To explain why we need mutability, consider the data structure again but now for a function with 100 lines of code. -This would mean a data structure with 100 nodes. - -Then the rewrite shown above would make changes to only a few of these nodes. -Thus, if we would make everything immutable, we would need to copy over almost all the nodes. -This would be very inefficient. - -Relatedly, we need mutability in order to be able to set the pointer to the parent. -For example, when parsing the code, at some point we have parsed the function definition for `add_one` and start parsing the assignment `y = 1`: - -```yaml -├── function: add_one(x) - ├── assignment: y = 1 - ^ Imagine the parser is here. -``` - -And now we want to set the parent of the `y = 1` object to be the `add_one` object. -However, we weren't able to construct the `add_one` object yet since we are still parsing the children `y = 1` and `return x + y`. -So we have to create the object for `add_one` first without setting its children. -Then, we pass this unfinished `add_one` object to the parser of the children and set it as the parent. - -Once the parser is done parsing the children: - -```yaml -├── function: add_one(x) - ├── assignment: y = 1 - └── return: x + y - ^ Imagine the parser is here. -``` - -We can finally complete the `add_one` object by setting the children. - -## Imperative Code - -Next to the side-effects, mutability, and pointers, there is also the question of how to describe the rewrite rules. -Let's consider again this rewrite: - -```yaml -├── function: add_one(x) - ├── assignment: y = 1 - └── return: x + y -``` - -to this: - -```yaml -├── function: add_one(x) - └── return: x + 1 -``` - -Now how would we describe this rewrite in code? -Preferably, we would do this in a declarative way. -MLIR uses a pattern language to describe these kinds of rewrites. -For example, the rewrite for `addi(addi(x, c0), c1)` to `addi(x, c0 + c1)` is described as -[follows]( -https://github.com/llvm/llvm-project/blob/6c062afc2e6ed4329e1e14cb011913195a5356fa/mlir/lib/Dialect/Arith/IR/ArithCanonicalization.td#L42-L48): - -```cpp -def AddIAddConstant : - Pat<(Arith_AddIOp:$res - (Arith_AddIOp $x, (ConstantLikeMatcher APIntAttr:$c0), $ovf1), - (ConstantLikeMatcher APIntAttr:$c1), $ovf2), - (Arith_AddIOp $x, (Arith_ConstantOp (AddIntAttrs $res, $c0, $c1)), - (MergeOverflow $ovf1, $ovf2))>; -``` - -So then I guess our `add_one` rewrite would be something like this: - -```cpp -def AddOne : - Pat<(Add $x, (ConstantLikeMatcher APIntAttr:$c1)), - (Add $x, (Arith_ConstantOp (AddIntAttrs $res, $c0, $c1))>; -``` - -Now my usual problem with declarative code is that it works great until it doesn't. -There are always cases that cannot be expressed in declarative code. -And then you are left with handling the most complex cases in imperative code, while you have done all the easy cases in declarative code. -Also the codebase is then a mix of declarative and imperative code. -Maybe that's still better than having to do everything imperative. -I'm not sure yet. -It does appear complex, that's all I'm saying. - -The alternative is to write imperative code. -For example, we could write a rewrite rule that looks like this: - -```python -def rewrite(op: Add): - if op.rhs.definition is not None and if op.rhs.definition.op == Constant: - new_rhs = Arith_ConstantOp(op.rhs.definition.value) - op.rhs.replace(new_rhs) -``` - -This somehow is to me also not that easy to understand. -It's different code than for example: - -```python -def count_vowels(s): - vowels = "aeiou" - count = 0 - for char in s: - if char.lower() in vowels: - count += 1 - return count -``` - -Although `count_vowels` is longer than the rewrite above, I think it's much easier to understand. -I'm not sure what the reason is for this. -Maybe it's because of the pointers such as `op.rhs.definition`, all the non-standard data types such as `Arith_ConstantOp`, or because the operations (like `replace`) mutate things in the background? -Maybe it's just because my brain is not used to it yet. - -## Other Difficult Parts - -Other people have also noticed that certain parts are complex. -For example, A famous [post by ShipReq](http://web.archive.org/web/20210121042722/https://blog.shipreq.com/post/compilers_are_hard) observed that you need mutability for performance, which complicates things, and the (too) many combinations that are possible. -Furthermore, [Basile Starynkevitch on StackOverflow](https://softwareengineering.stackexchange.com/a/273711/324697) argues that the main difficulty is in implementing all the many optimizations which are necessary to make a competitive compiler. -Especially the middle-end optimizations (so after parsing and before going into target-specific optimizations). -Similarly, [m-in, SeatedInAnOffice, chickyban on Reddit](https://www.reddit.com/r/Compilers/comments/1hjkb49/why_is_building_a_compiler_so_hard) say that building a compiler is not necessarily the hard part, but build a production-grade compiler for real users with a messy language spec is. - -## A More Positive Note - -So why is writing a compiler so hard? -Currently, I think it's because of the side-effects, mutability, and pointers. - -To end on a more positive note, I want to mention that I think there are also some enjoyable aspects of writing a compiler. -One is that when you look at the compiler as a whole, it is deterministic and without side-effects. -Put differently, given the same input, the compiler will always produce the same output. -At the same time, the inputs and outputs are all in textual form. -This makes reproducing bugs and writing tests very easy. -This is in contrast to testing graphical user interfaces, where most of the tests cannot be (fully) automated. - -Another enjoyable aspect is that the problems are hard but fair. -If the program crashes, it's probably your fault. -You cannot blame other software, the internet provider, operating system, or the hard disk. - -So that's why I'll keep working on this framework. -It's hard, but fun. -Hopefully the biggest sources of complexity can be moved into the framework, so that other people don't have to deal with them. -Now if you after reading this became less interested in compilers, then that's fine. -If after reading this you became more interested, feel free to check out the [project on GitHub](https://github.com/rikhuijzer/xrcf). -Contributions as well as complaints are welcome! - -Okay now that this is written up, time for me to go back to writing code. - -[^1]: You could also decide to have each object know about its direct siblings, but then you would need to update the pointers when the siblings change. -When printing the data structure, you anyway need to know the children because you start printing at the root and then print recursively. -So that's why, currently, xrcf only has a pointer from the child to the parent and from the parent to the children. diff --git a/content/blog/why.md b/content/blog/why.md new file mode 100644 index 0000000..731cc21 --- /dev/null +++ b/content/blog/why.md @@ -0,0 +1,6 @@ ++++ +title = "Actually, Rust is great for AI" +date = 2025-02-04 +description = "Python is the default choice for running AI locally, but Rust is a great fit too." ++++ + diff --git a/content/contributing.md b/content/contributing.md index 7cfce15..7bd7f94 100644 --- a/content/contributing.md +++ b/content/contributing.md @@ -28,3 +28,6 @@ $ cargo watch -x test ``` to automatically run the tests when you make changes. +This does require the right API keys to be set up. +Depending on what API keys you have, you can either run only the tests that you care about, or make the changes and send the patch in with a request to run the tests in CI. +The CI has access to many API keys, so feel free to ask for the test to be run there. diff --git a/templates/index.html b/templates/index.html index 7a67af7..27e938f 100644 --- a/templates/index.html +++ b/templates/index.html @@ -9,8 +9,6 @@ - -