Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add import statements #110

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 2 additions & 8 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,7 @@
resolver = "2"
exclude = ["assets/", "the_book/"]
members = [
"examples/chirp_menu",
"examples/custom_dsl",
"examples/dsl_and_chirp",
"examples/hello_world",
"examples/simple_menu",
"examples/sprite_debug",
"examples/templates",
"examples/*",
"dsl",
"chirp",
"chirp_macros",
Expand Down Expand Up @@ -59,7 +53,7 @@ bevy-inspector-egui = "0.21"
bevy_mod_picking = { version = "0.17.0", default-features = false, features = [
"backend_bevy_ui",
] }
# bevy-ui-navigation = "0.32.0"
bevy-ui-navigation = "0.33.1"
rust-hsluv = "0.1.4"

[workspace.metadata.release]
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,10 @@ if you don't care for explanations.

See the [./CHANGELOG.md](./CHANGELOG.md) file.

### Tooling

Tooling for the `chirp` file format is available at <https://github.com/nicopap/cuicui_chirp_tooling>

### Version matrix

| bevy | latest supporting version |
Expand Down
53 changes: 53 additions & 0 deletions assets/imports.chirp
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
use "imports_defs.chirp" (
deep_trailing1
(call_nested as call_nested2)
)

fn deep_trailing0() {
deep_trailing1!("Second line", "First line") (column bg(beige) rules(1.1*, 2*) margin(20)) {
Trailing0 (text("Third line") bg(blue))
}
}
fn spacer() {
Spacer(height(10px) width(10%) bg(coral))
}
fn button($text) {
Button(named($text) width(95%) height(200px) bg(purple) row) {
ButtonText(text($text))
}
}
fn nested_template($text) {
NestedText(text($text))
}
fn call_nested(text) {
CallNested(rules(250px, 250px) row bg(brown) margin(10)) {
nested_template!(text)(font_size(32) width(50%))
CallNested2nd(bg(white))
}
}
fn call_nested1(zbrs) {
Entity {
call_nested!(zbrs)
call_nested2!(zbrs)
}
}
fn multiple_arguments(name, $text1, $text2) {
Entity(named(name) rules(90%, 125px) bg(darkblue) column margin(10)) {
spacer!()
MultipleText(text($text1))
MultipleText(text($text2))
spacer!()
}
}
Menu(screen_root row bg(darkgrey)) {
Menu1(column rules(47%, 95%)) {
"TestSpacer"(width(30%) height(100px) bg(aquamarine))
spacer!()
deep_trailing0!()
button!("Hello world")
}
Menu2(column rules(47%, 95%)) {
call_nested1!("Call Nested")
multiple_arguments!("Multiple name", "First text line", "Second text line")
}
}
19 changes: 19 additions & 0 deletions assets/imports_defs.chirp
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
fn deep_trailing2(line, color) {
Trailing2Parent {
Trailing2 (text(line) bg(color) width(1*))
}
}
pub fn deep_trailing1(line2, line1) {
deep_trailing2!(line1, red) {
Trailing1 (text(line2) bg(green) width(2*))
}
}
pub fn nested_template($text) {
NestedText(text($text))
}
pub fn call_nested(text) {
CallNested(rules(250px, 250px) row bg(brown) margin(10)) {
nested_template!(text)(font_size(32) width(50%))
CallNested2nd(bg(white))
}
}
112 changes: 81 additions & 31 deletions chirp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,52 +133,50 @@ template definitions (`fn`), and template calls (`template!()`).

#### Import statements

They are currently not implemented, so please proceed to the next section.

<details><summary><b>Draft design</b></summary>

> **Note**
> Imports ARE NOT IMPLEMENTED

In `cuicui_chirp` you are not limited to a single file. You can _import_ other
chirp files.

To do so, use an import statement. Import statements **are the first statements
in the file**; They start with the `use` keyword, are followed by the source
path of the file to import and an optional "`as` imported_name", this is the
name with which the import will be refered in this file.
in the file**; They start with the `use` keyword, followed by the source
path of the file to import, and — between parenthesis — the list of templates to
import.

```ron
use different/file
// ...
// tree assets/
// assets/
// ├── components
// │ ├── widgets.chirp
// │ └── icons.chirp
// └── menus
// ├── main.chirp
// ├── pause.chirp
// └── settings.chirp
use components/widgets.chirp (button slider)

// You can also rename the templates
use components/widgets.chirp (
(button as widget_button)
(slider as widget_slider)
)
// Like with entity names, you can quote the path name.
// This is necessary if your file has spaces or special symbols in the name.
use "component files/Copy of widget (2).chirp" (button slider)
```

You have two ways to use imports:

1. As **whole file imports**. You can import any file and directly use it as
if it was a template without parameters. This is useful if you want to compose
several complex menus you write in different files.
2. As **template collections**. You can import individual templates defined in
a file. Just complete the path with a `.template_name`.

Similarly to rust, you can combine imports, but only templates from the same file,
so the following is valid:
Renaming is supported. To rename a template:

```ron
use different/file.template
use different/file.{template1, template2}
// ...
```
- surround it in parenthesis
- Add a `as target_name`.
- For `use foo.chirp ((source as target))` the template named `source` in the
file `foo.chirp` will be useable as `target` in the current file.

Wild card imports are not supported.
Wild card imports are not supported, rust-style nested imports are not supported.

#### Publicity

However, to be able to import templates, you need to mark them as `pub` in the
source template. Just prefix the `fn` with `pub` and that's it.

</details>

#### Template definitions

chirp files admit a series of `fn` definitions at the very beginning of the
Expand Down Expand Up @@ -391,6 +389,58 @@ their type.

See [`parse_dsl_impl::type_parsers`] for details.

## Formal specification

### Grammar

```ungrammar
TokenTree
= 'ident'
| '(' (TokenTree)* ')'
| '[' (TokenTree)* ']'
| '{' (TokenTree)* '}'
| StringLit

Method = 'ident' ('(' (TokenTree)* ')')?

Statement
= 'code' '(' 'ident' ')'
| 'Entity' StatementTail
| 'ident' '!' '(' (TokenTree (',' TokenTree)*)? ')' (StatementTail)?
| 'ident' StatementTail
| StringLit StatementTail

StatementTail
= '(' (Method)* ')' ('{' (Statement)* '}')?
| '{' (Statement)* '}'

Path = 'ident' | StringLit
ImportItem = '(' 'ident' as 'ident' ')' | 'ident'
Import = 'use' Path ((ImportItem)*)
Fn = ('pub')? 'fn' 'ident' '(' ('ident' (',' 'ident')*)? ')' '{' Statement '}'
ChirpFile = (Import)* (Fn)* Statement
```

* Notice how `StatementTail` is **never empty**. This ensures that syntax errors
such as `My Entity Name()` are detected and reported correctly.
* `'ident'` is any series of character that is not a whitespace or delimiter such
as `[]{}()"'!,`, so this includes surprising stuff such as `+-_` and `234`.
* `StringLit` works similarly to a rust string literal.
* `TokenTree` aims to work like a [rust `TokenTree`], may accept more than what
the rust grammar accepts (within acceptable limits) for performance reason.
The inside of the parenthesis is passed as-is to `ParseDsl::method`.

### Comments

Currently, only `//` line comments are supported.

[rust `TokenTree`]: https://doc.rust-lang.org/reference/macros.html#macro-invocation


## Tooling

Tooling for the `chirp` file format is available at <https://github.com/nicopap/cuicui_chirp_tooling>

## What is the relationship between `cuicui_dsl` and `cuicui_chirp`?

`cuicui_dsl` is a macro (`dsl!`), while `cuicui_chirp` is a scene file format,
Expand All @@ -403,7 +453,7 @@ different features than `cuicui_dsl`. Here is a feature matrix:
|`code` blocks with in-line rust code| ✅ | |
|`code` calling registered functions | | ✅ |
|`fn` templates |rust| ✅ |
|import from other files |rust| |
|import from other files |rust| |
|hot-reloading | | ✅ |
|reflection-based methods | | ✅ |
|special syntax for colors, rules | | ✅ |
Expand Down
64 changes: 34 additions & 30 deletions chirp/src/interpret.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use winnow::BStr;

use crate::parse_dsl::{self, MethodCtx, ParseDsl};
use crate::parser::{self, chirp_file, Arguments, ChirpFile, FnIndex, Input, Name};
use crate::InterpretResult;

type Span = (u32, u32);

Expand Down Expand Up @@ -76,6 +77,36 @@ impl InterpError {
err.downcast_ref().and_then(ReflectError::maybe_offset)
}
}

