From 45da288831fab0c706ccfeb97ce660de860bb09c Mon Sep 17 00:00:00 2001 From: Michael Zhang Date: Mon, 17 Feb 2020 19:04:27 -0600 Subject: [PATCH] ouais --- .../helloworld.js | 0 .../helloworld.wasm | Bin .../index.md | 7 +- .../enterprise/2020-02-17-syntax-update.md | 96 ++++++++++++++++++ sass/_content.scss | 6 ++ sass/_graph.scss | 2 + sass/main.scss | 6 +- templates/rss.xml | 15 ++- 8 files changed, 121 insertions(+), 11 deletions(-) rename content/enterprise/{prototype => 2020-02-11-prototype}/helloworld.js (100%) rename content/enterprise/{prototype => 2020-02-11-prototype}/helloworld.wasm (100%) rename content/enterprise/{prototype => 2020-02-11-prototype}/index.md (98%) create mode 100644 content/enterprise/2020-02-17-syntax-update.md diff --git a/content/enterprise/prototype/helloworld.js b/content/enterprise/2020-02-11-prototype/helloworld.js similarity index 100% rename from content/enterprise/prototype/helloworld.js rename to content/enterprise/2020-02-11-prototype/helloworld.js diff --git a/content/enterprise/prototype/helloworld.wasm b/content/enterprise/2020-02-11-prototype/helloworld.wasm similarity index 100% rename from content/enterprise/prototype/helloworld.wasm rename to content/enterprise/2020-02-11-prototype/helloworld.wasm diff --git a/content/enterprise/prototype/index.md b/content/enterprise/2020-02-11-prototype/index.md similarity index 98% rename from content/enterprise/prototype/index.md rename to content/enterprise/2020-02-11-prototype/index.md index e4fed52..f5109dd 100644 --- a/content/enterprise/prototype/index.md +++ b/content/enterprise/2020-02-11-prototype/index.md @@ -4,14 +4,13 @@ date = 2020-02-11 template = "post.html" [taxonomies] -tags = [] - -[extra] -toc = true +tags = ["enterprise", "web", "ui", "rust", "design"] +++ This past weekend, while on my trip to Minneapolis, I completed a very early prototype of "enterprise", a new UI framework I've been kind of envisioning over the past couple of weeks. While the UI framework is mainly targeted at web apps, the hope is that with a bit more effort, native UIs can be produced with almost no changes to existing applications. Before I begin to describe how it works, I'd like to acknowledge [Nathan Ringo][1] for his massively helpful feedback in both the brainstorming and the implementation process. + + ## Goals of the project This project was born out of many frustrations with existing web frameworks. This is kind of the combination of several projects I wanted to tackle; since it's such a long-term thing I'm going to document a bit of what I want to achieve so I can stay on track. The high-level goals of the project are: diff --git a/content/enterprise/2020-02-17-syntax-update.md b/content/enterprise/2020-02-17-syntax-update.md new file mode 100644 index 0000000..baaf16e --- /dev/null +++ b/content/enterprise/2020-02-17-syntax-update.md @@ -0,0 +1,96 @@ ++++ +title = "enterprise: syntax update" +date = 2020-02-17 +template = "post.html" + +[taxonomies] +tags = ["enterprise", "web", "ui", "syntax"] ++++ + +[Enterprise][1]'s frontend DSL just got a syntax! Although the major functionality hasn't really changed, I threw out the ugly verbose AST-construction syntax for a hand-rolled recursive-descent-ish parser. The rehashed "Hello, world" example looks a bit like this: + +``` +component HelloWorld { + model { + name: String = "hello", + } + + view { + + "Hello, " {name} "!" + } +} +``` + +This compiles using `cargo-web` into a working version of the last post's prototype. You'll notice that quoted literals are used to represent text rather than just typing it out directly like in XML. This is because I'm actually borrowing Rust syntax and parsing it a bit differently. If I had bare text, then everything you put would have to follow Rust's lexical rules; additionally, data about spacing would be a lot more complicated (and unstable!) to retrieve. + +I could possibly have thrown the whole thing into a parser-generator, using Rust's `proc-macro::TokenTree` as tokens, but TokenTree actually gives you blocks (eg. `()` `{}` `[]`) for free, so I can parse expressions like `{name}` incredibly easily. + +Syntax isn't the only thing that's changed since the last update: I've also revamped how builds work. + +New Build Method +---------------- + +I'm switching to a build method that's rather unconventional. The original approach looked something like this. + +```dot +digraph "dependency graph" { + graph[bgcolor="transparent", class="default-coloring"]; + rankdir="LR"; + + "Component DSL" -> "AST" [label = "Parsing"]; + "AST" -> "Dependency Graph" [label = "Graph traversal"]; +} +``` + +Problem here is, when we want code to be modular, the graph traversal approach is going to need information about _all_ modules that are imported in order to +be able to produce a flat set of instructions in the final result. If I make a library for a component (say, `enterprise-router`), what should its crate's contents be? + +> **Tangent**: Here's where I'm going to distract myself a little and put this into a more big-picture perspective. Ultimately, the ideal manifestation of an architecture/business-logic separation would be a DSL that completely hides all implementation of its internals. +> +> That's a pretty far-out goal, so I'm building enterprise incrementally. Sadly, large parts of the language will still rely on the language in which this framework is implemented, Rust. This means that the underlying implementation of features such as modules and async will be relying on the Rust language having these features. However, note that in the long term, a separate DSL for business logic will be planned. + +So what's the solution here? Instead of visiting your component node by node when your component is defined, all the framework is going to do is parse your definition and store the AST of your component as-is. I chose here to serialize ASTs as JSON data and dump it into a static string that will be bundled into your crate. + +Then, in your `build.rs` file, you'll call something like `enterprise_compiler::build(App)`, where `App` is the name of the static string containing the JSON data of the description of your app. This will actually perform the analysis process, calculating the graph of update dependencies, as well as generating the code that will go into a Rust module that you can include into your code. + +Your `build.rs` file might look something like this: + +```rs +#[macro_use] +extern crate enterprise_macros; + +component! { + component HelloWorld { + model { + name: String = "hello", + } + + view { + + "Hello, " {name} "!" + } + } +} + +fn main() { + enterprise_compiler::process("helloworld", HelloWorld); +} +``` + +This will create a string called `HelloWorld` for the HelloWorld component, and then analyze and generate the final application code for it into a file called `helloworld.rs` that you can `mod` into your application. The advantage to this approach is that external modules can just rely on Rust's crate system, since we're just fetching strings out of other crates. + +Source code: [here][3]. + +Next Steps +---------- + +As mentioned in my previous post, I'm still working on implementing [TodoMVC][2], a simple Todo application that should flesh out some more of the reactive functionalities of the framework. This should solidify some more of the questions regarding interactions between data model and DOM. + +I'll also try to abstract more of the system away so it's less dependent on stdweb's implementation. This means adding a notion of "backend", where different backends may have different implementations of a particular component. + + + +[1]: @/enterprise/2020-02-11-prototype/index.md +[2]: http://todomvc.com/ +[3]: https://git.iptq.io/michael/enterprise/src/commit/1453885ed2c3a5159431bb41398b9b8bea4d49f5 diff --git a/sass/_content.scss b/sass/_content.scss index 909e18d..857d60b 100644 --- a/sass/_content.scss +++ b/sass/_content.scss @@ -47,6 +47,12 @@ a { } } +blockquote { + color: $small-text-color; + border-left: 4px solid $small-text-color; + padding-left: 12px; +} + .postlisting-row td { padding-bottom: 12px; } diff --git a/sass/_graph.scss b/sass/_graph.scss index 90b6770..0b8b8cc 100644 --- a/sass/_graph.scss +++ b/sass/_graph.scss @@ -1,4 +1,6 @@ .graphviz { + margin: 24px auto; + svg { max-width: 100%; width: 100%; diff --git a/sass/main.scss b/sass/main.scss index e43db19..960ac17 100644 --- a/sass/main.scss +++ b/sass/main.scss @@ -8,7 +8,7 @@ $monofont: "Roboto Mono", "Roboto Mono for Powerline", "Inconsolata", "Consolas" @media (prefers-color-scheme: light) { $background-color: white; $text-color: #15202B; - $small-text-color: lighten($text-color, 10%); + $small-text-color: lighten($text-color, 15%); $link-color: royalblue; @import "content"; @import "graph"; @@ -16,8 +16,8 @@ $monofont: "Roboto Mono", "Roboto Mono for Powerline", "Inconsolata", "Consolas" @media (prefers-color-scheme: dark) { $background-color: #15202B; - $text-color: #EADFD4; - $small-text-color: darken($text-color, 10%); + $text-color: #D4D4D4; + $small-text-color: darken($text-color, 15%); $link-color: lightskyblue; @import "content"; @import "graph"; diff --git a/templates/rss.xml b/templates/rss.xml index b050b37..8e9f409 100644 --- a/templates/rss.xml +++ b/templates/rss.xml @@ -1,18 +1,25 @@ {{ config.title }} - {{ config.base_url }} + {{ config.base_url | safe }} {{ config.description }} zola {{ config.default_language }} - + {{ last_build_date | date(format="%a, %d %b %Y %H:%M:%S %z") }} {% for page in pages %} {{ page.title }} {{ page.date | date(format="%a, %d %b %Y %H:%M:%S %z") }} - {{ page.permalink }} - {% if page.summary %}{{ page.summary }}{% else %}{{ page.content }}{% endif %} + {{ page.permalink | safe }} + + {% if page.summary %} + {{ page.summary | safe }} + Continue reading... + {% else %} + Read {{ page.title }}. + {% endif %} + {% endfor %}