pub(crate) fn interpret<'a, D: ParseDsl>(
input_u8: &[u8],
builder: &'a mut EntityCommands<'_, '_, 'a>,
load_ctx: Option<&'a mut LoadContext>,
reg: &'a TypeRegistry,
handles: &'a Handles,
) -> InterpretResult<()> {
let input = Input::new(input_u8, ());
let ast = match chirp_file(input) {
parser::ParseResult::Ast(ast) => ast,
parser::ParseResult::TemplateLibrary(template_library) => {
return InterpretResult::TemplateLibrary(template_library);
}
parser::ParseResult::Err(err, span) => {
let error = SpannedError::new::<D>(err, span);
return InterpretResult::Err(Errors::new(vec![error], input_u8, load_ctx.as_deref()));
}
};
let chirp_file = ChirpFile::new(input, ast.as_ref());
let mut interpreter = Interpreter::<D>::new(builder, load_ctx, reg, handles);
chirp_file.interpret(&mut interpreter);
if interpreter.errors.is_empty() {
InterpretResult::Ok(())
} else {
let ctx = interpreter.load_ctx.as_deref();
InterpretResult::Err(Errors::new(interpreter.errors, input_u8, ctx))
}
}

// TODO(feat): print call stack.
#[derive(Debug, Error, Diagnostic)]
#[error("{error}")]
Expand Down Expand Up @@ -202,7 +233,7 @@ impl Debug for LoadCtx<'_, '_> {
.finish()
}
}
pub(crate) struct Interpreter<'w, 's, 'a, 'l, D> {
struct Interpreter<'w, 's, 'a, 'l, D> {
ctx: LoadCtx<'a, 'a>,
cmds: &'a mut Commands<'w, 's>,
parent_chain: SmallVec<[Entity; 2]>,
Expand All @@ -227,33 +258,6 @@ impl<'w, 's, 'a, 'l, D> fmt::Debug for Interpreter<'w, 's, 'a, 'l, D> {
}
}

impl<'w, 's, 'a, 'l> Interpreter<'w, 's, 'a, 'l, ()> {
pub(crate) fn interpret<D: ParseDsl>(
input_u8: &[u8],
builder: &'a mut EntityCommands<'w, 's, 'a>,
load_ctx: Option<&'a mut LoadContext<'l>>,
reg: &'a TypeRegistry,
handles: &'a Handles,
) -> Result<(), Errors> {
let input = Input::new(input_u8, ());
let ast = match chirp_file(input) {
Ok(v) => v,
Err((err, span)) => {
let error = SpannedError::new::<D>(err, span);
return Err(Errors::new(vec![error], input_u8, load_ctx.as_deref()));
}
};
let chirp_file = ChirpFile::new(input, ast.as_ref());
let mut interpreter = Interpreter::<D>::new(builder, load_ctx, reg, handles);
chirp_file.interpret(&mut interpreter);
if interpreter.errors.is_empty() {
Ok(())
} else {
let ctx = interpreter.load_ctx.as_deref();
Err(Errors::new(interpreter.errors, input_u8, ctx))
}
}
}
impl<'w, 's, 'a, 'l, D: ParseDsl> Interpreter<'w, 's, 'a, 'l, D> {
fn new(
builder: &'a mut EntityCommands<'w, 's, 'a>,
Expand Down Expand Up @@ -391,8 +395,8 @@ impl<'w, 's, 'a, 'l, D: ParseDsl> parser::Interpreter<'a, 'a> for Interpreter<'w
*root_entity = entity;
}

fn import(&mut self, (_name, span): Name<'a>, _alias: Option<Name>) {
self.push_error(span, InterpError::Import);
fn import(&mut self, _file: Name<'a>, name: Name<'a>, _alias: Option<Name>) {
self.push_error(name.1, InterpError::Import);
}

fn register_fn(&mut self, (name, _): Name<'a>, index: FnIndex<'a>) {
Expand Down
Loading